Dashboard¶
The dashboard gives every user an at-a-glance view of operational health — metrics, alerts, and recent activity — scoped to what their role allows them to see.
Accessing the Dashboard¶
Endpoint: GET /{slug}/api/v1/dashboard/
All authenticated users can access the dashboard. The data returned is filtered by role.
In-App Documentation Link¶
When you are signed in, use the Docs link in the main app navigation to open /docs/ directly. You do not need to sign out or return to the public landing page to access documentation.
Response Structure¶
{
"metrics": {
"bids": { "new_bid": 3, "awaiting_po": 2, "approved": 10, "rejected": 1, "cancelled": 0 },
"jobs": { "ready": 5, "in_progress": 4, "needs_invoice": 2, "cancelled": 1 },
"invoices": { "invoice_submitted": 3, "partially_paid": 1, "paid": 8, "workflow_complete": 2 }
},
"alerts": {
"priority_bids": 1,
"blocked_bids": 0,
"priority_jobs": 2,
"blocked_jobs": 1,
"stale_jobs": 3
}
}
Role-Scoped Metrics¶
| Section | Roles that see it |
|---|---|
metrics.bids |
Owner, PM, Accounting, Platform Admin |
metrics.invoices |
Owner, Accounting, Platform Admin |
metrics.jobs |
All roles (Supervisor and Badge Worker see only their own jobs) |
alerts.priority_bids / alerts.blocked_bids |
Owner, PM, Accounting, Platform Admin |
alerts.priority_jobs / alerts.blocked_jobs / alerts.stale_jobs |
All roles (scoped) |
Alerts¶
Stale Jobs¶
A job is stale when it has been in Ready to Start status for longer than the tenant's configured threshold. The threshold is set in Company Settings (stale_job_threshold_days, default 14 days).
Configuring the stale threshold
Owners can adjust the stale job threshold in Company Settings to match their operational cadence.
Priority and Blocked¶
- Priority bids/jobs — flagged by PMs or Owners as high priority.
- Blocked bids/jobs — have an active blocker requiring resolution before work can proceed.
Activity Feed¶
The dashboard includes a role-scoped activity feed that shows the 15 most recent events across ActivityLog entries, user-posted updates, and photo uploads.
Endpoint: GET /{slug}/api/v1/dashboard/feed/
Event Shape¶
Every event is returned in a normalized shape regardless of source:
{
"id": "<uuid>",
"occurred_at": "<ISO 8601>",
"actor": { "id": "<uuid>", "name": "Jane Smith", "avatar_url": "" },
"entity_type": "job",
"entity_id": "<uuid>",
"action": "status_change",
"summary": "In Progress → Completed / Needs Invoice",
"link": "/acme/jobs/<uuid>"
}
actor is null for system-generated events. action is one of the ActionType values, "update_posted" for manual notes, or "photo_uploaded" for photo uploads.
summary is built from the event metadata — for example, status changes show the previous and new status (Ready to Start → In Progress), invites include the email address, and field updates list what changed.
Role Scoping¶
| Role | Feed scope |
|---|---|
| Owner, Platform Admin | All events |
| Project Manager, Accounting | All events |
| Supervisor | Jobs where they are the assigned supervisor |
| Badge Worker | Jobs they are assigned to via JobWorkerAssignment |
Sensitive action types (login, role_change, admin_action, tenant_provisioned) are hidden from all roles except Owner and Platform Admin.
Pagination¶
The feed uses cursor-based pagination with a default page size of 15. Pass the cursor query parameter (returned as next in the response) to fetch older events:
GET /{slug}/api/v1/dashboard/feed/?cursor=<opaque-cursor>
Response shape:
{
"results": [ ... ],
"next": "<cursor-string or null>"
}