# Staff Court Monitor (Mobile) — Per-Screen Spec

> Acceptance detail per screen (Given/When/Then where useful). Behaviour is shared with the Senior Court Monitor and Cleaning Supervisor packs (same flow; different auto-assigned templates). RBAC: Monitor sees only **own roster / own assigned checklists / own Game Zone** — enforced server-side, mirrored in the app.
> References: `FOUNDATION_SPEC.md` (schema, conventions, auto-assign, cascade), `DESIGN_SYSTEM.md` (§2.4 state colors, §6.12 PhotoUpload, §6.13 ChecklistItem, §6.14 ApprovalTimeline, §8.2 mobile shell), `PROJECT_PLAN §4a/§4b/§9`.

---

## Global rules (apply to every screen)

- **Chrome:** navy 56px top app bar (white title + bell + avatar); a **Game-Zone scope strip** below it in light-blue wash reading `Game Zone · Air Maniax Ahmedabad-1` (read-only — Monitor is locked to one Game Zone). 56px bottom nav, active slot orange. Orange FAB (camera) on fill screens only.
- **RBAC:** nav + permissions come from `GET /me` (`FOUNDATION_SPEC §2`). The app never hardcodes role logic. No Approve/Verify affordance exists anywhere (Monitor cannot approve, `§3`).
- **Game-Zone scope:** every list is server-filtered to the Monitor's Game Zone + own assignments. An out-of-scope id → 403 (`states/forbidden.html`).
- **Offline (L10):** any network failure shows the "No connection — retry" state; the **entered draft (G/A picks, notes, photos staged) is held in memory** and the action re-POSTs on retry. No data is silently lost on a momentary drop.
- **Status semantics:** `Pending` is what higher roles see; the Monitor always sees the **real sub-state** of their own instance (Submitted / TL Approved / … / Sent back) — derived by the shared `renderStatusForViewer()` (`FOUNDATION_SPEC §4`).

---

## `AUTH-LOGIN` — Login (`screens/01-login.html`)

- **Given** the app is launched logged-out, **When** Ramesh enters email/phone + password and taps Sign In, **Then** the app calls `POST /auth/login`, stores the access+refresh tokens, calls `GET /me`, and routes to `MON-DASH` (role resolved = MON).
- **Given** bad credentials, **Then** show an inline `danger` error ("Incorrect email or password"); password field reveals on the eye icon.
- **Given** too many failed attempts, **Then** show the **locked** state (`states/locked.html`) — countdown + "Too many attempts".
- Primary button = **navy-on-orange** "Sign In" (52px mobile). Maniax wordmark lockup (AIR in orange / MANIAX in navy) above the form. "Forgot password?" link in `brand-blue`.

## `MON-DASH` — Monitor Home (`screens/02-dashboard.html`)

- **Purpose:** at-a-glance — today's shift + the count of assigned checklists by status, due-soon and overdue badges.
- **Given** Ramesh is on Trampoline · Morning, **Then** the Home shows a shift card ("Trampoline · Morning · 10:00–16:00") + KPI tiles (e.g. "Assigned 2 · In progress 0 · Done 0") + a list of today's checklists with `StatusBadge` chips, tappable into `MON-CHK-TODAY`/`MON-CHK-FILL`.
- **Given** a checklist is past `dueAt` and unsubmitted, **Then** its row shows a **solid-red Overdue** badge.
- **Given** nothing is assigned yet (roster not published), **Then** show the nothing-assigned empty state ("No checklists assigned for your shift yet", `states/empty.html`).
- Loading = skeleton list (`states/loading.html`), never a bare spinner.

## `MON-CHK-TODAY` — Today's Checklists (`screens/03-today-checklists.html`)

- **Purpose:** the full list of this shift's auto-assigned checklists with status chips; the entry point to fill.
- **Given** the roster was published, **Then** the list shows each `ChecklistInstance` for Ramesh's roster entry (e.g. **Trampoline Daily**, **Trampoline Opening** if he is the opener) with: template name, frequency tag, item-count, a `StatusBadge` (Not started / In progress / Submitted / Sent back / Overdue), and a "📷 needed" hint where photos are still required.
- **When** Ramesh taps a Not-started or In-progress row, **Then** open `MON-CHK-FILL` for that instance (state → `IN_PROGRESS` on first response).
- **When** he taps a Submitted/approved row, **Then** open `MON-CHK-STATUS` (read-only cascade view).
- A **Sent back** row is loud (red) with a "Re-fill" affordance → reopens `MON-CHK-FILL`.
- **Ride changed mid-shift (CL-4):** **Given** the Team Leader reassigns Ramesh to a new ride mid-shift (e.g. Trampoline → ZipZag), **When** Ramesh is on Today's Checklists, **Then** the screen receives the change **live via FCM** (mobile is **online-only**) and shows a dismissible **"Ride changed — your checklists were updated"** banner (brand-light-blue wash + orange accent); the list refreshes to the **new ride's auto-assigned checklists**, and his old ride's checklists move to whoever now holds it. State: `states/ride-changed.html`. The banner is informational (no approve affordance) and dismisses on tap.

## `MON-CHK-FILL` — Fill Checklist (`screens/04-checklist-fill.html`) — CRITICAL

- **Purpose:** the heart of the role — set **G/A** per item + note + time + initials; attach the mandatory **completion photo**; attach a **per-A-item issue photo**.
- **Layout:** sectioned list of `ChecklistItem` rows (`DESIGN_SYSTEM §6.13`). Each row: item text → large **segmented G | A toggle** (44px; G green when selected, A red-orange when selected; neutral until chosen) → auto-stamped **time** (editable) + **initials** → conditional **note** + **A-photo tile** that appears and becomes **required** when A is chosen. A sticky **completion-photo** section + a **Review & Submit** bar at the bottom (thumb zone).
- **G/A toggle behaviour:**
  - **Given** an item is untouched, **Then** it renders neutral and prompts action; the instance cannot be submitted with any untouched item.
  - **When** Ramesh taps **G**, **Then** the row gets a green left rail, `value=G`, time auto-stamped, note optional; `POST/PATCH …/responses` saves the response (draft, `IN_PROGRESS`).
  - **When** Ramesh taps **A**, **Then** the **A-note+photo bottom sheet** opens (`bottom-sheets/a-item-note-photo.html`); the row gets a red-orange left rail + a **photo-required flag**; the item is **blocked** from final submit until a `Photo{kind:NEGATIVE, itemId}` exists.
- **Completion photo:** the `PhotoUpload` completion tile (≥96px) opens the **photo-capture sheet** (Camera / Gallery, `bottom-sheets/photo-capture.html`); ≥1 completion photo is required before submit.
- **Save-draft:** responses persist as entered (`IN_PROGRESS`); leaving and returning restores them. Offline → in-memory draft + retry (L10).
- **Photo storage:** each photo is a multipart `POST /uploads/photos` → local disk → `Photo` row with uploader/timestamp/device/kind (`FOUNDATION_SPEC §1`, §4a).
- **Concurrency:** response saves carry `If-Match: <instance.version>` → 409 if stale (rare for a single filler, but mirrored from the contract).

## `MON-CHK-SUBMIT` — Submit Checklist (`screens/05-checklist-submit.html`)

- **Purpose:** final review + submit; the **photo gate** is enforced here (and server-side).
- **Given** Ramesh taps Review & Submit, **Then** show a summary: count of G vs A items, the completion photo thumbnail(s), and any A items with their issue-photo thumbnails.
- **Submit gate (§4a, L7):**
  - **Given** any item is untouched, **Then** submit is blocked with an inline `danger` message listing the untouched items.
  - **Given** no completion photo, **Then** submit is blocked ("Add a completion photo to submit").
  - **Given** any A item lacks its issue photo, **Then** submit is blocked ("Item X needs a photo of the issue") and the row is highlighted.
  - **Given** all gates pass **and** Ramesh confirms in the **confirm-submit sheet** (`bottom-sheets/confirm-submit.html`), **Then** `POST …/submit` → instance state `SUBMITTED`, `submittedAt` set, `ApprovalStep` rows created, **TL (Priya) notified** (FCM + in-app), and the app routes to `MON-CHK-STATUS` showing "Submitted · awaiting TL".
- The server independently re-checks the gate and returns **422** with the same `fields` shape if anything is missing — the UI never bypasses it.

## `MON-CHK-STATUS` — My Checklist Status (`screens/06-my-status.html`)

- **Purpose:** live cascade status of the Monitor's own submissions; the **send-back** re-fill entry.
- **Given** a submitted instance, **Then** render the **ApprovalTimeline** (`DESIGN_SYSTEM §6.14`) as a vertical stepper: `Filled by Ramesh (Monitor) ✓ → TL → Store Manager → Operation Head (Done)`, each node showing actor + role + timestamp + a `StatusBadge`; the current pending node carries a ring.
- The Monitor sees the **real** sub-state (Submitted / TL Approved / SM Approved / Done) — not "Pending" (that is the higher roles' view, L8).
- **Given** an instance was **Sent back** (§9 #3), **Then** show a prominent **red banner** with the reject reason + a **"Re-fill"** CTA → reopens `MON-CHK-FILL` (state `IN_PROGRESS`). Send-back always returns to **Ramesh** (the original filler), regardless of which approver rejected.
- Empty state when nothing submitted yet.

## `MON-ROSTER-OWN` — My Roster (`screens/08-my-roster.html`)

- **Purpose:** Ramesh's own shifts — **read-only** (Monitor cannot edit rosters, `§3`).
- **Given** the day/week toggle, **Then** show own `RosterEntry` rows (ride + shift + date), shift blocks colored by template (Morning light-blue / Evening blue wash, `DESIGN_SYSTEM §6.19`). Leave days overlaid.
- No create/edit affordance anywhere. Empty state when no shifts.

## `SH-PROFILE` (More tab) — Profile (`screens/07-profile.html`)

- **Purpose:** the More-tab landing — own profile, role, reports-to, certified rides; logout.
- **Given** the profile, **Then** show name, role ("Staff Court Monitor"), Game Zone, reports-to ("Priya Nair · Team Leader"), and certified rides ("Trampoline"). Logout confirms then clears tokens and returns to `AUTH-LOGIN`.
- Alerts (notification center) is reachable from here and the top-bar bell.

---

## Acceptance summary (the slot's must-pass behaviours)
- [ ] Login → `/me` role-resolve → Monitor Home; no nav/permission is hardcoded.
- [ ] Today's list shows exactly the auto-assigned checklists for Ramesh's published roster entry (own GZ, own assignments).
- [ ] G/A toggle: G = green, A = red-orange; A opens the note+photo sheet and flags photo-required.
- [ ] Submit is blocked (UI **and** server 422) unless every item is answered, ≥1 completion photo exists, and every A item has its issue photo.
- [ ] On submit: state → Submitted, TL notified, status timeline shows "awaiting TL".
- [ ] Sent-back returns to Ramesh with a red banner + reason + Re-fill.
- [ ] Monitor sees the real sub-state of own instances (never "Pending"); roster is read-only.
- [ ] Offline retry preserves the in-memory draft; out-of-scope access → 403.
