kamau1 commited on
Commit
078be83
·
1 Parent(s): 4835b24

feat: add meta_apps config and include in login and preferences endpoints for project role navigation context

Browse files
docs/agent/thoughts/frontend.md ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Frontend Team - Navigation & App Context Requirements
2
+
3
+ **Date:** 2025-11-21
4
+ **Topic:** Project Selection & Context-Aware App Navigation
5
+ **Status:** Pending Backend Implementation
6
+
7
+ ---
8
+
9
+ ## Problem Statement
10
+
11
+ We've implemented a project selection system for project-based roles (Project Manager, Sales Manager, Dispatcher, Field Agent). These users now have two distinct contexts:
12
+
13
+ 1. **Outside Project Context**: `/dashboard` - Project selection view
14
+ 2. **Inside Project Context**: `/project/:projectId` - Project-specific dashboard
15
+
16
+ Currently, the navigation system shows all available apps regardless of context. This creates a poor UX because:
17
+ - Users see project-specific apps (Tickets, Inventory, Map, etc.) when they're on the project selection screen
18
+ - These apps are not functional without a project context
19
+ - It's confusing and cluttered
20
+
21
+ ## Proposed Solution
22
+
23
+ We need the **backend to provide context-aware app lists** based on user role and current context.
24
+
25
+ ---
26
+
27
+ ## Backend API Requirements
28
+
29
+ ### Current API Response Structure
30
+ ```json
31
+ GET /api/v1/users/me/preferences
32
+
33
+ {
34
+ "available_apps": [
35
+ { "code": "dashboard", "name": "Dashboard", "route": "/dashboard" },
36
+ { "code": "tickets", "name": "Tickets", "route": "/tickets" },
37
+ { "code": "settings", "name": "Settings", "route": "/settings" },
38
+ // ... all apps
39
+ ],
40
+ "current_favorites": ["dashboard", "tickets", "inventory"],
41
+ "default_favorites": ["dashboard", "tickets", "map"]
42
+ }
43
+ ```
44
+
45
+ ### Proposed API Response Structure
46
+ ```json
47
+ GET /api/v1/users/me/preferences
48
+
49
+ {
50
+ "available_apps": [
51
+ { "code": "dashboard", "name": "Dashboard", "route": "/dashboard" },
52
+ { "code": "tickets", "name": "Tickets", "route": "/tickets" },
53
+ { "code": "inventory", "name": "Inventory", "route": "/inventory" },
54
+ { "code": "settings", "name": "Settings", "route": "/settings" },
55
+ { "code": "help", "name": "Help", "route": "/help" },
56
+ // ... all apps user has access to
57
+ ],
58
+
59
+ "meta_apps": [
60
+ "dashboard",
61
+ "settings",
62
+ "help",
63
+ "notifications",
64
+ "search"
65
+ ],
66
+
67
+ "current_favorites": ["dashboard", "tickets", "inventory"],
68
+ "default_favorites": ["dashboard", "tickets", "map"]
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## App Categorization by Role
75
+
76
+ ### For Project-Based Roles (PM, Sales Manager, Dispatcher, Field Agent)
77
+
78
+ **Meta Apps** (Available outside project context):
79
+ - `dashboard` - For returning to project selection
80
+ - `settings` - User preferences
81
+ - `help` - Help & support
82
+ - `notifications` - User notifications
83
+ - `search` - Global search (if applicable)
84
+
85
+ **Project Apps** (Only available inside project context):
86
+ - `tickets` - Work orders/tickets
87
+ - `inventory` - Inventory management
88
+ - `map` - Map view
89
+ - `finance` - Financial data
90
+ - `expenses` - Expense tracking
91
+ - `payroll` - Payroll (if applicable)
92
+ - `team` - Team management
93
+ - `projects` - Project list (might be meta for some roles)
94
+ - `reports` - Reporting
95
+ - `customers` - Customer management
96
+ - `documents` - Document management
97
+
98
+ ### For Admin Roles (Platform Admin, Client Admin, Contractor Admin)
99
+
100
+ **All apps are always available** - No context switching needed.
101
+
102
+ Their apps might include:
103
+ - `dashboard` - Admin dashboard
104
+ - `organizations` - Organization management (Platform Admin)
105
+ - `users` - User management
106
+ - `activity` - Activity logs
107
+ - `billing` - Billing (Platform Admin)
108
+ - `invitations` - User invitations
109
+ - `team` - Team management (Org Admins)
110
+ - `projects` - Project management (Org Admins)
111
+ - `settings` - Settings
112
+ - `help` - Help
113
+
114
+ ---
115
+
116
+ ## Backend Implementation Requirements
117
+
118
+ ### 1. Add `meta_apps` Field to User Preferences Response
119
+
120
+ The backend should return a `meta_apps` array that lists app codes that are available **outside** of a project context.
121
+
122
+ **Logic:**
123
+ - For **project-based roles**: Return a curated list of meta apps
124
+ - For **admin roles**: Return empty array `[]` or all apps (since they don't have context switching)
125
+
126
+ ### 2. Role-Based Meta Apps Configuration
127
+
128
+ Create a configuration mapping for each role:
129
+
130
+ ```python
131
+ # Example backend configuration
132
+ META_APPS_BY_ROLE = {
133
+ 'project_manager': ['dashboard', 'settings', 'help', 'notifications', 'search'],
134
+ 'sales_manager': ['dashboard', 'settings', 'help', 'notifications', 'search'],
135
+ 'dispatcher': ['dashboard', 'settings', 'help', 'notifications', 'search'],
136
+ 'field_agent': ['dashboard', 'settings', 'help', 'notifications', 'search'],
137
+
138
+ # Admin roles - no context switching, so empty or all apps
139
+ 'platform_admin': [],
140
+ 'client_admin': [],
141
+ 'contractor_admin': [],
142
+ }
143
+ ```
144
+
145
+ ### 3. Update Preferences Endpoint
146
+
147
+ **Endpoint:** `GET /api/v1/users/me/preferences`
148
+
149
+ **Response Schema:**
150
+ ```typescript
151
+ {
152
+ available_apps: Array<{
153
+ code: string;
154
+ name: string;
155
+ route: string;
156
+ }>;
157
+
158
+ meta_apps: string[]; // NEW FIELD - Array of app codes available outside project context
159
+
160
+ current_favorites: string[];
161
+ default_favorites: string[];
162
+ }
163
+ ```
164
+
165
+ ### 4. Validation Rules
166
+
167
+ - `meta_apps` should only contain codes that exist in `available_apps`
168
+ - For admin roles, `meta_apps` can be empty or contain all app codes
169
+ - For project-based roles, `meta_apps` should be a subset of `available_apps`
170
+
171
+ ---
172
+
173
+ ## Frontend Implementation Plan
174
+
175
+ Once the backend provides `meta_apps`, we'll implement the following:
176
+
177
+ ### 1. Detect Project Context
178
+
179
+ ```typescript
180
+ // In a new hook or context
181
+ const location = useLocation();
182
+ const isInProjectContext = location.pathname.startsWith('/project/');
183
+ ```
184
+
185
+ ### 2. Filter Apps in StickyHeader
186
+
187
+ ```typescript
188
+ const visibleFavoriteApps = useMemo(() => {
189
+ const { meta_apps } = availableApps;
190
+ const isProjectBasedRole = !['platform_admin', 'client_admin', 'contractor_admin'].includes(user.role);
191
+
192
+ // If outside project context and user is project-based role
193
+ if (!isInProjectContext && isProjectBasedRole) {
194
+ // Show only meta apps from favorites
195
+ return accessibleFavoriteApps.filter(app => meta_apps.includes(app.code));
196
+ }
197
+
198
+ // Otherwise show all favorites
199
+ return accessibleFavoriteApps;
200
+ }, [isInProjectContext, user.role, accessibleFavoriteApps, availableApps.meta_apps]);
201
+ ```
202
+
203
+ ### 3. Filter Apps in AppLauncher
204
+
205
+ ```typescript
206
+ const visibleApps = useMemo(() => {
207
+ const { meta_apps } = availableApps;
208
+ const isProjectBasedRole = !['platform_admin', 'client_admin', 'contractor_admin'].includes(user.role);
209
+
210
+ // If outside project context and user is project-based role
211
+ if (!isInProjectContext && isProjectBasedRole) {
212
+ // Show only meta apps
213
+ return accessibleApps.filter(app => meta_apps.includes(app.code));
214
+ }
215
+
216
+ // Otherwise show all apps
217
+ return accessibleApps;
218
+ }, [isInProjectContext, user.role, accessibleApps, availableApps.meta_apps]);
219
+ ```
220
+
221
+ ### 4. Handle Favorites
222
+
223
+ **Important:** When a user is outside a project context, their favorite apps should still be **saved** but just **not displayed** if they're project apps.
224
+
225
+ **Behavior:**
226
+ - User sets favorites: `['dashboard', 'tickets', 'inventory', 'settings']`
227
+ - Outside project context: Only show `['dashboard', 'settings']` (meta apps)
228
+ - Inside project context: Show all `['dashboard', 'tickets', 'inventory', 'settings']`
229
+
230
+ This ensures when they enter a project, their favorites are immediately available.
231
+
232
+ ---
233
+
234
+ ## User Experience Flow
235
+
236
+ ### Scenario 1: PM Logs In (Multiple Projects)
237
+
238
+ 1. **Login** → Lands on `/dashboard` (project selection)
239
+ 2. **Header Pills**: Shows only meta apps from favorites (e.g., Dashboard, Settings)
240
+ 3. **App Launcher**: Shows only meta apps (Dashboard, Settings, Help, Notifications)
241
+ 4. **User selects project** → Navigates to `/project/abc-123`
242
+ 5. **Header Pills**: Now shows ALL favorites (Dashboard, Tickets, Inventory, Settings)
243
+ 6. **App Launcher**: Shows ALL available apps
244
+
245
+ ### Scenario 2: PM with Single Project (Auto-Navigate)
246
+
247
+ 1. **Login** → Lands on `/dashboard`
248
+ 2. **Auto-navigates** to `/project/abc-123` (single project)
249
+ 3. **Header Pills**: Shows all favorites immediately
250
+ 4. **App Launcher**: Shows all apps immediately
251
+ 5. **User never sees the restricted view** (seamless experience)
252
+
253
+ ### Scenario 3: Admin Logs In
254
+
255
+ 1. **Login** → Lands on `/dashboard` (admin dashboard)
256
+ 2. **Header Pills**: Shows all favorites (no filtering)
257
+ 3. **App Launcher**: Shows all apps (no filtering)
258
+ 4. **No context switching** - always sees everything
259
+
260
+ ---
261
+
262
+ ## Benefits of Backend-Driven Approach
263
+
264
+ ### 1. **Centralized Logic**
265
+ - App categorization lives in one place (backend)
266
+ - Easy to update which apps are meta vs project-specific
267
+ - No hardcoded lists in frontend
268
+
269
+ ### 2. **Role Flexibility**
270
+ - Different roles can have different meta apps
271
+ - Easy to add new roles with custom meta app lists
272
+ - Backend controls access, frontend just displays
273
+
274
+ ### 3. **Consistency**
275
+ - Same logic applies to all frontend components
276
+ - No risk of frontend/backend mismatch
277
+ - Single source of truth
278
+
279
+ ### 4. **Scalability**
280
+ - Easy to add new app types in the future
281
+ - Can introduce more context levels if needed
282
+ - Frontend code stays simple
283
+
284
+ ### 5. **Security**
285
+ - Backend enforces what apps are available
286
+ - Frontend can't accidentally show unauthorized apps
287
+ - Access control remains server-side
288
+
289
+ ---
290
+
291
+ ## Migration & Backward Compatibility
292
+
293
+ ### Phase 1: Backend Update
294
+ - Add `meta_apps` field to preferences response
295
+ - Populate based on user role
296
+ - Ensure existing fields remain unchanged
297
+
298
+ ### Phase 2: Frontend Update
299
+ - Update `useUserPreferences` types to include `meta_apps`
300
+ - Implement filtering logic in StickyHeader
301
+ - Implement filtering logic in AppLauncher
302
+ - Add context detection
303
+
304
+ ### Phase 3: Testing
305
+ - Test all role types
306
+ - Test project context switching
307
+ - Test favorite app persistence
308
+ - Test edge cases (direct URLs, bookmarks)
309
+
310
+ ### Backward Compatibility
311
+ - If `meta_apps` is missing or empty, show all apps (current behavior)
312
+ - No breaking changes for existing users
313
+ - Gradual rollout possible
314
+
315
+ ---
316
+
317
+ ## Open Questions for Backend Team
318
+
319
+ 1. **Should `meta_apps` be role-based or user-specific?**
320
+ - Recommendation: Role-based for consistency, but allow user overrides if needed
321
+
322
+ 2. **Should `projects` app be meta or project-specific?**
323
+ - For PM: Probably meta (they manage multiple projects)
324
+ - For Field Agent: Probably project-specific (they work in one project at a time)
325
+
326
+ 3. **Should `dashboard` always be in meta apps?**
327
+ - Yes - users need a way to return to project selection
328
+
329
+ 4. **How to handle apps that work in both contexts?**
330
+ - Example: `search` might work globally or within a project
331
+ - Recommendation: If it works outside project, include in meta apps
332
+
333
+ 5. **Should we support per-user customization of meta apps?**
334
+ - Or is role-based sufficient?
335
+ - Recommendation: Start with role-based, add user customization later if needed
336
+
337
+ ---
338
+
339
+ ## Example API Responses by Role
340
+
341
+ ### Project Manager
342
+ ```json
343
+ {
344
+ "available_apps": [
345
+ { "code": "dashboard", "name": "Dashboard", "route": "/dashboard" },
346
+ { "code": "tickets", "name": "Tickets", "route": "/tickets" },
347
+ { "code": "inventory", "name": "Inventory", "route": "/inventory" },
348
+ { "code": "map", "name": "Map", "route": "/map" },
349
+ { "code": "finance", "name": "Finance", "route": "/finance" },
350
+ { "code": "team", "name": "Team", "route": "/team" },
351
+ { "code": "reports", "name": "Reports", "route": "/reports" },
352
+ { "code": "settings", "name": "Settings", "route": "/settings" },
353
+ { "code": "help", "name": "Help", "route": "/help" },
354
+ { "code": "notifications", "name": "Notifications", "route": "/notifications" }
355
+ ],
356
+ "meta_apps": ["dashboard", "settings", "help", "notifications"],
357
+ "current_favorites": ["dashboard", "tickets", "inventory", "map"],
358
+ "default_favorites": ["dashboard", "tickets", "inventory", "map"]
359
+ }
360
+ ```
361
+
362
+ ### Platform Admin
363
+ ```json
364
+ {
365
+ "available_apps": [
366
+ { "code": "dashboard", "name": "Dashboard", "route": "/dashboard" },
367
+ { "code": "organizations", "name": "Organizations", "route": "/organizations" },
368
+ { "code": "users", "name": "Users", "route": "/users" },
369
+ { "code": "activity", "name": "Activity", "route": "/activity" },
370
+ { "code": "billing", "name": "Billing", "route": "/billing" },
371
+ { "code": "invitations", "name": "Invitations", "route": "/invitations" },
372
+ { "code": "settings", "name": "Settings", "route": "/settings" },
373
+ { "code": "help", "name": "Help", "route": "/help" }
374
+ ],
375
+ "meta_apps": [], // Empty - no context switching for admins
376
+ "current_favorites": ["dashboard", "organizations", "users"],
377
+ "default_favorites": ["dashboard", "organizations", "users"]
378
+ }
379
+ ```
380
+
381
+ ### Field Agent
382
+ ```json
383
+ {
384
+ "available_apps": [
385
+ { "code": "dashboard", "name": "Dashboard", "route": "/dashboard" },
386
+ { "code": "tickets", "name": "Tickets", "route": "/tickets" },
387
+ { "code": "map", "name": "Map", "route": "/map" },
388
+ { "code": "inventory", "name": "Inventory", "route": "/inventory" },
389
+ { "code": "settings", "name": "Settings", "route": "/settings" },
390
+ { "code": "help", "name": "Help", "route": "/help" }
391
+ ],
392
+ "meta_apps": ["dashboard", "settings", "help"],
393
+ "current_favorites": ["tickets", "map", "inventory"],
394
+ "default_favorites": ["tickets", "map", "inventory"]
395
+ }
396
+ ```
397
+
398
+ ---
399
+
400
+ ## Summary
401
+
402
+ **What we need from backend:**
403
+ 1. Add `meta_apps: string[]` field to `/api/v1/users/me/preferences` response
404
+ 2. Populate `meta_apps` based on user role
405
+ 3. For project-based roles: Include apps that work outside project context
406
+ 4. For admin roles: Empty array or all apps (no context switching)
407
+
408
+ **What frontend will do:**
409
+ 1. Detect if user is in project context (via route)
410
+ 2. Filter favorite apps in header based on `meta_apps` when outside project
411
+ 3. Filter all apps in launcher based on `meta_apps` when outside project
412
+ 4. Show all apps normally when inside project or for admin roles
413
+
414
+ **Result:**
415
+ - Clean UX for project selection
416
+ - No irrelevant apps shown
417
+ - Seamless transition when entering/exiting projects
418
+ - Backend controls app categorization
419
+ - Frontend just displays what backend provides
420
+
421
+ ---
422
+
423
+ **Next Steps:**
424
+ 1. Backend team reviews this proposal
425
+ 2. Backend implements `meta_apps` field
426
+ 3. Frontend team updates filtering logic
427
+ 4. QA tests all role types and contexts
428
+ 5. Deploy and monitor
429
+
430
+ ---
431
+
432
+ **Contact:** Frontend Team
433
+ **For Questions:** Please review and provide feedback on the proposed API changes
src/app/api/v1/auth.py CHANGED
@@ -21,6 +21,7 @@ from app.config.apps import (
21
  get_available_apps_for_role,
22
  get_available_app_codes_for_role,
23
  get_default_favorites_for_role,
 
24
  validate_apps_for_role,
25
  get_app_by_code,
26
  APPS
@@ -518,6 +519,9 @@ async def login_full(
518
  # Step 4: Get available apps
519
  available_apps = get_available_apps_for_role(user.role)
520
 
 
 
 
521
  # Step 5: Audit log (async, doesn't block response)
522
  AuditService.log_auth_event(
523
  db=db,
@@ -548,7 +552,8 @@ async def login_full(
548
  "contractor_id": str(user.contractor_id) if user.contractor_id else None,
549
  },
550
  "preferences": preferences,
551
- "available_apps": available_apps
 
552
  }
553
 
554
  except HTTPException:
@@ -1237,11 +1242,15 @@ async def get_available_apps_for_user(
1237
  # Get default favorites
1238
  default_favorites = get_default_favorites_for_role(current_user.role)
1239
 
 
 
 
1240
  return {
1241
  "role": current_user.role,
1242
  "current_favorites": current_favorites,
1243
  "available_apps": available_apps_detail, # Full app metadata
1244
  "default_favorites": default_favorites,
 
1245
  "max_favorites": 6
1246
  }
1247
 
 
21
  get_available_apps_for_role,
22
  get_available_app_codes_for_role,
23
  get_default_favorites_for_role,
24
+ get_meta_apps_for_role,
25
  validate_apps_for_role,
26
  get_app_by_code,
27
  APPS
 
519
  # Step 4: Get available apps
520
  available_apps = get_available_apps_for_role(user.role)
521
 
522
+ # Step 4b: Get meta apps (apps available outside project context)
523
+ meta_apps = get_meta_apps_for_role(user.role)
524
+
525
  # Step 5: Audit log (async, doesn't block response)
526
  AuditService.log_auth_event(
527
  db=db,
 
552
  "contractor_id": str(user.contractor_id) if user.contractor_id else None,
553
  },
554
  "preferences": preferences,
555
+ "available_apps": available_apps,
556
+ "meta_apps": meta_apps
557
  }
558
 
559
  except HTTPException:
 
1242
  # Get default favorites
1243
  default_favorites = get_default_favorites_for_role(current_user.role)
1244
 
1245
+ # Get meta apps (apps available outside project context)
1246
+ meta_apps = get_meta_apps_for_role(current_user.role)
1247
+
1248
  return {
1249
  "role": current_user.role,
1250
  "current_favorites": current_favorites,
1251
  "available_apps": available_apps_detail, # Full app metadata
1252
  "default_favorites": default_favorites,
1253
+ "meta_apps": meta_apps, # Apps available outside project context
1254
  "max_favorites": 6
1255
  }
1256
 
src/app/config/apps.py CHANGED
@@ -431,6 +431,26 @@ DEFAULT_FAVORITE_APPS: Dict[str, List[str]] = {
431
  }
432
 
433
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  # ============================================================
435
  # HELPER FUNCTIONS
436
  # ============================================================
@@ -469,6 +489,11 @@ def get_default_favorites_for_role(role: str) -> List[str]:
469
  return DEFAULT_FAVORITE_APPS.get(role, [])
470
 
471
 
 
 
 
 
 
472
  def validate_apps_for_role(app_codes: List[str], role: str) -> tuple[bool, List[str]]:
473
  """
474
  Validate that app codes are available for the given role
 
431
  }
432
 
433
 
434
+ # ============================================================
435
+ # META APPS - Apps available outside project context
436
+ # Used for context-aware navigation in project-based roles
437
+ # ============================================================
438
+
439
+ META_APPS_BY_ROLE: Dict[str, List[str]] = {
440
+ # Project-based roles: Only show context-independent apps outside project
441
+ "project_manager": ["dashboard", "settings", "notifications", "help"],
442
+ "sales_manager": ["dashboard", "settings", "notifications", "help"],
443
+ "dispatcher": ["dashboard", "settings", "notifications", "help"],
444
+ "field_agent": ["dashboard", "settings", "notifications", "help"],
445
+ "sales_agent": ["dashboard", "settings", "notifications", "help"],
446
+
447
+ # Admin roles: No context switching - empty list means show all apps always
448
+ "platform_admin": [],
449
+ "client_admin": [],
450
+ "contractor_admin": []
451
+ }
452
+
453
+
454
  # ============================================================
455
  # HELPER FUNCTIONS
456
  # ============================================================
 
489
  return DEFAULT_FAVORITE_APPS.get(role, [])
490
 
491
 
492
+ def get_meta_apps_for_role(role: str) -> List[str]:
493
+ """Get meta app codes for a role (apps available outside project context)"""
494
+ return META_APPS_BY_ROLE.get(role, [])
495
+
496
+
497
  def validate_apps_for_role(app_codes: List[str], role: str) -> tuple[bool, List[str]]:
498
  """
499
  Validate that app codes are available for the given role