Spaces:
Sleeping
Field Agent Dashboard API - Implementation Summary
Overview
Enhanced the existing GET /api/v1/analytics/user/overview endpoint to support field agent dashboard requirements without creating new endpoints. This maintains consistency with existing RBAC and reduces maintenance overhead.
What Was Built
Enhanced Endpoint: GET /api/v1/analytics/user/overview
URL: /api/v1/analytics/user/overview?limit=50
Query Parameters:
limit(optional): Max items in work queue (default: 50, max: 100)
Authorization: Any authenticated user (RBAC filters data automatically)
Response Structure
For Field Agents/Sales Agents (Simplified Response)
Field agents receive a personalized response showing only THEIR work, not organization-wide stats:
{
"user_info": {
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"role": "field_agent"
},
"projects": {
"total": 3,
"active": 2
},
"my_tickets": {
"total_assigned": 15,
"pending": 4,
"in_progress": 2,
"completed_this_week": 9
},
"my_expenses": {
"total_amount": 12500.00,
"pending_approval": 3,
"pending_amount": 4500.00
},
"my_inventory": {
"items_on_hand": 8
},
"my_time": {
"hours_worked_this_week": 32.5
},
"notifications": {
"unread": 5
},
"work_queue": {
"pending_assignments": [...],
"total_pending": 4,
"high_priority": 1,
"due_today": 2
},
"generated_at": "2025-11-27T10:30:00Z"
}
Key Differences from Manager Response:
- β
my_tickets- Only tickets assigned to ME (not all project tickets) - β
my_expenses- Only MY expenses (not team expenses) - β
my_inventory- Only items I have (not warehouse inventory) - β
my_time- MY hours worked - β
work_queue- MY pending assignments - β No
teamstats (don't manage team) - β No
sales_orders(not relevant) - β No organization-wide
inventoryvalue
Metrics Explained:
my_tickets.total_assigned: All tickets ever assigned to memy_tickets.pending: Tickets with status "assigned" (not started)my_tickets.in_progress: Tickets with status "en_route" or "in_progress"my_tickets.completed_this_week: Completed this ISO week (Monday-Sunday)my_expenses.total_amount: Sum of all MY expenses (approved + pending)my_expenses.pending_approval: Count of MY unapproved expensesmy_expenses.pending_amount: Sum of MY unapproved expensesmy_inventory.items_on_hand: Equipment I collected but haven't returned/installedmy_time.hours_worked_this_week: Hours from MY timesheets (ISO week)
For Managers/Admins (Full Response)
Managers receive organization-wide stats:
{
"user_info": {...},
"projects": {...},
"team": {
"total_members": 25
},
"tickets": {
"total": 150,
"open": 45,
"in_progress": 30
},
"expenses": {
"total_amount": 250000.00,
"pending_approval": 15
},
"sales_orders": {
"total": 80,
"pending": 20
},
"inventory": {
"total_value": 500000.00,
"active_assignments": 45
},
"notifications": {...},
"generated_at": "..."
}
Work Queue Structure (Field Agents Only)
"work_queue": {
"pending_assignments": [
{
"assignment_id": "uuid",
"ticket_id": "uuid",
"ticket_title": "Install fiber at Westlands Office",
"ticket_number": "TKT-2024-001",
"project_id": "uuid",
"project_title": "Nairobi Fiber Rollout",
"client_name": "Airtel Kenya",
"status": "assigned",
"priority": "high",
"due_date": "2025-11-28T00:00:00Z",
"execution_order": 1,
"assigned_at": "2025-11-27T08:00:00Z"
}
],
"total_pending": 4,
"high_priority": 1,
"due_today": 2
}
Work Queue Details:
- Shows ALL pending assignments across ALL projects
- Sorted by:
execution_order(if set) βassigned_at(oldest first) - Includes assignments with status:
assigned,en_route,in_progress - Limited to max 4 items per field agent (business rule)
- No location data included (not all tickets have locations)
Frontend Implementation Guide
1. Global Dashboard (Root /)
Fetch Overview:
const response = await fetch('/api/v1/analytics/user/overview?limit=50', {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
Display Summary Cards:
// Field agent response has simplified structure
const { my_tickets, my_expenses, my_inventory, my_time } = data;
<Card title="Hours This Week">
{my_time.hours_worked_this_week} hrs
</Card>
<Card title="Pending Expenses">
KES {my_expenses.pending_amount.toLocaleString()}
</Card>
<Card title="Inventory On Hand">
{my_inventory.items_on_hand} items
</Card>
<Card title="Completed This Week">
{my_tickets.completed_this_week} tickets
</Card>
Display Work Queue:
const workQueue = data.work_queue;
<WorkQueue>
<Header>
Pending Work: {workQueue.total_pending}
(High Priority: {workQueue.high_priority}, Due Today: {workQueue.due_today})
</Header>
{workQueue.pending_assignments.map(assignment => (
<WorkItem key={assignment.assignment_id}>
<Badge>{assignment.execution_order || 'β'}</Badge>
<Title>{assignment.ticket_title}</Title>
<Subtitle>{assignment.project_title} β’ {assignment.client_name}</Subtitle>
<Meta>
<Priority>{assignment.priority}</Priority>
<DueDate>{assignment.due_date}</DueDate>
</Meta>
</WorkItem>
))}
</WorkQueue>
2. Project List
Use Existing Endpoint:
const response = await fetch('/api/v1/projects', {
headers: { 'Authorization': `Bearer ${token}` }
});
const projects = await response.json();
No changes needed - RBAC automatically filters to show only projects the field agent is assigned to via ProjectTeam.
3. Project Dashboard
Use Existing Endpoint:
const response = await fetch(`/api/v1/projects/${projectId}/dashboard`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const dashboard = await response.json();
No changes needed - Already returns project-scoped stats with proper authorization.
Technical Details
Database Queries
Field Agent Stats:
- Timesheet:
SUM(hours_worked) WHERE user_id = current_user AND work_date >= week_start - Expenses:
SUM(total_cost) WHERE user_id = current_user AND is_approved = false - Inventory:
COUNT(*) WHERE user_id = current_user AND is_returned = false AND installed_at IS NULL - Tickets:
COUNT(*) WHERE user_id = current_user AND status = 'completed' AND ended_at >= week_start
Work Queue:
SELECT ta.*, t.*, p.*, c.*
FROM ticket_assignments ta
JOIN tickets t ON ta.ticket_id = t.id
JOIN projects p ON t.project_id = p.id
JOIN clients c ON p.client_id = c.id
WHERE ta.user_id = :user_id
AND ta.status IN ('assigned', 'en_route', 'in_progress')
AND ta.ended_at IS NULL
AND ta.deleted_at IS NULL
ORDER BY ta.execution_order ASC NULLS LAST, ta.assigned_at ASC
LIMIT :limit
Performance Considerations
- Caching: Not cached (real-time data needed for work queue)
- Query Optimization: Uses joins with
joinedloadto avoid N+1 queries - Limit: Max 100 items in work queue (business rule: max 4 active assignments)
- ISO Week: Monday = start of week (Python's
date.weekday())
Authorization
- RBAC: Existing role-based access control applies
- Data Filtering: Queries automatically filter by
user_id = current_user.id - Project Access: Field agents only see projects they're assigned to via
ProjectTeam
Testing
Manual Test (cURL)
# Login as field agent
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "agent@example.com", "password": "password"}'
# Get overview
curl http://localhost:8000/api/v1/analytics/user/overview?limit=50 \
-H "Authorization: Bearer YOUR_TOKEN"
Expected Response
Field agents should see:
field_agent_statsobject with 4 metricswork_queueobject with pending assignments- All other standard fields
Non-field-agent roles should NOT see:
field_agent_stats(null/undefined)work_queue(null/undefined)
Migration Notes
No Breaking Changes
- Existing endpoints unchanged
- New fields only added for field_agent/sales_agent roles
- Other roles receive same response as before
Frontend Migration Path
- Update API client to handle new optional fields
- Create field agent dashboard components
- Reuse existing project list/dashboard components
- Test with field agent user account
Questions?
Contact backend team or check:
- Service:
src/app/services/dashboard_service.py - Endpoint:
src/app/api/v1/analytics.py - Models:
src/app/models/ticket_assignment.py,src/app/models/timesheet.py