# MarichWellness Database Schema

> **Database:** SQLite (`database.sqlite`) with MySQL compatibility (`marich_wellness_db`)
> **Total Tables:** 26 (25 active + 1 utility)
> **Last Verified:** 2026-06-14


---

## 🏛️ ADMIN PORTAL TABLES

### 1. `admins` — Admin User Accounts

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Admin ID (e.g. `admin-1`) |
| `email` | TEXT | UNIQUE, NOT NULL | Admin email address |
| `username` | TEXT | UNIQUE | Admin username |
| `password_hash` | TEXT | NOT NULL | Bcrypt-hashed password |
| `otp_email` | TEXT | NOT NULL | Email for OTP delivery |
| `role` | TEXT | DEFAULT `'admin'` | Role: `admin`, `superadmin` |
| `is_active` | INTEGER | DEFAULT `1` | Active status (0=disabled, 1=active) |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Account creation timestamp |

**Seeded Admins:**
- `mngarii@gmail.com` / `marichadmin` — role: `superadmin`
- `magdah785@gmail.com` / `magdahadmin` — role: `admin`

---

### 2. `admin_otps` — Admin OTP Codes

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-increment ID |
| `admin_email` | TEXT | NOT NULL | Admin email requesting OTP |
| `code` | TEXT | NOT NULL | OTP code |
| `expires_at` | DATETIME | NOT NULL | Expiration timestamp |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |

---

## 🌐 MARICH WELLNESS SITE TABLES

### 3. `User` — Customer Accounts

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | User ID |
| `email` | VARCHAR(191) | UNIQUE, NOT NULL | User email |
| `passwordHash` | VARCHAR(191) | NOT NULL | Bcrypt-hashed password |
| `name` | VARCHAR(191) | | Display name |
| `username` | VARCHAR(191) | UNIQUE | Username |
| `role` | VARCHAR(20) | DEFAULT `'USER'` | Role (`USER`, `ADMIN`) |
| `subscriptionPreferences` | VARCHAR(191) | | Newsletter/subscription prefs |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Account creation |
| `is_active` | INTEGER | DEFAULT `1` | Active status |

---

### 4. `user_profiles` — Extended Wellness Profiles

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `user_email` | VARCHAR(255) | PRIMARY KEY | Links to User.email |
| `wellness_goal` | VARCHAR(100) | | Primary wellness goal |
| `activity_level` | VARCHAR(100) | | Activity level description |
| `days_per_week` | VARCHAR(50) | | Workout days per week |
| `focus` | VARCHAR(50) | | Training focus area |
| `goals` | TEXT | | JSON array of goals |
| `age` | INT | | User age |
| `height` | DECIMAL(5,2) | | Height in cm |
| `weight` | DECIMAL(5,2) | | Weight in kg |
| `sex` | VARCHAR(20) | | Sex |
| `fitness_level` | VARCHAR(50) | | Beginner/Intermediate/Advanced |
| `equipment` | TEXT | | JSON array of available equipment |
| `dietary` | TEXT | | JSON array of dietary preferences |
| `calories_target` | VARCHAR(50) | | Daily calorie target |
| `proteins_target` | TEXT | | Protein target |
| `carbs_target` | TEXT | | Carbs target |
| `veggies_target` | TEXT | | Vegetables target |
| `calculated_calories` | INT | | System-calculated calories |
| `calculated_protein` | INT | | System-calculated protein (g) |
| `calculated_carbs` | INT | | System-calculated carbs (g) |
| `calculated_fats` | INT | | System-calculated fats (g) |
| `created_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Profile creation |
| `updated_at` | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last update |

---

### 5. `Category` — Product Categories

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Category ID |
| `name` | VARCHAR(191) | NOT NULL | Display name |
| `slug` | VARCHAR(191) | UNIQUE, NOT NULL | URL-friendly slug |
| `image` | VARCHAR(191) | | Category image URL |
| `description` | VARCHAR(191) | | Short description |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |

**Seeded Categories:**
| ID | Name | Slug |
|----|------|------|
| `cat_supps` | Supplements | `supplements` |
| `cat_equip` | Equipment | `equipment` |
| `cat_calorie` | Calorie Tracker | `calorie-tracker` |
| `cat_workout` | Workout Log | `workout-log` |
| `cat_meal` | Meal Planner | `meal-planner` |

---

### 6. `Product` — Products (Supplements & Equipment)

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Product ID |
| `name` | VARCHAR(191) | NOT NULL | Product name |
| `slug` | VARCHAR(191) | UNIQUE, NOT NULL | URL-friendly slug |
| `description` | TEXT | | Short description (for cards) |
| `price` | DECIMAL(10,2) | NOT NULL | Product price |
| `image` | VARCHAR(191) | | Product image URL |
| `brand` | VARCHAR(191) | | Brand name |
| `stock` | INT | DEFAULT `0` | Stock quantity |
| `badge` | VARCHAR(191) | | Badge label (e.g. "Best Seller", "Most Popular", "New") |
| `isAffiliate` | TINYINT(1) | DEFAULT `0` | Is affiliate product (always 1 for current products) |
| `affiliateLink` | TEXT | | Affiliate purchase link |
| `benefits` | TEXT | | Product benefits |
| `categoryId` | VARCHAR(191) | FK → Category(id) | Category reference |
| `productDescription` | TEXT | | Detailed SEO/product description (HTML) |
| `publishStatus` | VARCHAR(20) | DEFAULT `'Draft'` | `Draft`, `Published`, or `Disabled` |
| `commission` | DECIMAL(5,2) | DEFAULT `10.00` | Affiliate commission % |
| `revenue` | DECIMAL(10,2) | DEFAULT `0.00` | Tracked revenue |
| `cookieLife` | INT | DEFAULT `30` | Affiliate cookie lifespan (days) |
| `faqs` | TEXT | DEFAULT NULL | JSON array of FAQs `[{question, answer}]` |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
| `updatedAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Last update |

**Note:** The `partnerLink` field used in the admin UI is mapped to `affiliateLink` in the database. The `keywords` field is not stored directly in the Product table — it's used client-side for SEO description generation.

**Seeded Products:**
| ID | Name | Price | Category |
|----|------|-------|----------|
| `prod_supp_1` | Bio-Available Magnesium | $34.00 | Supplements |
| `prod_supp_2` | Organic Vegan Protein | $49.00 | Supplements |
| `prod_equip_1` | Sonic Massage Gun | $129.00 | Equipment |
| `prod_equip_2` | Pro Fitness Tracker | $199.00 | Equipment |

---

### 7. `ProductArticle` — Product Article Content (Rich Text)

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-increment ID |
| `productId` | VARCHAR(191) | NOT NULL, UNIQUE, FK → Product(id) ON DELETE CASCADE | Product reference |
| `productDescription` | TEXT | DEFAULT `''` | Article HTML content |
| `richTextState` | TEXT | DEFAULT NULL | JSON serialized TipTap editor state |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
| `updatedAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Last update |

**Note:** This table stores the rich-text article content separately from the Product table. The `productDescription` field in Product is synced when an article is saved.

---

### 8. `Order` — Customer Orders

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Order ID |
| `userId` | VARCHAR(191) | FK → User(id) | Customer reference |
| `totalAmount` | DECIMAL(10,2) | NOT NULL | Order total |
| `status` | VARCHAR(20) | DEFAULT `'PENDING'` | Order status |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Order creation |
| `updatedAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Last update |

---

### 9. `OrderItem` — Order Line Items

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Line item ID |
| `orderId` | VARCHAR(191) | FK → Order(id), NOT NULL | Order reference |
| `productId` | VARCHAR(191) | FK → Product(id), NOT NULL | Product reference |
| `quantity` | INT | NOT NULL | Quantity ordered |
| `price` | DECIMAL(10,2) | NOT NULL | Unit price at time of order |

---

### 10. `SavedItem` — User Saved/Favorited Products

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Saved item ID |
| `userId` | VARCHAR(191) | FK → User(id), NOT NULL | User reference |
| `productId` | VARCHAR(191) | FK → Product(id), NOT NULL | Product reference |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | When saved |
| | | UNIQUE(userId, productId) | Prevents duplicates |

---

### 11. `BlogArticle` — Blog/Journal Articles

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Article ID |
| `title` | VARCHAR(191) | NOT NULL | Article title |
| `slug` | VARCHAR(191) | UNIQUE, NOT NULL | URL-friendly slug |
| `content` | TEXT | NOT NULL | Article body content |
| `image` | VARCHAR(191) | | Featured image URL |
| `published` | TINYINT(1) | DEFAULT `0` | Published status (0=draft, 1=published) |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |

---

### 12. `ContactMessage` — Contact Form Submissions

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Message ID |
| `name` | TEXT | NOT NULL | Sender name |
| `email` | TEXT | NOT NULL | Sender email |
| `message` | TEXT | NOT NULL | Message body |
| `status` | TEXT | DEFAULT `'new'` | Status: `new`, `read`, `replied` |
| `reply_text` | TEXT | | Admin reply content |
| `replied_at` | DATETIME | | When admin replied |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Submission timestamp |

---

### 13. `SiteConfig` — Site Configuration

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Config entry ID |
| `config_key` | VARCHAR(191) | UNIQUE, NOT NULL | Configuration key |
| `config_value` | TEXT | NOT NULL | Configuration value |
| `updatedAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Last update |

---

### 14. `UserAction` — User Activity Tracking / Analytics

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Action ID |
| `userId` | VARCHAR(191) | | Authenticated user (nullable for visitors) |
| `visitorId` | VARCHAR(191) | | Anonymous visitor identifier |
| `actionType` | VARCHAR(50) | | Type of action (e.g. `page_view`, `purchase`) |
| `entityType` | VARCHAR(50) | | Entity type (e.g. `product`, `article`) |
| `entityId` | VARCHAR(191) | | Entity identifier |
| `metadata` | TEXT | | JSON metadata |
| `createdAt` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Action timestamp |

---

## 🧪 UTILITY TABLES

### 15. `test_upsert` — Test/Utility Table

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Entry ID |
| `val` | TEXT | | Value |

---

## 📋 TABLES DEFINED IN CODE BUT NOT IN DATABASE

### `exercises` — Exercise Library (defined in `setup_db.js` but table was never created)

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | VARCHAR(191) | PRIMARY KEY | Exercise ID |
| `name` | VARCHAR(191) | NOT NULL | Exercise name |
| `slug` | VARCHAR(191) | UNIQUE, NOT NULL | URL-friendly slug |
| `description` | TEXT | | Exercise description |
| `muscle_group` | VARCHAR(100) | | Target muscle group |
| `equipment` | VARCHAR(100) | | Required equipment |
| `difficulty` | VARCHAR(20) | | Difficulty level |
| `instructions` | TEXT | | Step-by-step instructions |
| `video_url` | VARCHAR(500) | | Video demonstration URL |
| `image_url` | VARCHAR(500) | | Image URL |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | Creation timestamp |

**Note:** This table was defined in `setup_db.js` with 20 seeded exercises but was never actually created in the database due to conditional logic that checked for its existence. The table does not exist in the current SQLite database.

---

## 🔗 RELATIONSHIP DIAGRAM

```
admins ─── admin_otps          (Admin OTP verification)
  │
User ─── user_profiles         (Extended profile data)
  │
  ├── Order ─── OrderItem ─── Product
  │                              │
  ├── SavedItem ──────────────── Product
  │                              │
  └── UserAction                 │
                                 │
  ProductArticle ─────────────── Product    (Rich text article storage)
                                 │
                           Category ─── Product
```

## 🗄️ DATABASE CONFIGURATION

- **Primary:** SQLite (`database.sqlite` in project root)
- **Fallback/Production:** MySQL (`marich_wellness_db` on `127.0.0.1:3306`)
- **Connection:** Managed via `src/lib/db.ts` (abstracts SQLite/MySQL differences)
- **Image Uploads:** Processed via Sharp (resized to 800×800 WebP at quality 75), stored in `public/uploads/products/`

---

## 🏋️ WORKOUT PLANNER TABLES (v1.0 — 2026-06-14)

### 17. `exercise_library` — Exercise Database

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Exercise ID (e.g. `ex_bench_press`) |
| `name` | TEXT | NOT NULL | Exercise name |
| `muscle_group` | TEXT | NOT NULL | Primary muscle group |
| `secondary_muscles` | TEXT | DEFAULT `''` | Secondary muscles worked |
| `equipment` | TEXT | NOT NULL | Equipment needed |
| `difficulty` | TEXT | DEFAULT `'beginner'` | `beginner`, `intermediate`, `advanced` |
| `instructions` | TEXT | | Step-by-step instructions |
| `pro_tips` | TEXT | | Pro tips for form |
| `common_mistakes` | TEXT | | Common mistakes to avoid |
| `video_url` | TEXT | | Video demonstration URL |
| `image_url` | TEXT | | Image URL |
| `is_cardio` | INTEGER | DEFAULT `0` | Cardio exercise flag |
| `is_bodyweight` | INTEGER | DEFAULT `0` | Bodyweight exercise flag |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | |

**Indexes:** `muscle_group`, `equipment`, `difficulty`

### 18. `workout_templates` — Pre-Built Programs

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Template ID |
| `name` | TEXT | NOT NULL | Program name |
| `description` | TEXT | | Short description |
| `long_description` | TEXT | | Detailed description |
| `level` | TEXT | NOT NULL, CHECK | `beginner`, `intermediate`, `advanced` |
| `goal` | TEXT | NOT NULL | Fitness goal |
| `days_per_week` | INTEGER | NOT NULL | Workout days per week |
| `total_weeks` | INTEGER | NOT NULL | Program duration in weeks |
| `estimated_calories_per_week` | INTEGER | DEFAULT `0` | Estimated calorie burn |
| `equipment_needed` | TEXT | | Required equipment |
| `who_is_for` | TEXT | | Target audience |
| `is_published` | INTEGER | DEFAULT `1` | Published status |
| `sort_order` | INTEGER | DEFAULT `0` | Display order |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | |

**Indexes:** `level`, `goal`

### 19. `workout_template_weeks` — Template Weeks

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Week ID |
| `template_id` | TEXT | FK → `workout_templates(id)` ON DELETE CASCADE | Parent template |
| `week_number` | INTEGER | NOT NULL | Week number |
| `focus` | TEXT | | Week focus/theme |
| | | UNIQUE(template_id, week_number) | |

### 20. `workout_template_days` — Template Days

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Day ID |
| `week_id` | TEXT | FK → `workout_template_weeks(id)` ON DELETE CASCADE | Parent week |
| `day_number` | INTEGER | NOT NULL | Day number (1-7) |
| `day_name` | TEXT | NOT NULL | Day name (e.g. "Push A") |
| `focus_area` | TEXT | | Muscle focus |
| `is_rest_day` | INTEGER | DEFAULT `0` | Rest day flag |
| | | UNIQUE(week_id, day_number) | |

### 21. `workout_template_exercises` — Template Day Exercises

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Exercise assignment ID |
| `day_id` | TEXT | FK → `workout_template_days(id)` ON DELETE CASCADE | Parent day |
| `exercise_id` | TEXT | FK → `exercise_library(id)` | Exercise reference |
| `sets` | INTEGER | DEFAULT `3` | Number of sets |
| `reps` | TEXT | DEFAULT `'10-12'` | Rep range (e.g. "10-12", "30s") |
| `rest_seconds` | INTEGER | DEFAULT `60` | Rest between sets |
| `order_index` | INTEGER | NOT NULL | Exercise order |
| `notes` | TEXT | | Additional notes |
| | | UNIQUE(day_id, order_index) | |

### 22. `user_saved_programs` — User Programs

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Program ID |
| `user_id` | TEXT | NOT NULL | User ID |
| `template_id` | TEXT | FK → `workout_templates(id)` ON DELETE SET NULL | Source template |
| `name` | TEXT | NOT NULL | Program name |
| `description` | TEXT | | Program description |
| `is_active` | INTEGER | DEFAULT `0` | Active program flag |
| `is_custom` | INTEGER | DEFAULT `0` | Custom program flag |
| `source` | TEXT | CHECK | `template`, `ai_generated`, `custom` |
| `current_week` | INTEGER | DEFAULT `1` | Current week progress |
| `current_day` | INTEGER | DEFAULT `1` | Current day progress |
| `started_at` | DATETIME | | Program start date |
| `completed_at` | DATETIME | | Program completion date |
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | |
| `updated_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | |

**Indexes:** `user_id`, `(user_id, is_active)`

### 23. `user_workout_days` — Custom User Days

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Day ID |
| `user_program_id` | TEXT | FK → `user_saved_programs(id)` ON DELETE CASCADE | Parent program |
| `week_number` | INTEGER | NOT NULL | Week number |
| `day_number` | INTEGER | NOT NULL | Day number |
| `day_name` | TEXT | | Custom day name |
| `focus_area` | TEXT | | Focus area |
| `is_rest_day` | INTEGER | DEFAULT `0` | Rest day flag |
| `is_custom` | INTEGER | DEFAULT `0` | Custom override flag |
| | | UNIQUE(user_program_id, week_number, day_number) | |

### 24. `user_workout_exercises` — Custom User Exercises

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Exercise ID |
| `user_day_id` | TEXT | FK → `user_workout_days(id)` ON DELETE CASCADE | Parent day |
| `exercise_id` | TEXT | FK → `exercise_library(id)` | Exercise reference |
| `sets` | INTEGER | DEFAULT `3` | Number of sets |
| `reps` | TEXT | DEFAULT `'10-12'` | Rep range |
| `rest_seconds` | INTEGER | DEFAULT `60` | Rest between sets |
| `order_index` | INTEGER | NOT NULL | Exercise order |
| `notes` | TEXT | | Notes |
| | | UNIQUE(user_day_id, order_index) | |

### 25. `workout_sessions` — Active Workout Tracking

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Session ID |
| `user_id` | TEXT | NOT NULL | User ID |
| `user_program_id` | TEXT | FK → `user_saved_programs(id)` ON DELETE SET NULL | Program reference |
| `week_number` | INTEGER | DEFAULT `1` | Current week |
| `day_number` | INTEGER | DEFAULT `1` | Current day |
| `started_at` | DATETIME | NOT NULL | Session start |
| `ended_at` | DATETIME | | Session end |
| `duration_seconds` | INTEGER | DEFAULT `0` | Duration |
| `exercises_completed` | INTEGER | DEFAULT `0` | Exercises done |
| `sets_completed` | INTEGER | DEFAULT `0` | Sets done |
| `total_exercises` | INTEGER | DEFAULT `0` | Total planned exercises |
| `total_sets` | INTEGER | DEFAULT `0` | Total planned sets |
| `status` | TEXT | CHECK | `in_progress`, `completed`, `abandoned` |
| `notes` | TEXT | | Session notes |

**Indexes:** `user_id`, `status`

### 26. `user_workout_progress` — Daily/Weekly Progress

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | TEXT | PRIMARY KEY | Progress ID |
| `user_id` | TEXT | NOT NULL | User ID |
| `user_program_id` | TEXT | FK → `user_saved_programs(id)` ON DELETE CASCADE | Program reference |
| `week_number` | INTEGER | NOT NULL | Week number |
| `day_number` | INTEGER | NOT NULL | Day number |
| `date` | DATE | NOT NULL | Workout date |
| `completed` | INTEGER | DEFAULT `0` | Completion flag |
| `exercises_done` | INTEGER | DEFAULT `0` | Exercises completed |
| `sets_done` | INTEGER | DEFAULT `0` | Sets completed |
| `calories_burned` | INTEGER | DEFAULT `0` | Calories burned |
| `duration_minutes` | INTEGER | DEFAULT `0` | Duration in minutes |
| `notes` | TEXT | | Progress notes |
| | | UNIQUE(user_id, user_program_id, week_number, day_number, date) | |

**Indexes:** `user_id`, `date`

### Seeded Data

- **Exercise Library:** 42 exercises across 9 muscle groups
- **Pre-Built Programs:** 9 programs (3 beginner, 3 intermediate, 3 advanced)
  - Beginner: Full Body Starter, Beginner Fat Burn, Beginner Muscle Builder
  - Intermediate: Push Pull Legs, Upper Lower Split, Endurance & Conditioning
  - Advanced: 5/3/1 Powerbuilding, Advanced Bodybuilding, Athletic Performance
- **Total Template Weeks:** 9 (1 week each, programs designed for progressive overload)
- **Total Template Days:** 63
- **Total Template Exercises:** 190

### Helper Library

`src/lib/workout-planner/db.ts` — Provides typed query functions:
- Exercise library queries (by muscle group, equipment, difficulty, search)
- Template queries (by level, goal, full template with nested structure)
- User program management (save, activate, progress tracking)
- Workout session tracking (start, complete, history)
- Progress logging and dashboard aggregation
- Dashboard endpoint: `getUserDashboard(userId)` returns active program, recent sessions, weekly stats, total stats


