Skip to content

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.

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>"
}