# Mobile Frontend Implementation Spec

This is the shortest path for frontend to implement the v2 mobile clients against the unified backend.

Base URL:

- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`

Use this doc together with:

- [Authentication](./authentication.md)
- [Unified API](./unified-api.md)
- [Staff Shifts](./staff-shifts.md)

## 1) Global rules

- Use unified routes only.
- Send `Authorization: Bearer <firebase-id-token>` on protected routes.
- Send `Idempotency-Key` on all write routes.
- Do not call `/query/*`, `/commands/*`, or `/core/*` directly from frontend.

## 2) Core model frontend should assume

- `order` is the client-facing request for staffing.
- `shift` is the concrete scheduled unit of work under an order.
- `shiftRole` is the role slot inside a shift that staff apply to.
- `assignment` is the worker-to-shift record once a worker is attached.

Important consequences:

- `GET /staff/shifts/open` returns open shift-role opportunities.
- `POST /staff/shifts/:shiftId/apply` must send the `roleId` from that response.
- `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

### Client app

- sign in with `POST /auth/client/sign-in`
- sign up with `POST /auth/client/sign-up`
- hydrate session with `GET /auth/session`
- sign out with `POST /auth/client/sign-out`

### Staff app

- start phone auth with `POST /auth/staff/phone/start`
- complete phone auth with `POST /auth/staff/phone/verify`
- hydrate session with `GET /auth/session`
- sign out with `POST /auth/staff/sign-out`

Token refresh:

- keep using Firebase client SDK refresh behavior
- there is no backend `/auth/refresh` route

## 4) Client app screen mapping

### Home / dashboard

- `GET /client/session`
- `GET /client/dashboard`
- `GET /client/reorders`

### Billing / payments

- `GET /client/billing/accounts`
- `GET /client/billing/invoices/pending`
- `GET /client/billing/invoices/history`
- `GET /client/billing/current-bill`
- `GET /client/billing/savings`
- `GET /client/billing/spend-breakdown`
- `POST /client/billing/invoices/:invoiceId/approve`
- `POST /client/billing/invoices/:invoiceId/dispute`

### Coverage

- `GET /client/coverage?date=YYYY-MM-DD`
- `GET /client/coverage/stats?date=YYYY-MM-DD`
- `GET /client/coverage/core-team?date=YYYY-MM-DD`
- `GET /client/coverage/incidents?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD`
- `GET /client/coverage/blocked-staff`
- `GET /client/coverage/swap-requests?status=OPEN`
- `GET /client/coverage/dispatch-teams`
- `GET /client/coverage/dispatch-candidates?shiftId=uuid&roleId=uuid`
- `POST /client/coverage/reviews`
- `POST /client/coverage/late-workers/:assignmentId/cancel`
- `POST /client/coverage/swap-requests/:swapRequestId/resolve`
- `POST /client/coverage/swap-requests/:swapRequestId/cancel`
- `POST /client/coverage/dispatch-teams/memberships`
- `DELETE /client/coverage/dispatch-teams/memberships/:membershipId`

Use `POST /client/coverage/reviews` when the business is rating a worker after coverage review.

Payload may include:

```json
{
  "staffId": "uuid",
  "assignmentId": "uuid",
  "rating": 4,
  "feedback": "Strong performance on the shift",
  "markAsFavorite": true,
  "markAsBlocked": false
}
```

If `markAsFavorite` is `true`, backend adds that worker to the business favorites list. If `markAsFavorite` is `false`, backend removes them from that list. If `markAsBlocked` is `true`, backend blocks that worker for that business and rejects future apply or assign attempts until a later review sets `markAsBlocked: false`.

Swap-management rule:

- use `GET /client/coverage/swap-requests` as the client review feed
- use `GET /client/coverage/dispatch-candidates` for the ranked replacement list
- use `POST /client/coverage/swap-requests/:swapRequestId/resolve` when ops selects a replacement
- use `POST /client/coverage/swap-requests/:swapRequestId/cancel` when ops wants to close the swap request without replacement

Dispatch-priority rule:

1. `CORE`
2. `CERTIFIED_LOCATION`
3. `MARKETPLACE`

### Orders

- `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`
- `POST /client/orders/permanent`
- `POST /client/orders/:orderId/edit`
- `POST /client/orders/:orderId/cancel`

Rapid-order flow:

- use `POST /rapid-orders/process` for the single-call transcribe-and-parse flow

### Hubs and managers

- `GET /client/hubs`
- `GET /client/cost-centers`
- `GET /client/hubs/:hubId/managers`
- `GET /client/team-members`
- `POST /client/shift-managers`
- `POST /client/hubs`
- `PUT /client/hubs/:hubId`
- `DELETE /client/hubs/:hubId`
- `POST /client/hubs/:hubId/assign-nfc`
- `POST /client/hubs/:hubId/managers`

`POST /client/shift-managers` is the fastest path to create an invited manager identity for a business. If `hubId` is provided, backend also links that manager to the hub.

### Reports

- `GET /client/reports/summary?date=YYYY-MM-DD`
- `GET /client/reports/daily-ops?date=YYYY-MM-DD`
- `GET /client/reports/spend?date=YYYY-MM-DD`
- `GET /client/reports/coverage?date=YYYY-MM-DD`
- `GET /client/reports/forecast?date=YYYY-MM-DD`
- `GET /client/reports/performance?date=YYYY-MM-DD`
- `GET /client/reports/no-show?date=YYYY-MM-DD`

## 5) Staff app screen mapping

### Home / dashboard

- `GET /staff/session`
- `GET /staff/dashboard`
- `GET /staff/profile-completion`

### Availability

- `GET /staff/availability`
- `PUT /staff/availability`
- `POST /staff/availability/quick-set`

### 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 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

- `GET /staff/shifts/pending`
- `GET /staff/shifts/assigned`
- `GET /staff/shifts/cancelled`
- `GET /staff/shifts/completed`
- `GET /staff/shifts/:shiftId`
- `POST /staff/shifts/:shiftId/accept`
- `POST /staff/shifts/:shiftId/decline`
- `POST /staff/shifts/:shiftId/request-swap`
- `POST /staff/shifts/:shiftId/submit-for-approval`

Current swap behavior:

- backend records the swap request
- assignment moves to `SWAP_REQUESTED`
- shift becomes visible in the replacement pool
- client/ops can review and resolve swap requests through the coverage endpoints
- if the swap request expires without coverage, backend auto-cancels it and alerts both the manager path and the original worker

### Clock in / clock out

- `GET /staff/clock-in/shifts/today`
- `GET /staff/clock-in/status`
- `POST /staff/clock-in`
- `POST /staff/clock-out`
- `POST /staff/location-streams`

Frontend should respect:

- `clockInMode`
- `allowClockInOverride`
- `latitude`
- `longitude`
- `geofenceRadiusMeters`
- `nfcTagId`

Clock-in proof rules:

- use `nfcTagId` for NFC clocking
- use `latitude`, `longitude`, and `accuracyMeters` for geolocation clocking
- send `overrideReason` only when a geofence override is allowed
- send `proofNonce` and `proofTimestamp` on attendance writes

### Payments

- `GET /staff/payments/summary`
- `GET /staff/payments/history`
- `GET /staff/payments/chart`

### Profile

- `GET /staff/profile/sections`
- `GET /staff/profile/personal-info`
- `GET /staff/profile/industries`
- `GET /staff/profile/skills`
- `GET /staff/profile/documents`
- `GET /staff/profile/attire`
- `GET /staff/profile/tax-forms`
- `GET /staff/profile/emergency-contacts`
- `GET /staff/profile/certificates`
- `GET /staff/profile/bank-accounts`
- `GET /staff/profile/benefits`
- `GET /staff/profile/benefits/history`
- `GET /staff/profile/time-card`
- `GET /staff/profile/privacy`
- `PUT /staff/profile/personal-info`
- `PUT /staff/profile/experience`
- `PUT /staff/profile/locations`
- `POST /staff/profile/emergency-contacts`
- `PUT /staff/profile/emergency-contacts/:contactId`
- `PUT /staff/profile/tax-forms/:formType`
- `POST /staff/profile/tax-forms/:formType/submit`
- `POST /staff/profile/bank-accounts`
- `PUT /staff/profile/privacy`

Document model rule:

- `GET /staff/profile/documents` returns only documents
- `GET /staff/profile/attire` returns attire items
- `GET /staff/profile/tax-forms` returns tax-form rows
- `GET /staff/profile/certificates` returns certificates

### FAQ

- `GET /staff/faqs`
- `GET /staff/faqs/search?q=...`

## 6) Upload implementation

For documents, attire, and certificates:

1. `POST /upload-file`
2. `POST /create-signed-url`
3. upload file bytes to storage with the signed URL
4. `POST /verifications`
5. finalize with:
   - `PUT /staff/profile/documents/:documentId/upload`
   - `PUT /staff/profile/attire/:documentId/upload`
   - `POST /staff/profile/certificates`

Use the verification-linked file as the source of truth.

## 7) What frontend should not assume

- do not assume order edit mutates past shifts
- do not assume swap resolution is complete beyond the request step
- do not assume raw `/query/*` or `/commands/*` routes are stable for app integration
- do not assume blocked workers can still apply to future shifts for that business

## 8) Demo reset

To reset dev demo data:

```bash
source ~/.nvm/nvm.sh
nvm use 23.5.0
cd backend/command-api
npm run seed:v2-demo
```
