kamau1 commited on
Commit
569a682
·
1 Parent(s): be87f92

app rearrangement

Browse files
docs/agent/thoughts/user-profile.md ADDED
@@ -0,0 +1,629 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # User Profile Management - Frontend Guide
2
+
3
+ Quick reference for building user profile pages. All endpoints require authentication via Bearer token.
4
+
5
+ ---
6
+
7
+ ## Base URL
8
+ ```
9
+ /api/v1
10
+ ```
11
+
12
+ ---
13
+
14
+ ## 1. Get Current User Profile
15
+
16
+ **Get my complete profile**
17
+ ```http
18
+ GET /profile/me
19
+ ```
20
+
21
+ **Response:**
22
+ ```json
23
+ {
24
+ "basic_info": {
25
+ "id": "uuid",
26
+ "name": "John Doe",
27
+ "email": "john@example.com",
28
+ "phone": "+254712345678",
29
+ "phone_alternate": "+254787654321",
30
+ "display_name": "Johnny",
31
+ "id_number": "12345678",
32
+ "emergency_contact_name": "Jane Doe",
33
+ "emergency_contact_phone": "+254700000000",
34
+ "role": "field_agent",
35
+ "status": "active",
36
+ "is_active": true,
37
+ "client_id": "uuid",
38
+ "contractor_id": null,
39
+ "created_at": "2025-01-01T00:00:00Z",
40
+ "updated_at": "2025-01-01T00:00:00Z"
41
+ },
42
+ "health_info": {
43
+ "blood_type": "O+",
44
+ "allergies": "Peanuts",
45
+ "chronic_conditions": "None",
46
+ "medications": "None",
47
+ "last_medical_check": "2024-12-01",
48
+ "medical_notes": "All good"
49
+ },
50
+ "ppe_sizes": {
51
+ "height": "180cm",
52
+ "weight": "75kg",
53
+ "waist": "32",
54
+ "shoe_size": "42",
55
+ "helmet_size": "M",
56
+ "shirt_size": "L",
57
+ "pants_size": "32",
58
+ "glove_size": "M",
59
+ "vest_size": "L"
60
+ },
61
+ "location": {
62
+ "current_location_name": "Nairobi Office",
63
+ "current_country": "Kenya",
64
+ "current_region": "Nairobi",
65
+ "current_city": "Nairobi",
66
+ "current_address_line1": "123 Main St",
67
+ "current_address_line2": "Apt 4B",
68
+ "current_maps_link": "https://maps.google.com/...",
69
+ "current_latitude": -1.286389,
70
+ "current_longitude": 36.817223,
71
+ "current_location_updated_at": "2025-01-01T00:00:00Z"
72
+ },
73
+ "completion_status": {
74
+ "basic_info": true,
75
+ "health_info": true,
76
+ "ppe_sizes": false,
77
+ "financial_accounts": true,
78
+ "documents": false,
79
+ "location": true,
80
+ "completion_percentage": 67
81
+ },
82
+ "financial_accounts_count": 2,
83
+ "documents_count": 3,
84
+ "asset_assignments_count": 1
85
+ }
86
+ ```
87
+
88
+ ---
89
+
90
+ ## 2. Update Profile Sections
91
+
92
+ ### Basic Info
93
+ ```http
94
+ PUT /profile/me/basic
95
+ Content-Type: application/json
96
+
97
+ {
98
+ "name": "John Doe",
99
+ "phone": "+254712345678",
100
+ "phone_alternate": "+254787654321",
101
+ "email": "john@example.com",
102
+ "display_name": "Johnny",
103
+ "emergency_contact_name": "Jane Doe",
104
+ "emergency_contact_phone": "+254700000000"
105
+ }
106
+ ```
107
+ All fields optional. Phone numbers must start with `+`.
108
+
109
+ ### Health Info
110
+ ```http
111
+ PUT /profile/me/health
112
+ Content-Type: application/json
113
+
114
+ {
115
+ "blood_type": "O+",
116
+ "allergies": "Peanuts, Shellfish",
117
+ "chronic_conditions": "Asthma",
118
+ "medications": "Inhaler",
119
+ "last_medical_check": "2024-12-01",
120
+ "medical_notes": "Regular checkups"
121
+ }
122
+ ```
123
+ Blood type: `A+`, `A-`, `B+`, `B-`, `AB+`, `AB-`, `O+`, `O-`
124
+
125
+ ### PPE Sizes
126
+ ```http
127
+ PUT /profile/me/ppe
128
+ Content-Type: application/json
129
+
130
+ {
131
+ "height": "180cm",
132
+ "weight": "75kg",
133
+ "waist": "32",
134
+ "shoe_size": "42",
135
+ "helmet_size": "M",
136
+ "shirt_size": "L",
137
+ "pants_size": "32",
138
+ "glove_size": "M",
139
+ "vest_size": "L"
140
+ }
141
+ ```
142
+ Sizes: `XS`, `S`, `M`, `L`, `XL`, `XXL`, `XXXL`
143
+
144
+ ### Location
145
+ ```http
146
+ PUT /profile/me/location
147
+ Content-Type: application/json
148
+
149
+ {
150
+ "current_location_name": "Nairobi Office",
151
+ "current_country": "Kenya",
152
+ "current_region": "Nairobi",
153
+ "current_city": "Nairobi",
154
+ "current_address_line1": "123 Main St",
155
+ "current_address_line2": "Apt 4B",
156
+ "current_maps_link": "https://maps.google.com/...",
157
+ "current_latitude": -1.286389,
158
+ "current_longitude": 36.817223
159
+ }
160
+ ```
161
+ Latitude: -90 to 90, Longitude: -180 to 180
162
+
163
+ ### Bulk Update (Profile Setup Wizard)
164
+ ```http
165
+ PUT /profile/me/bulk
166
+ Content-Type: application/json
167
+
168
+ {
169
+ "basic_info": { /* BasicProfileUpdate */ },
170
+ "health_info": { /* HealthInfoUpdate */ },
171
+ "ppe_sizes": { /* PPESizesUpdate */ },
172
+ "location": { /* LocationUpdate */ }
173
+ }
174
+ ```
175
+ Include only sections you want to update.
176
+
177
+ ---
178
+
179
+ ## 3. Profile Completion
180
+
181
+ **Get completion status**
182
+ ```http
183
+ GET /profile/me/completion
184
+ ```
185
+
186
+ **Response:**
187
+ ```json
188
+ {
189
+ "basic_info": true,
190
+ "health_info": true,
191
+ "ppe_sizes": false,
192
+ "financial_accounts": true,
193
+ "documents": false,
194
+ "location": true,
195
+ "completion_percentage": 67
196
+ }
197
+ ```
198
+
199
+ **Validate profile**
200
+ ```http
201
+ GET /profile/me/validation
202
+ ```
203
+
204
+ **Response:**
205
+ ```json
206
+ {
207
+ "is_valid": false,
208
+ "missing_fields": ["ppe_sizes", "emergency_contact_phone"],
209
+ "invalid_fields": {
210
+ "phone": "Must start with country code"
211
+ },
212
+ "warnings": ["Medical check is overdue"]
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ## 4. Financial Accounts (Payout Details)
219
+
220
+ **List my accounts**
221
+ ```http
222
+ GET /financial-accounts/me
223
+ ```
224
+
225
+ **Response:**
226
+ ```json
227
+ [
228
+ {
229
+ "id": "uuid",
230
+ "user_id": "uuid",
231
+ "account_name": "M-Pesa Main",
232
+ "payout_method": "mobile_money",
233
+ "mobile_money_provider": "Safaricom",
234
+ "mobile_money_phone": "+254712345678",
235
+ "mobile_money_account_name": "John Doe",
236
+ "bank_name": null,
237
+ "bank_account_name": null,
238
+ "bank_account_number": null,
239
+ "bank_branch": null,
240
+ "bank_swift_code": null,
241
+ "payout_frequency": "weekly",
242
+ "is_primary": true,
243
+ "is_active": true,
244
+ "is_verified": true,
245
+ "verified_at": "2025-01-01T00:00:00Z",
246
+ "notes": null,
247
+ "created_at": "2025-01-01T00:00:00Z",
248
+ "updated_at": "2025-01-01T00:00:00Z"
249
+ }
250
+ ]
251
+ ```
252
+
253
+ **Create account**
254
+ ```http
255
+ POST /financial-accounts/me
256
+ Content-Type: application/json
257
+
258
+ {
259
+ "account_name": "M-Pesa Main",
260
+ "payout_method": "mobile_money",
261
+ "mobile_money_provider": "Safaricom",
262
+ "mobile_money_phone": "+254712345678",
263
+ "mobile_money_account_name": "John Doe",
264
+ "payout_frequency": "weekly",
265
+ "is_primary": true,
266
+ "notes": "Primary payment method"
267
+ }
268
+ ```
269
+
270
+ **Update account**
271
+ ```http
272
+ PUT /financial-accounts/me/{account_id}
273
+ Content-Type: application/json
274
+
275
+ {
276
+ "account_name": "M-Pesa Updated",
277
+ "is_primary": false,
278
+ "is_active": true
279
+ }
280
+ ```
281
+
282
+ **Delete account**
283
+ ```http
284
+ DELETE /financial-accounts/me/{account_id}
285
+ ```
286
+
287
+ **Payout Methods:**
288
+ - `mobile_money` - M-Pesa, Airtel Money, etc.
289
+ - `bank_transfer` - Bank account details
290
+ - `cash` - Cash pickup
291
+ - `other` - Alternative methods
292
+
293
+ ---
294
+
295
+ ## 5. View Other Users (Manager Access)
296
+
297
+ **Get user profile**
298
+ ```http
299
+ GET /profile/{user_id}
300
+ ```
301
+ Returns same structure as `/profile/me`. Requires permission.
302
+
303
+ **Check edit permissions**
304
+ ```http
305
+ GET /profile/{user_id}/permissions
306
+ ```
307
+
308
+ **Response:**
309
+ ```json
310
+ {
311
+ "can_edit_basic_info": true,
312
+ "can_edit_health_info": true,
313
+ "can_edit_ppe_sizes": true,
314
+ "can_edit_location": true,
315
+ "can_edit_financial_accounts": false,
316
+ "can_upload_documents": true,
317
+ "can_edit_role": false,
318
+ "can_edit_status": false,
319
+ "can_assign_assets": true,
320
+ "can_view_health_info": true,
321
+ "can_view_financial_accounts": false,
322
+ "can_view_documents": true
323
+ }
324
+ ```
325
+ Use this to show/hide edit buttons in UI.
326
+
327
+ **Update user sections**
328
+ ```http
329
+ PUT /profile/{user_id}/basic
330
+ PUT /profile/{user_id}/health
331
+ PUT /profile/{user_id}/ppe
332
+ PUT /profile/{user_id}/location
333
+ ```
334
+ Same request body as `/profile/me/*` endpoints. Permission checked.
335
+
336
+ ---
337
+
338
+ ## 6. User Management (Admin)
339
+
340
+ **List users with filters**
341
+ ```http
342
+ GET /users?skip=0&limit=50&role=field_agent&status=active&is_active=true
343
+ ```
344
+
345
+ **Query params:**
346
+ - `skip` (int, default 0) - Pagination offset
347
+ - `limit` (int, default 50, max 100) - Page size
348
+ - `email` (string) - Search by email
349
+ - `phone` (string) - Search by phone
350
+ - `role` (string) - Filter by role
351
+ - `status` (string) - Filter by status
352
+ - `is_active` (bool) - Filter by active
353
+ - `client_id` (uuid) - Filter by client
354
+ - `contractor_id` (uuid) - Filter by contractor
355
+ - `sort_by` (string, default created_at) - Sort field
356
+ - `sort_order` (string, default desc) - asc/desc
357
+
358
+ **Response:**
359
+ ```json
360
+ {
361
+ "users": [ /* array of UserResponse */ ],
362
+ "total": 150,
363
+ "page": 1,
364
+ "page_size": 50,
365
+ "total_pages": 3
366
+ }
367
+ ```
368
+
369
+ **Get user by ID**
370
+ ```http
371
+ GET /users/{user_id}
372
+ ```
373
+
374
+ **Update user**
375
+ ```http
376
+ PUT /users/{user_id}
377
+ Content-Type: application/json
378
+
379
+ {
380
+ "name": "John Doe",
381
+ "email": "john@example.com",
382
+ "phone": "+254712345678",
383
+ "display_name": "Johnny",
384
+ "is_active": true
385
+ }
386
+ ```
387
+
388
+ **Change status**
389
+ ```http
390
+ POST /users/{user_id}/status
391
+ Content-Type: application/json
392
+
393
+ {
394
+ "status": "active",
395
+ "reason": "Profile completed"
396
+ }
397
+ ```
398
+ Statuses: `invited`, `pending_setup`, `active`, `suspended`
399
+
400
+ **Change role**
401
+ ```http
402
+ POST /users/{user_id}/role
403
+ Content-Type: application/json
404
+
405
+ {
406
+ "role": "project_manager",
407
+ "reason": "Promotion"
408
+ }
409
+ ```
410
+ Roles: `platform_admin`, `client_admin`, `contractor_admin`, `sales_manager`, `project_manager`, `dispatcher`, `field_agent`, `sales_agent`
411
+
412
+ **Deactivate user**
413
+ ```http
414
+ POST /users/{user_id}/deactivate?reason=Left company
415
+ ```
416
+
417
+ **Activate user**
418
+ ```http
419
+ POST /users/{user_id}/activate
420
+ ```
421
+
422
+ **Reset password (admin)**
423
+ ```http
424
+ POST /users/{user_id}/reset-password
425
+ Content-Type: application/json
426
+
427
+ {
428
+ "new_password": "NewSecure123"
429
+ }
430
+ ```
431
+
432
+ ---
433
+
434
+ ## 7. Bulk Operations
435
+
436
+ **Bulk update users**
437
+ ```http
438
+ POST /users/bulk/update
439
+ Content-Type: application/json
440
+
441
+ {
442
+ "user_ids": ["uuid1", "uuid2", "uuid3"],
443
+ "updates": {
444
+ "is_active": true,
445
+ "status": "active"
446
+ },
447
+ "reason": "Mass activation"
448
+ }
449
+ ```
450
+
451
+ **Bulk change status**
452
+ ```http
453
+ POST /users/bulk/status
454
+ Content-Type: application/json
455
+
456
+ {
457
+ "user_ids": ["uuid1", "uuid2"],
458
+ "status": "suspended",
459
+ "reason": "Contract ended"
460
+ }
461
+ ```
462
+
463
+ **Bulk change role**
464
+ ```http
465
+ POST /users/bulk/role
466
+ Content-Type: application/json
467
+
468
+ {
469
+ "user_ids": ["uuid1", "uuid2"],
470
+ "role": "field_agent",
471
+ "reason": "Restructuring"
472
+ }
473
+ ```
474
+
475
+ **Response:**
476
+ ```json
477
+ {
478
+ "total": 3,
479
+ "successful": 2,
480
+ "failed": 1,
481
+ "errors": [
482
+ {
483
+ "user_id": "uuid3",
484
+ "error": "Permission denied"
485
+ }
486
+ ]
487
+ }
488
+ ```
489
+
490
+ ---
491
+
492
+ ## 8. Asset Assignments
493
+
494
+ **Get my assets**
495
+ ```http
496
+ GET /asset-assignments/me
497
+ ```
498
+
499
+ **Response:**
500
+ ```json
501
+ [
502
+ {
503
+ "id": "uuid",
504
+ "user_id": "uuid",
505
+ "asset_type": "vehicle",
506
+ "asset_name": "Toyota Hilux KBX 123A",
507
+ "asset_description": "Field work truck",
508
+ "serial_number": "ABC123",
509
+ "registration_number": "KBX 123A",
510
+ "asset_value": 2500000.00,
511
+ "currency": "KES",
512
+ "assigned_at": "2025-01-01T00:00:00Z",
513
+ "assigned_by_user_id": "uuid",
514
+ "expected_return_at": null,
515
+ "returned_at": null,
516
+ "condition_on_assign": "good",
517
+ "condition_on_return": null,
518
+ "is_active": true,
519
+ "notes": "Handle with care",
520
+ "created_at": "2025-01-01T00:00:00Z",
521
+ "updated_at": "2025-01-01T00:00:00Z"
522
+ }
523
+ ]
524
+ ```
525
+
526
+ **Get user's assets (manager)**
527
+ ```http
528
+ GET /asset-assignments/user/{user_id}
529
+ ```
530
+
531
+ **Assign asset (manager)**
532
+ ```http
533
+ POST /asset-assignments/user/{user_id}
534
+ Content-Type: application/json
535
+
536
+ {
537
+ "asset_type": "laptop",
538
+ "asset_name": "Dell Latitude 5420",
539
+ "asset_description": "Work laptop",
540
+ "serial_number": "SN12345",
541
+ "asset_value": 65000.00,
542
+ "condition_on_assign": "new",
543
+ "notes": "Company property"
544
+ }
545
+ ```
546
+
547
+ **Return asset**
548
+ ```http
549
+ POST /asset-assignments/{assignment_id}/return
550
+ Content-Type: application/json
551
+
552
+ {
553
+ "condition_on_return": "good",
554
+ "notes": "Returned in good condition"
555
+ }
556
+ ```
557
+
558
+ ---
559
+
560
+ ## Permission Model
561
+
562
+ ### Platform Admin
563
+ - Full access to all users
564
+ - Can change roles, organizations
565
+ - Can delete users
566
+
567
+ ### Client/Contractor Admin
568
+ - Manage users in their organization
569
+ - Cannot change to/from `platform_admin`
570
+ - Cannot move users between organizations
571
+
572
+ ### Project/Sales Manager
573
+ - View team profiles
574
+ - Edit team profiles (permission checked per field)
575
+
576
+ ### Field/Sales Agent
577
+ - View & edit own profile only
578
+ - Manage own financial accounts
579
+
580
+ ---
581
+
582
+ ## Error Responses
583
+
584
+ **401 Unauthorized**
585
+ ```json
586
+ {
587
+ "detail": "Not authenticated"
588
+ }
589
+ ```
590
+
591
+ **403 Forbidden**
592
+ ```json
593
+ {
594
+ "detail": "You don't have permission to view this user's profile"
595
+ }
596
+ ```
597
+
598
+ **404 Not Found**
599
+ ```json
600
+ {
601
+ "detail": "User not found"
602
+ }
603
+ ```
604
+
605
+ **422 Validation Error**
606
+ ```json
607
+ {
608
+ "detail": [
609
+ {
610
+ "loc": ["body", "phone"],
611
+ "msg": "Phone number must start with country code",
612
+ "type": "value_error"
613
+ }
614
+ ]
615
+ }
616
+ ```
617
+
618
+ ---
619
+
620
+ ## Notes
621
+
622
+ 1. **All dates** use ISO 8601 format: `2025-01-01T00:00:00Z`
623
+ 2. **Phone numbers** must include country code: `+254712345678`
624
+ 3. **Passwords** must be 8+ chars, 1 uppercase, 1 digit
625
+ 4. **UUIDs** are strings in API requests/responses
626
+ 5. **Null values** mean field not set (valid state)
627
+ 6. **All PATCH/PUT** endpoints accept partial updates (only send changed fields)
628
+ 7. **Pagination** is 0-indexed for `skip` parameter
629
+ 8. **Soft deletes** - deleted records have `deleted_at` timestamp but data preserved
src/app/config/apps.py CHANGED
@@ -420,9 +420,9 @@ ROLE_APP_ACCESS: Dict[str, List[str]] = {
420
  # Field agents (casual workers) need to see their earnings and daily work
421
  # Include payroll to track earnings from daily errands
422
  # To Add later: "incidents", "documents",
423
- "notifications", "dashboard", "tickets", "inventory", "timesheets", "tasks",
424
  "payroll", # Track their daily earnings
425
- "expenses", "customers", "sales_orders", "maps", # Customer info for field visits
426
  "profile", "help"
427
  ],
428
 
@@ -446,11 +446,11 @@ DEFAULT_FAVORITE_APPS: Dict[str, List[str]] = {
446
  "platform_admin": ["dashboard", "organizations", "users", "invitations", "activity"],
447
  "client_admin": ["dashboard", "projects", "team", "invitations", "activity"],
448
  "contractor_admin": ["dashboard", "projects", "team", "invitations", "activity"],
449
- "sales_manager": ["dashboard", "sales_orders", "customers", "team", "maps"],
450
- "project_manager": ["dashboard", "projects", "tickets", "team", "maps"],
451
- "dispatcher": ["dashboard", "tickets", "sales_orders", "maps", "tasks"],
452
- "field_agent": ["dashboard", "tickets", "maps", "expenses", "customers"],
453
- "sales_agent": ["dashboard", "sales_orders", "customers", "maps"]
454
  }
455
 
456
 
@@ -461,11 +461,11 @@ DEFAULT_FAVORITE_APPS: Dict[str, List[str]] = {
461
 
462
  META_APPS_BY_ROLE: Dict[str, List[str]] = {
463
  # Project-based roles: Only show context-independent apps outside project
464
- "project_manager": ["overview", "projects", "users", "finance", "notifications", "settings", "help"],
465
- "sales_manager": ["overview", "projects", "users", "finance", "notifications", "settings", "help"],
466
- "dispatcher": ["overview", "projects", "users", "finance", "notifications", "settings", "help"],
467
- "field_agent": ["overview", "notifications", "projects", "payroll"],
468
- "sales_agent": ["overview", "notifications", "projects", "payroll"],
469
 
470
  # Admin roles: No context switching - empty list means show all apps always
471
  "platform_admin": [],
 
420
  # Field agents (casual workers) need to see their earnings and daily work
421
  # Include payroll to track earnings from daily errands
422
  # To Add later: "incidents", "documents",
423
+ "notifications", "dashboard", "tickets", "inventory", "timesheets", # "tasks",
424
  "payroll", # Track their daily earnings
425
+ "expenses", # "customers", "sales_orders", "maps", # Customer info for field visits
426
  "profile", "help"
427
  ],
428
 
 
446
  "platform_admin": ["dashboard", "organizations", "users", "invitations", "activity"],
447
  "client_admin": ["dashboard", "projects", "team", "invitations", "activity"],
448
  "contractor_admin": ["dashboard", "projects", "team", "invitations", "activity"],
449
+ "sales_manager": ["dashboard", "sales_orders", "customers", "notifications", "team"],
450
+ "project_manager": ["dashboard", "tickets", "sales_orders", "notifications", "team"],
451
+ "dispatcher": ["dashboard", "tickets", "sales_orders", "team"],
452
+ "field_agent": ["dashboard", "tickets", "notifications", "expenses"],
453
+ "sales_agent": ["dashboard", "sales_orders", "customers", "notifications"]
454
  }
455
 
456
 
 
461
 
462
  META_APPS_BY_ROLE: Dict[str, List[str]] = {
463
  # Project-based roles: Only show context-independent apps outside project
464
+ "project_manager": ["overview", "projects", "users", "finance", "notifications", "settings", "profile", "help"],
465
+ "sales_manager": ["overview", "projects", "users", "finance", "notifications", "settings", "profile", "help"],
466
+ "dispatcher": ["overview", "projects", "users", "finance", "notifications", "settings", "profile", "help"],
467
+ "field_agent": ["overview", "notifications", "projects", "payroll", "profile"],
468
+ "sales_agent": ["overview", "notifications", "projects", "payroll", "profile"],
469
 
470
  # Admin roles: No context switching - empty list means show all apps always
471
  "platform_admin": [],