248 lines
5.1 KiB
Markdown
248 lines
5.1 KiB
Markdown
# Staff Shifts V2
|
|
|
|
This document is the frontend handoff for the `staff/shifts/*` routes on the unified v2 API.
|
|
|
|
Base URL:
|
|
|
|
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
|
|
|
|
## Read routes
|
|
|
|
- `GET /staff/orders/available`
|
|
- `GET /staff/shifts/assigned`
|
|
- `GET /staff/shifts/open`
|
|
- `GET /staff/shifts/pending`
|
|
- `GET /staff/shifts/cancelled`
|
|
- `GET /staff/shifts/completed`
|
|
- `GET /staff/shifts/:shiftId`
|
|
|
|
## Write routes
|
|
|
|
- `POST /staff/orders/:orderId/book`
|
|
- `POST /staff/shifts/:shiftId/apply`
|
|
- `POST /staff/shifts/:shiftId/accept`
|
|
- `POST /staff/shifts/:shiftId/decline`
|
|
- `POST /staff/shifts/:shiftId/request-swap`
|
|
- `POST /staff/shifts/:shiftId/submit-for-approval`
|
|
|
|
All write routes require:
|
|
|
|
- `Authorization: Bearer <firebase-id-token>`
|
|
- `Idempotency-Key: <unique-per-action>`
|
|
|
|
## Shift lifecycle
|
|
|
|
### Find work by order
|
|
|
|
`GET /staff/orders/available`
|
|
|
|
- use this for grouped recurring or permanent work cards
|
|
- each item represents one order plus one role
|
|
- this feed is already filtered to the current worker context
|
|
- `schedule` gives the preview for the whole booking window
|
|
|
|
Example response:
|
|
|
|
```json
|
|
{
|
|
"orderId": "uuid",
|
|
"orderType": "RECURRING",
|
|
"roleId": "uuid",
|
|
"roleCode": "BARISTA",
|
|
"roleName": "Barista",
|
|
"clientName": "Google Mountain View Cafes",
|
|
"location": "Google MV Cafe Clock Point",
|
|
"locationAddress": "1600 Amphitheatre Pkwy, Mountain View, CA",
|
|
"hourlyRateCents": 2300,
|
|
"hourlyRate": 23,
|
|
"requiredWorkerCount": 1,
|
|
"filledCount": 0,
|
|
"instantBook": false,
|
|
"dispatchTeam": "CORE",
|
|
"dispatchPriority": 1,
|
|
"schedule": {
|
|
"totalShifts": 3,
|
|
"startDate": "2026-03-24",
|
|
"endDate": "2026-03-28",
|
|
"daysOfWeek": ["WED", "FRI"],
|
|
"startTime": "09:00",
|
|
"endTime": "15:00",
|
|
"timezone": "America/Los_Angeles",
|
|
"firstShiftStartsAt": "2026-03-25T16:00:00.000Z",
|
|
"lastShiftEndsAt": "2026-03-27T22:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
`POST /staff/orders/:orderId/book`
|
|
|
|
- use this when the worker books the full order instead of one shift
|
|
- booking is atomic across the future shifts in that order for the selected role
|
|
- backend returns `PENDING` when the booking is reserved but not instant-booked
|
|
- backend returns `CONFIRMED` when every future shift in that booking path is instant-booked
|
|
|
|
Example request:
|
|
|
|
```json
|
|
{
|
|
"roleId": "uuid"
|
|
}
|
|
```
|
|
|
|
Important:
|
|
|
|
- `roleId` for the order-booking flow is the role catalog id returned by `GET /staff/orders/available`
|
|
- it is not the same thing as the per-shift `shift_roles.id`
|
|
|
|
### Find shifts
|
|
|
|
`GET /staff/shifts/open`
|
|
|
|
- use this for the worker marketplace feed
|
|
- the worker applies to a concrete shift role
|
|
- send the `roleId` returned by the open-shifts response
|
|
- `roleId` here means `shift_roles.id`, not the role catalog id
|
|
|
|
Apply request example:
|
|
|
|
```json
|
|
{
|
|
"roleId": "uuid",
|
|
"instantBook": false
|
|
}
|
|
```
|
|
|
|
### Pending shifts
|
|
|
|
`GET /staff/shifts/pending`
|
|
|
|
- use `POST /staff/shifts/:shiftId/accept` to accept
|
|
- use `POST /staff/shifts/:shiftId/decline` to decline
|
|
|
|
### Assigned shifts
|
|
|
|
`GET /staff/shifts/assigned`
|
|
|
|
Each item now includes:
|
|
|
|
- `clientName`
|
|
- `hourlyRate`
|
|
- `totalRate`
|
|
- `startTime`
|
|
- `endTime`
|
|
|
|
### Shift detail
|
|
|
|
`GET /staff/shifts/:shiftId`
|
|
|
|
Each detail response now includes:
|
|
|
|
- `clientName`
|
|
- `latitude`
|
|
- `longitude`
|
|
- `hourlyRate`
|
|
- `totalRate`
|
|
|
|
Use this as the source of truth for the shift detail screen.
|
|
|
|
### Request swap
|
|
|
|
`POST /staff/shifts/:shiftId/request-swap`
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"reason": "Need coverage for a family emergency"
|
|
}
|
|
```
|
|
|
|
Current backend behavior:
|
|
|
|
- marks the assignment as `SWAP_REQUESTED`
|
|
- stores the reason
|
|
- emits `SHIFT_SWAP_REQUESTED`
|
|
- exposes the shift in the replacement pool
|
|
- starts the swap-expiry window used by backend auto-cancellation
|
|
|
|
Manager/ops review happens through:
|
|
|
|
- `GET /client/coverage/swap-requests`
|
|
- `GET /client/coverage/dispatch-candidates`
|
|
- `POST /client/coverage/swap-requests/:swapRequestId/resolve`
|
|
- `POST /client/coverage/swap-requests/:swapRequestId/cancel`
|
|
|
|
If the swap request expires without coverage, backend auto-cancels it and alerts the manager path plus the original worker.
|
|
|
|
### Submit completed shift for approval
|
|
|
|
`POST /staff/shifts/:shiftId/submit-for-approval`
|
|
|
|
Use this after the worker has clocked out.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"note": "Worked full shift and all tasks were completed"
|
|
}
|
|
```
|
|
|
|
Current backend behavior:
|
|
|
|
- only allows shifts in `CHECKED_OUT` or `COMPLETED`
|
|
- creates or updates the assignment timesheet
|
|
- sets the timesheet to `SUBMITTED` unless it is already `APPROVED` or `PAID`
|
|
- emits `TIMESHEET_SUBMITTED_FOR_APPROVAL`
|
|
|
|
Example response:
|
|
|
|
```json
|
|
{
|
|
"assignmentId": "uuid",
|
|
"shiftId": "uuid",
|
|
"timesheetId": "uuid",
|
|
"status": "SUBMITTED",
|
|
"submitted": true
|
|
}
|
|
```
|
|
|
|
## Completed shifts
|
|
|
|
`GET /staff/shifts/completed`
|
|
|
|
Each item now includes:
|
|
|
|
- `date`
|
|
- `clientName`
|
|
- `startTime`
|
|
- `endTime`
|
|
- `hourlyRate`
|
|
- `totalRate`
|
|
- `timesheetStatus`
|
|
- `paymentStatus`
|
|
|
|
## Clock-in support fields
|
|
|
|
`GET /staff/clock-in/shifts/today`
|
|
|
|
Each item now includes:
|
|
|
|
- `clientName`
|
|
- `hourlyRate`
|
|
- `totalRate`
|
|
- `latitude`
|
|
- `longitude`
|
|
- `clockInMode`
|
|
- `allowClockInOverride`
|
|
|
|
## Frontend rule
|
|
|
|
Use the unified routes only.
|
|
|
|
Do not build new mobile work on:
|
|
|
|
- `/query/*`
|
|
- `/commands/*`
|
|
- `/core/*`
|