|
| 1 | +# Project Category Migration Guide |
| 2 | + |
| 3 | +## Overview |
| 4 | +Successfully migrated the project-category relationship from **one-to-one** to **many-to-many** (one project can have multiple categories). |
| 5 | + |
| 6 | +## Changes Summary |
| 7 | + |
| 8 | +### 1. Database Schema Changes |
| 9 | + |
| 10 | +#### New Junction Table Created |
| 11 | +- **Table**: `project_categories` |
| 12 | +- **Purpose**: Links projects to multiple categories |
| 13 | +- **Columns**: |
| 14 | + - `id` (UUID, primary key) |
| 15 | + - `project_id` (UUID, foreign key to `projects.id`) |
| 16 | + - `category_id` (INTEGER, foreign key to `projects_category.id`) |
| 17 | + - `created_at` (timestamp) |
| 18 | +- **Constraints**: |
| 19 | + - Unique constraint on `(project_id, category_id)` to prevent duplicates |
| 20 | + - Foreign keys with CASCADE delete |
| 21 | + |
| 22 | +#### Removed Column |
| 23 | +- Removed `project_category_id` from `projects` table (old one-to-one relationship) |
| 24 | + |
| 25 | +#### Migration File Location |
| 26 | +[migration-scripts/project-category-many-to-many.sql](migration-scripts/project-category-many-to-many.sql) |
| 27 | + |
| 28 | +**The migration includes**: |
| 29 | +- Automatic data migration from old `project_category_id` to new junction table |
| 30 | +- Indexes for performance optimization |
| 31 | +- A helpful view `projects_with_categories` for easy querying |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +### 2. TypeScript Type Updates |
| 36 | + |
| 37 | +#### New Types Added |
| 38 | +Location: [apps/codebility/types/home/codev.ts](apps/codebility/types/home/codev.ts) |
| 39 | + |
| 40 | +```typescript |
| 41 | +export interface ProjectCategory { |
| 42 | + id: number; |
| 43 | + name: string; |
| 44 | + description?: string; |
| 45 | + created_at?: string; |
| 46 | + updated_at?: string; |
| 47 | +} |
| 48 | + |
| 49 | +export interface ProjectCategoryJunction { |
| 50 | + id: string; |
| 51 | + project_id: string; |
| 52 | + category_id: number; |
| 53 | + created_at?: string; |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +#### Updated Project Interface |
| 58 | +- **Changed**: `project_category_id?: number` |
| 59 | +- **To**: `categories?: ProjectCategory[]` |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +### 3. Service Layer Updates |
| 64 | + |
| 65 | +#### Updated Files: |
| 66 | +- [apps/codebility/lib/server/project.service.ts](apps/codebility/lib/server/project.service.ts) |
| 67 | +- [apps/codebility/app/home/projects/actions.ts](apps/codebility/app/home/projects/actions.ts) |
| 68 | + |
| 69 | +**Changes:** |
| 70 | +- All queries now fetch categories through the junction table |
| 71 | +- Categories are automatically flattened for easier consumption |
| 72 | +- Query structure: |
| 73 | +```typescript |
| 74 | +categories:project_categories( |
| 75 | + projects_category( |
| 76 | + id, |
| 77 | + name, |
| 78 | + description |
| 79 | + ) |
| 80 | +) |
| 81 | +``` |
| 82 | + |
| 83 | +--- |
| 84 | + |
| 85 | +### 4. CRUD Operations Updated |
| 86 | + |
| 87 | +#### Create Project |
| 88 | +- Now accepts `category_ids` (JSON array) instead of single `project_category_id` |
| 89 | +- Automatically inserts into junction table |
| 90 | + |
| 91 | +#### Update Project |
| 92 | +- Accepts `category_ids` (JSON array) |
| 93 | +- Replaces all existing categories for the project |
| 94 | + |
| 95 | +#### Get Project |
| 96 | +- Automatically includes all categories in the response |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +### 5. New Helper Functions |
| 101 | + |
| 102 | +Location: [apps/codebility/app/home/projects/actions.ts](apps/codebility/app/home/projects/actions.ts) |
| 103 | + |
| 104 | +```typescript |
| 105 | +// Get all categories for a specific project |
| 106 | +getProjectCategoriesForProject(projectId: string) |
| 107 | + |
| 108 | +// Add categories to a project |
| 109 | +addCategoriesToProject(projectId: string, categoryIds: number[]) |
| 110 | + |
| 111 | +// Remove a single category from a project |
| 112 | +removeCategoryFromProject(projectId: string, categoryId: number) |
| 113 | + |
| 114 | +// Replace all categories for a project |
| 115 | +replaceProjectCategories(projectId: string, categoryIds: number[]) |
| 116 | +``` |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +### 6. Admin Dashboard Updates |
| 121 | + |
| 122 | +Location: [apps/codebility/app/home/admin-dashboard/page.tsx](apps/codebility/app/home/admin-dashboard/page.tsx) |
| 123 | + |
| 124 | +- Updated to query the junction table |
| 125 | +- Counts unique projects per category |
| 126 | +- Handles many-to-many relationship correctly |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## How to Apply the Migration |
| 131 | + |
| 132 | +### Step 1: Run the Migration SQL |
| 133 | +Execute the migration file in your Supabase SQL editor: |
| 134 | + |
| 135 | +```bash |
| 136 | +# File: migration-scripts/project-category-many-to-many.sql |
| 137 | +``` |
| 138 | + |
| 139 | +**This will**: |
| 140 | +1. Create `projects_category` table if it doesn't exist |
| 141 | +2. Create the `project_categories` junction table |
| 142 | +3. Migrate existing data automatically |
| 143 | +4. Remove the old `project_category_id` column |
| 144 | +5. Create helpful indexes and views |
| 145 | + |
| 146 | +### Step 2: Test the Changes |
| 147 | +No code changes needed - the application code has been updated to work with the new schema. |
| 148 | + |
| 149 | +### Step 3: Update Frontend Components (If Needed) |
| 150 | +You'll need to update any forms that create/edit projects to send `category_ids` as a JSON array: |
| 151 | + |
| 152 | +#### Example: Creating a Project |
| 153 | +```javascript |
| 154 | +const formData = new FormData(); |
| 155 | +formData.append("name", "My Project"); |
| 156 | +formData.append("category_ids", JSON.stringify([1, 2, 3])); // Multiple categories |
| 157 | +``` |
| 158 | + |
| 159 | +#### Example: Displaying Categories |
| 160 | +```typescript |
| 161 | +// Old way (single category) |
| 162 | +{project.project_category_id} |
| 163 | + |
| 164 | +// New way (multiple categories) |
| 165 | +{project.categories?.map(cat => cat.name).join(", ")} |
| 166 | +``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Frontend Components That May Need Updates |
| 171 | + |
| 172 | +These components reference `projects_category` or `project_category_id` and may need UI updates: |
| 173 | + |
| 174 | +1. [apps/codebility/app/home/projects/_components/ProjectAddModal.tsx](apps/codebility/app/home/projects/_components/ProjectAddModal.tsx) |
| 175 | +2. [apps/codebility/app/home/projects/_components/ProjectEditModal.tsx](apps/codebility/app/home/projects/_components/ProjectEditModal.tsx) |
| 176 | +3. [apps/codebility/app/home/projects/_components/ProjectViewModal.tsx](apps/codebility/app/home/projects/_components/ProjectViewModal.tsx) |
| 177 | +4. [apps/codebility/app/home/projects/_components/ProjectCardContainer.tsx](apps/codebility/app/home/projects/_components/ProjectCardContainer.tsx) |
| 178 | + |
| 179 | +**Recommended UI Update:** |
| 180 | +- Change single select dropdowns to multi-select or checkbox groups for categories |
| 181 | +- Display multiple category badges/chips instead of a single category name |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## Benefits of This Change |
| 186 | + |
| 187 | +1. **Flexibility**: Projects can now belong to multiple categories |
| 188 | +2. **Better Organization**: More accurate categorization of projects |
| 189 | +3. **Analytics**: Better insights into project distribution across categories |
| 190 | +4. **Scalability**: Easier to add new categories without restructuring |
| 191 | + |
| 192 | +--- |
| 193 | + |
| 194 | +## Database View Available |
| 195 | + |
| 196 | +A convenient view has been created for querying projects with their categories: |
| 197 | + |
| 198 | +```sql |
| 199 | +SELECT * FROM projects_with_categories; |
| 200 | +``` |
| 201 | + |
| 202 | +This view returns projects with all their categories as a JSON array. |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +## Rollback Plan (If Needed) |
| 207 | + |
| 208 | +If you need to rollback: |
| 209 | + |
| 210 | +```sql |
| 211 | +-- 1. Add back the old column |
| 212 | +ALTER TABLE projects ADD COLUMN project_category_id INTEGER; |
| 213 | + |
| 214 | +-- 2. Populate it with the first category from junction table (if any) |
| 215 | +UPDATE projects p |
| 216 | +SET project_category_id = ( |
| 217 | + SELECT category_id |
| 218 | + FROM project_categories |
| 219 | + WHERE project_id = p.id |
| 220 | + LIMIT 1 |
| 221 | +); |
| 222 | + |
| 223 | +-- 3. Drop the junction table |
| 224 | +DROP TABLE project_categories; |
| 225 | + |
| 226 | +-- 4. Revert code changes via git |
| 227 | +git revert <commit-hash> |
| 228 | +``` |
| 229 | + |
| 230 | +--- |
| 231 | + |
| 232 | +## Questions or Issues? |
| 233 | + |
| 234 | +If you encounter any issues: |
| 235 | +1. Check that the migration SQL ran successfully |
| 236 | +2. Verify the junction table exists: `SELECT * FROM project_categories LIMIT 5;` |
| 237 | +3. Check browser console for any API errors |
| 238 | +4. Review the helper functions in [actions.ts](apps/codebility/app/home/projects/actions.ts) for examples |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +## Next Steps |
| 243 | + |
| 244 | +1. **Run the migration** in your Supabase database |
| 245 | +2. **Test the application** to ensure project queries work correctly |
| 246 | +3. **Update frontend forms** to use multi-select for categories |
| 247 | +4. **Update display components** to show multiple categories |
| 248 | +5. **Test create/edit operations** with multiple categories |
| 249 | + |
| 250 | +Good luck! 🚀 |
0 commit comments