feat(metrics): per-staff performance view in dashboard

GET /v1/metrics/staff returns bookings, completed, cancelled,
utilization, and revenue per staff member over a trailing window
(default 30d, max 365d). Appointment bookings only — class sessions
have no staff assignment. All active staff appear, including
zero-booking rows.

Staff page gains a localized Performance panel; demo mode seeds
trailing-window bookings and matching metric rows.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 16:34:45 +02:00
1 parent 4674203502
commit 76e45d4178
11 files changed
+496 -3

No files matched your search

+64
View File
@@ -1431,6 +1431,34 @@ paths:
$ref: "#/components/schemas/ActivityFeedResponse"
"404":
description: Tenant membership not found
/v1/metrics/staff:
get:
tags: [Metrics]
operationId: getStaffMetrics
security:
- bearerAuth: []
summary: Per-staff appointment metrics over a trailing window
description: >-
Bookings, completions, cancellations, utilization, and revenue per
staff member. Only appointment bookings carry a staff assignment —
class attendance is excluded by design. Every active staff member
appears, including zero rows.
parameters:
- name: days
in: query
schema:
type: integer
default: 30
maximum: 365
responses:
"200":
description: Per-staff metrics
content:
application/json:
schema:
$ref: "#/components/schemas/StaffMetricsResponse"
"404":
description: Tenant membership not found
/v1/export/bookings.csv:
get:
tags: [Export]
@@ -2158,6 +2186,42 @@ components:
type: array
items:
$ref: "#/components/schemas/ActivityEvent"
StaffMetric:
type: object
required: [staffId, displayName, bookings, completed, cancelled, utilizationPercent, revenueCents]
properties:
staffId:
type: string
format: uuid
displayName:
type: string
bookings:
type: integer
completed:
type: integer
cancelled:
type: integer
utilizationPercent:
type: integer
description: Share of bookings that stayed confirmed, 0-100
revenueCents:
type: integer
format: int64
description: Sum of linked service prices for non-cancelled bookings
StaffMetricsResponse:
type: object
required: [from, to, staff]
properties:
from:
type: string
format: date-time
to:
type: string
format: date-time
staff:
type: array
items:
$ref: "#/components/schemas/StaffMetric"
DashboardBookingRequest:
type: object
required: [customerName, customerEmail, startsAt]