feat(api): add staff order booking contract and shift timeline alias

This commit is contained in:
zouantchaw
2026-03-19 16:07:25 +01:00
parent 4b2ef9d843
commit 1d5c0e3b80
16 changed files with 766 additions and 19 deletions

View File

@@ -7,7 +7,7 @@ This is the frontend-facing source of truth for the v2 backend.
Frontend should call one public gateway:
```env
API_V2_BASE_URL=https://krow-api-v2-933560802882.us-central1.run.app
API_V2_BASE_URL=https://krow-api-v2-e3g6witsvq-uc.a.run.app
```
Frontend should not call the internal `core`, `command`, or `query` Cloud Run services directly.
@@ -95,14 +95,12 @@ Source-of-truth timestamp fields include:
- `startsAt`
- `endsAt`
- `startTime`
- `endTime`
- `clockInAt`
- `clockOutAt`
- `createdAt`
- `updatedAt`
Helper fields like `date` are UTC-derived helpers and should not replace the raw timestamp fields.
Helper fields like `date`, `startTime`, and `endTime` are display helpers and should not replace the raw timestamp fields.
## 4) Attendance policy and monitoring

View File

@@ -5,7 +5,7 @@ This document is the source of truth for V2 authentication.
Base URL:
```env
API_V2_BASE_URL=https://krow-api-v2-933560802882.us-central1.run.app
API_V2_BASE_URL=https://krow-api-v2-e3g6witsvq-uc.a.run.app
```
## 1) What is implemented

View File

@@ -22,7 +22,7 @@ That includes:
The live smoke executed successfully against:
- `https://krow-api-v2-933560802882.us-central1.run.app`
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
- Firebase demo users
- `krow-sql-v2`
- `krow-core-api-v2`

View File

@@ -6,7 +6,7 @@ Use this as the primary implementation brief.
Base URL:
- `https://krow-api-v2-933560802882.us-central1.run.app`
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
Supporting docs:
@@ -23,6 +23,7 @@ Supporting docs:
- Send `Idempotency-Key` on every write route.
- Treat `order`, `shift`, `shiftRole`, and `assignment` as different objects.
- For staff shift applications, `roleId` must come from the response of `GET /staff/shifts/open`.
- For staff order booking, `roleId` must come from the response of `GET /staff/orders/available`.
- Treat API timestamp fields as UTC and convert them to local time in the app.
## 2) What is implemented now
@@ -90,8 +91,10 @@ Do not assume staff auth is a fully backend-managed OTP flow.
Rules:
- `GET /staff/shifts/open` returns opportunities, not assignments
- `GET /staff/orders/available` returns grouped order opportunities for booking
- `GET /staff/shifts/assigned` returns active assigned shifts
- `GET /client/orders/view` is the timeline/read model for client
- `GET /client/shifts/scheduled` is the canonical timeline/read model for client
- `GET /client/orders/view` is now a deprecated compatibility alias
- `POST /client/orders/:orderId/edit` and `POST /client/orders/:orderId/cancel` apply to future shifts only
## 5) Client app screen mapping
@@ -165,7 +168,8 @@ Swap management flow:
### Orders
- `GET /client/orders/view`
- `GET /client/shifts/scheduled`
- `GET /client/orders/view` deprecated alias
- `GET /client/orders/:orderId/reorder-preview`
- `POST /client/orders/one-time`
- `POST /client/orders/recurring`
@@ -230,12 +234,17 @@ Important:
### Find shifts
- `GET /staff/orders/available`
- `POST /staff/orders/:orderId/book`
- `GET /staff/shifts/open`
- `POST /staff/shifts/:shiftId/apply`
Rule:
- use `roleId` from the open-shifts response
- use `roleId` from the order-available response when booking an order
- that `roleId` is the role catalog id for the grouped order booking flow
- use `roleId` from the open-shifts response only for shift-level apply
- that `roleId` is the concrete `shift_roles.id`
### My shifts

View File

@@ -4,7 +4,7 @@ This is the shortest path for frontend to implement the v2 mobile clients agains
Base URL:
- `https://krow-api-v2-933560802882.us-central1.run.app`
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
Use this doc together with:
@@ -30,7 +30,10 @@ Important consequences:
- `GET /staff/shifts/open` returns open shift-role opportunities.
- `POST /staff/shifts/:shiftId/apply` must send the `roleId` from that response.
- `GET /client/orders/view` is the timeline/read model for the client app.
- `GET /staff/orders/available` returns grouped order opportunities for atomic booking.
- `POST /staff/orders/:orderId/book` must send the `roleId` from that response.
- `GET /client/shifts/scheduled` is the canonical timeline/read model for the client app.
- `GET /client/orders/view` is a deprecated compatibility alias.
- `POST /client/orders/:orderId/edit` and `POST /client/orders/:orderId/cancel` only affect future shifts.
## 3) Auth implementation
@@ -122,7 +125,8 @@ Dispatch-priority rule:
### Orders
- `GET /client/orders/view`
- `GET /client/shifts/scheduled`
- `GET /client/orders/view` deprecated alias
- `GET /client/orders/:orderId/reorder-preview`
- `POST /client/orders/one-time`
- `POST /client/orders/recurring`
@@ -175,13 +179,17 @@ Rapid-order flow:
### Find shifts
- `GET /staff/orders/available`
- `POST /staff/orders/:orderId/book`
- `GET /staff/shifts/open`
- `POST /staff/shifts/:shiftId/apply`
Rule:
- send the `roleId` from the open-shifts response
- this is the concrete `shift_roles.id`
- send the `roleId` from the order-available response when booking an order
- this `roleId` is the role catalog id for grouped order booking
- send the `roleId` from the open-shifts response only when applying to one shift
- that route still uses the concrete `shift_roles.id`
### My shifts

View File

@@ -4,10 +4,11 @@ This document is the frontend handoff for the `staff/shifts/*` routes on the uni
Base URL:
- `https://krow-api-v2-933560802882.us-central1.run.app`
- `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`
@@ -17,6 +18,7 @@ Base URL:
## Write routes
- `POST /staff/orders/:orderId/book`
- `POST /staff/shifts/:shiftId/apply`
- `POST /staff/shifts/:shiftId/accept`
- `POST /staff/shifts/:shiftId/decline`
@@ -30,6 +32,68 @@ All write routes require:
## 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`

View File

@@ -2,7 +2,7 @@
Frontend should use this service as the single base URL:
- `https://krow-api-v2-933560802882.us-central1.run.app`
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
The gateway keeps backend services separate internally, but frontend should treat it as one API.
@@ -54,7 +54,8 @@ Full auth behavior, including staff phone flow and refresh rules, is documented
- `GET /client/vendors/:vendorId/roles`
- `GET /client/hubs/:hubId/managers`
- `GET /client/team-members`
- `GET /client/orders/view`
- `GET /client/shifts/scheduled`
- `GET /client/orders/view` deprecated compatibility alias
- `GET /client/orders/:orderId/reorder-preview`
- `GET /client/reports/summary`
- `GET /client/reports/daily-ops`
@@ -88,6 +89,12 @@ Full auth behavior, including staff phone flow and refresh rules, is documented
- `POST /client/coverage/dispatch-teams/memberships`
- `DELETE /client/coverage/dispatch-teams/memberships/:membershipId`
Timeline route naming:
- `GET /client/shifts/scheduled` is the canonical client timeline route
- it returns shift-level scheduled items, not order headers
- `GET /client/orders/view` still returns the same payload for compatibility, but now emits a deprecation header
Coverage-review request payload may also send:
```json
@@ -176,6 +183,7 @@ The manager is created as an invited business membership. If `hubId` is present,
- `GET /staff/payments/summary`
- `GET /staff/payments/history`
- `GET /staff/payments/chart`
- `GET /staff/orders/available`
- `GET /staff/shifts/assigned`
- `GET /staff/shifts/open`
- `GET /staff/shifts/pending`
@@ -239,6 +247,14 @@ Example `GET /staff/profile/stats` response:
}
```
Order booking route notes:
- `GET /staff/orders/available` is the canonical order-level marketplace feed for recurring and grouped work
- `GET /staff/shifts/open` remains available for shift-level opportunities and swap coverage
- `POST /staff/orders/:orderId/book` books the future shifts of an order atomically for one role
- the `roleId` returned by `GET /staff/orders/available` is the role catalog id for the order booking flow
- the `roleId` returned by `GET /staff/shifts/open` is still the concrete `shift_roles.id` for shift-level apply
### Staff writes
- `POST /staff/profile/setup`
@@ -249,6 +265,7 @@ Example `GET /staff/profile/stats` response:
- `POST /staff/location-streams`
- `PUT /staff/availability`
- `POST /staff/availability/quick-set`
- `POST /staff/orders/:orderId/book`
- `POST /staff/shifts/:shiftId/apply`
- `POST /staff/shifts/:shiftId/accept`
- `POST /staff/shifts/:shiftId/decline`