Spaces:
Sleeping
Sleeping
File size: 5,815 Bytes
ae9649e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | # 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 <token>
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
```
|