# Staff Court Monitor (Mobile) — Developer Task Breakdown

> Source-of-truth task list for the anchor slot (dev-mode v1.0 slot 5). One row per screen + the shared dependencies. Surface = Flutter mobile portrait.
> **Team for this slot:** Mobile (Flutter) FE + Backend (shared with TL/SM cascade) + Tester. No web-FE (mobile-primary role).
> **Priority:** P0 (must-ship Phase 1) · P1 (nice-to-have).
> **Dependencies** are topological — build in order. Foundation (Wave 0) precedes everything: schema, auth, runtime RBAC resolver, auto-assign engine, approval cascade, `/me`, photo endpoint, Flutter DS parity, generated Dart models.

---

## Columns
| Column | Meaning |
|---|---|
| Screen ID | Stable short ID (cross-refs `spec.md`, `api-contract.md`). |
| Screen file | Path under `screens/` (the pixel target). |
| States / sheets | The `states/` + `bottom-sheets/` variants to wire. |
| Dependencies | What must exist first. |
| Mobile-FE tasks | Flutter widget/route/repository work. |
| Backend tasks | Endpoint(s) + logic + migration notes (most are Wave-0 shared). |
| Pri | P0 / P1. |

---

## Auth + bootstrap

### AUTH-LOGIN — Login
| Field | Value |
|---|---|
| Screen file | `screens/01-login.html` |
| States / sheets | `states/locked.html`, `states/error.html` |
| Dependencies | Wave-0 auth + `/me` |
| Mobile-FE tasks | Login form (email/phone + password, reveal toggle); call `POST /auth/login`; store tokens (secure storage); on success `GET /me` → route by role; render locked/error states; navy-on-orange Sign In (52px); Maniax wordmark lockup. |
| Backend tasks | (Wave 0) `POST /auth/login` (bcrypt, JWT access+refresh, rate-limit→423/429); `POST /auth/refresh` (rotate+reuse-detect); `POST /auth/logout`; forgot/reset; `POST /devices/register`. |
| Pri | P0 |

### MON-DASH — Monitor Home (role-resolved)
| Field | Value |
|---|---|
| Screen file | `screens/02-dashboard.html` |
| States / sheets | `states/loading.html`, `states/empty.html` |
| Dependencies | AUTH-LOGIN; Wave-0 `/me`; auto-assign (roster published) |
| Mobile-FE tasks | Build the mobile shell (navy top bar + Game-Zone scope strip + 5-slot bottom nav + FAB) from the Flutter DS; render shift card + KPI tiles + today's checklist list via `useDashboard`/`ChecklistRepository`; overdue badge; nothing-assigned empty state; loading skeleton. |
| Backend tasks | `GET /me/dashboard` (own GZ + own assignments; counts by state; shift). |
| Pri | P0 |

## Checklist core (the anchor flow)

### MON-CHK-TODAY — Today's Checklists
| Field | Value |
|---|---|
| Screen file | `screens/03-today-checklists.html` |
| States / sheets | `states/loading.html`, `states/empty.html`, `states/error.html`, `states/forbidden.html` |
| Dependencies | MON-DASH; auto-assign engine (Wave 0) |
| Mobile-FE tasks | List of `ChecklistInstance` rows with template name + frequency tag + item-count + `StatusBadge` + "📷 needed" hint; tap → fill (not-started/in-progress) or status (submitted+); loud Sent-back row + Re-fill; mock repo then Dio. |
| Backend tasks | `GET /me/checklists/today` (own assignments, own GZ; stored sub-state per instance). |
| Pri | P0 |

### MON-CHK-FILL — Fill Checklist (CRITICAL)
| Field | Value |
|---|---|
| Screen file | `screens/04-checklist-fill.html` |
| States / sheets | `bottom-sheets/photo-capture.html`, `bottom-sheets/a-item-note-photo.html`, `states/offline.html` |
| Dependencies | MON-CHK-TODAY; photo endpoint (Wave 0); instance detail |
| Mobile-FE tasks | Sectioned `ChecklistItem` rows with the 44px **G/A segmented toggle** (G green / A red-orange, color+icon+label); auto-stamp time + initials; on **A** open the A-note+photo sheet + flag photo-required; completion-photo `PhotoUpload` tile → photo-capture sheet; multipart upload via `PhotoRepository`; save responses (`PATCH …/responses`, `If-Match`) as draft; **offline: hold draft in memory + retry**; FAB = camera. |
| Backend tasks | `GET /checklist-instances/:id` (filler+GZ scoped, version/ETag); `PATCH …/responses` (upsert responses, set `fillerId`, bump version, 409 on stale, audit); `POST /uploads/photos` (multipart → **local disk**, `sharp` thumb, `Photo` row); `DELETE /uploads/photos/:id`. |
| Pri | P0 |

### MON-CHK-SUBMIT — Submit Checklist
| Field | Value |
|---|---|
| Screen file | `screens/05-checklist-submit.html` |
| States / sheets | `bottom-sheets/confirm-submit.html`, `states/offline.html`, `states/error.html` |
| Dependencies | MON-CHK-FILL; cascade engine (Wave 0) |
| Mobile-FE tasks | Review summary (G/A counts, completion + A-item thumbnails); enforce the photo/answer **gate client-side** with inline danger messages; confirm-submit sheet; `POST …/submit`; handle 422 (highlight missing); on success route to status. |
| Backend tasks | `POST /checklist-instances/:id/submit` — **re-check gate (422 if untouched item / no completion photo / missing A-photo)**; state→`SUBMITTED`; create TL/SM/OH `ApprovalStep`s; **notify TL (FCM + in-app)**; audit. |
| Pri | P0 |

### MON-CHK-STATUS — My Checklist Status
| Field | Value |
|---|---|
| Screen file | `screens/06-my-status.html` |
| States / sheets | `states/empty.html`, sent-back banner (inline) |
| Dependencies | MON-CHK-SUBMIT; cascade (Wave 0); send-back from TL/SM/OH packs |
| Mobile-FE tasks | `ApprovalTimeline` vertical stepper (Filled→TL→SM→OH) with actor/role/timestamp + `StatusBadge`; show **real sub-state** (never Pending); **Sent-back red banner + reason + Re-fill** → reopen fill; `/me/submissions` list. |
| Backend tasks | `GET /me/submissions`; `GET /checklist-instances/:id/timeline` (steps + sentBackReason); `renderStatusForViewer()` used by all overview/status endpoints (shared, Wave 0). |
| Pri | P0 |

## Supporting screens

### MON-ROSTER-OWN — My Roster (read-only)
| Field | Value |
|---|---|
| Screen file | `screens/08-my-roster.html` |
| States / sheets | `states/empty.html`, day/week toggle |
| Dependencies | Wave-0 roster |
| Mobile-FE tasks | Day/week toggle; own `RosterEntry` rows (ride + shift, template-colored blocks); leave overlay; **no create/edit affordance**. |
| Backend tasks | `GET /me/roster?date=` / `?weekOf=` (own entries only; roster-create denied to MON by resolver). |
| Pri | P0 |

### SH-PROFILE — Profile / More
| Field | Value |
|---|---|
| Screen file | `screens/07-profile.html` |
| States / sheets | — |
| Dependencies | Wave-0 `/me` |
| Mobile-FE tasks | Profile (name, role, Game Zone, reports-to, certified rides); Alerts entry; logout (confirm → clear tokens → login). |
| Backend tasks | `GET /me/profile`; `POST /auth/logout`; `GET /me/notifications` + read. |
| Pri | P0 |

---

## Build order (topological)
1. **(Wave 0)** auth, `/me`, RBAC resolver, schema, auto-assign, cascade, photo endpoint, Flutter DS, Dart models.
2. `AUTH-LOGIN` → `MON-DASH` (shell + role-resolve).
3. `MON-CHK-TODAY` → `MON-CHK-FILL` → `MON-CHK-SUBMIT` (the anchor flow — depends on photo endpoint + cascade).
4. `MON-CHK-STATUS` (depends on submit + cascade + the TL send-back touchpoint).
5. `MON-ROSTER-OWN`, `SH-PROFILE` (independent; can run in parallel).

> The anchor demo gate: **Ramesh logs in → opens Trampoline Daily → ticks G/A with a completion photo + an A-item photo → submit blocked then passes → submitted → TL notified → status timeline shows awaiting-TL → (TL sends back) red banner + Re-fill.** When that closes end-to-end against real APIs, the anchor is DONE and SCM/CS clones can fan out.
