# User Profile with Project Context - API Update ## Summary Updated `/api/v1/auth/me` endpoint to include project context. The `last_active_project_id` is now stored in the `user_preferences` table (not `users` table) since it's a UI preference. ## Database Changes **Migration:** `012_add_user_last_active_project.sql` ```sql -- Adds last_active_project_id to user_preferences table ALTER TABLE user_preferences ADD COLUMN last_active_project_id UUID REFERENCES projects(id) ON DELETE SET NULL; ``` ## Updated API Endpoints ### 1. GET /api/v1/auth/me **Returns user profile with project context:** ```json { "id": "user_123", "email": "pm@example.com", "name": "John Doe", "role": "project_manager", "status": "active", "is_active": true, "client_id": null, "contractor_id": "contractor_456", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-11-19T14:30:00Z", // NEW FIELDS: "primary_project": { "id": "proj_456", "title": "Safaricom Fiber Rollout" }, "assigned_projects": [ { "id": "proj_456", "title": "Safaricom Fiber Rollout" }, { "id": "proj_789", "title": "Nairobi West Maintenance" } ], "last_active_project_id": "proj_456" } ``` **Project Selection Logic:** 1. **last_active_project_id** - User's last selected project (from preferences) 2. **primary_project** - Determined by priority: - Last active project (if set) - First project where user is primary manager - First assigned project (fallback) 3. **assigned_projects** - All projects user is a member of (via `project_team` table) ### 2. PUT /api/v1/auth/me/preferences **Update user preferences (e.g., switch active project):** ```http PUT /api/v1/auth/me/preferences Authorization: Bearer Content-Type: application/json { "last_active_project_id": "proj_789" } ``` **Response:** Returns full UserProfile (same as GET /me) with updated context **Features:** - ✅ Validates user is actually assigned to the project - ✅ Validates project exists and is active - ✅ Prevents unauthorized project access - ✅ Logs all changes in audit log - ✅ Auto-creates user_preferences if missing - ✅ Returns updated profile immediately **Clear preference (no active project):** ```json { "last_active_project_id": null } ``` ## Frontend Implementation ### On Login ```typescript // After successful login, call /auth/me const userProfile = await fetch('/api/v1/auth/me', { headers: { 'Authorization': `Bearer ${token}` } }).then(r => r.json()); // Use primary_project for initial dashboard const projectId = userProfile.primary_project?.id; if (projectId) { // Fetch dashboard data with this project context const metrics = await fetch(`/api/v1/tickets/stats?project_id=${projectId}`); } // Store assigned projects for project switcher dropdown localStorage.setItem('assigned_projects', JSON.stringify(userProfile.assigned_projects)); ``` ### On Project Switch ```typescript // User selects different project from dropdown async function switchProject(newProjectId: string) { // Update preference on backend const updatedProfile = await fetch('/api/v1/auth/me/preferences', { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ last_active_project_id: newProjectId }) }).then(r => r.json()); // Reload dashboard with new project context window.location.href = `/dashboard?project_id=${newProjectId}`; } ``` ### Project Context Across All Pages ```typescript // In your app context/store const { primary_project, assigned_projects } = userProfile; // Use primary_project.id for all API calls const tickets = await fetch( `/api/v1/tickets?project_id=${primary_project.id}` ); const finance = await fetch( `/api/v1/finance/cash-flow?project_id=${primary_project.id}` ); ``` ## Error Handling **403 Forbidden** - User not assigned to requested project ```json { "detail": "You are not assigned to this project" } ``` **404 Not Found** - Project doesn't exist ```json { "detail": "Project not found" } ``` **400 Bad Request** - Invalid UUID format ```json { "detail": "Invalid project ID format" } ``` ## Platform Admin Handling **Platform admins don't have project assignments** - they oversee the entire platform: ```json // Platform admin's /auth/me response: { "id": "admin_123", "email": "admin@platform.com", "role": "platform_admin", // ... other fields ... "primary_project": null, "assigned_projects": [], "last_active_project_id": null } ``` **Attempting to set project preference as platform_admin returns error:** ```http PUT /api/v1/auth/me/preferences { "last_active_project_id": "proj_456" } // Response: 400 Bad Request { "detail": "Platform admins cannot set active project (no project assignments)" } ``` Frontend should: - Hide project switcher for platform_admin role - Don't scope API calls to project_id for platform admins - Show platform-wide dashboard instead of project dashboard ## Notes - **Automatic Creation:** If user_preferences doesn't exist, it's auto-created on first preference update - **Backwards Compatible:** Existing code still works - just ignore new fields if not needed - **Security:** All project access is validated against project_team membership - **Performance:** Indexed queries, single DB roundtrip for profile data - **Audit Trail:** All preference changes logged with old/new values - **Platform Admin:** Returns null/empty project context, cannot set project preference ## Migration Run the migration: ```bash # Apply migration psql -U postgres -d your_database -f migrations/012_add_user_last_active_project.sql # Rollback if needed psql -U postgres -d your_database -f migrations/012_add_user_last_active_project_rollback.sql ```