# Cleaning Supervisor (Mobile) — Per-Screen Spec

> Acceptance detail per screen (Given/When/Then where useful). **Behaviour is identical to the anchor** (Staff Court Monitor) — same fill→submit→status flow; only the **auto-assigned templates (cleaning/hygiene)** and the **slot-2 nav label ("Cleaning")** differ. RBAC: the Cleaning Supervisor sees only **own roster / own assigned cleaning 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`. Anchor: `docs/design/dev-mode-v1.0/staff-court-monitor-mobile/spec.md`.

---

## 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 — CS is locked to one Game Zone). 56px bottom nav, active slot orange; **slot 2 = "Cleaning" (`spray-can`)**, **slot 3 = "Assign" (`user-plus`, CL-5)**, slot 4 = Status, slot 5 = More (**Roster lives under More**, CL-5). 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 (CS cannot approve, `§3`).
- **Game-Zone scope:** every list is server-filtered to the CS'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.
- **Time-stamp + initials (§9 #9):** each response is stamped with **server time** (tamper-proof) and the **initials are auto-derived from the logged-in user** (Suresh → "SY") — never manual entry or device time. The UI shows them read-only.
- **Status semantics:** `Pending` is what higher roles see; the Cleaning Supervisor 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** Suresh 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 `CS-DASH` (role resolved = CS).
- **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`.

## `CS-DASH` — Cleaning Supervisor Home (`screens/02-dashboard.html`)

- **Purpose:** at-a-glance — today's shift + the count of assigned cleaning checklists by status, due-soon and overdue badges.
- **Given** Suresh is on the Cleaning / Hygiene · Morning shift, **Then** the Home shows a shift card ("Cleaning / Hygiene · Morning · 10:00–16:00") + KPI tiles (e.g. "Assigned 2 · In progress 1 · Done 0") + a list of today's cleaning checklists with `StatusBadge` chips, tappable into `CS-CHK-TODAY`/`CS-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.

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

- **Purpose:** the full list of this shift's auto-assigned cleaning/hygiene checklists with status chips; the entry point to fill.
- **Given** the roster was published, **Then** the list shows each `ChecklistInstance` for Suresh's roster entry (e.g. **Cleaning / Hygiene — Daily**, **Washroom Hygiene Round**) 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** Suresh taps a Not-started or In-progress row, **Then** open `CS-CHK-FILL` for that instance (state → `IN_PROGRESS` on first response).
- **When** he taps a Submitted/approved row, **Then** open `CS-CHK-STATUS` (read-only cascade view).
- A **Sent back** row is loud (red) with a "Re-fill" affordance → reopens `CS-CHK-FILL`.

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

- **Purpose:** the heart of the role — set **G/A** per cleaning/hygiene item + note + server-time + auto-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) → read-only **server time** + **auto initials** ("SY") → 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).
- **Cleaning/hygiene items (seed "Cleaning / Hygiene — Daily"):** e.g. "Remove foreign matter from all play areas", "Clean & sanitize all playing surfaces (no ammonia)", "Washroom — no odours; toilets flushing; soap & paper stocked; bins emptied; surfaces sanitized", "Baby-changing station disinfected", "Café tables & high-touch surfaces sanitized". (Exact seed rows confirmed at the client demo — engine is generic, `§9 #2/#12`.)
- **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** Suresh taps **G**, **Then** the row gets a green left rail, `value=G`, **server time** auto-stamped, **initials auto-derived**, note optional; `PATCH …/responses` saves the response (draft, `IN_PROGRESS`).
  - **When** Suresh 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 (`§9 #7`).
- **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).

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

- **Purpose:** final review + submit; the **photo gate** is enforced here (and server-side).
- **Given** Suresh 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, §9 #7, 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** Suresh 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 `CS-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.

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

- **Purpose:** live cascade status of the Cleaning Supervisor'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 Suresh (Cleaning Supervisor) ✓ → TL → Store Manager → Operation Head (Done)`, each node showing actor + role + timestamp + a `StatusBadge`; the current pending node carries a ring.
- The Cleaning Supervisor 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 `CS-CHK-FILL` (state `IN_PROGRESS`). Send-back always returns to **Suresh** (the original filler), regardless of which approver rejected.
- Empty state when nothing submitted yet.

## `CS-ASSIGN` — Assign Cleaning Work (`screens/09-assign.html`) — CL-5

- **Purpose:** the Cleaning Supervisor's **light task-allocation** surface (AllocationBoard, `DESIGN_SYSTEM §6.23`): Suresh decides **which cleaning staff works where** across today's areas/zones. He **still fills the cleaning checklist himself** (`CS-CHK-FILL`) — Assign routes the *labour*, not the checklist ownership.
- **Layout:** an intro + summary tiles (Areas / Assigned / Open), then an **area→staff list**: each area row (e.g. **Washrooms**, **Café & high-touch surfaces**, **Courts & play surfaces**) shows its cadence, an allocation `StatusBadge` (Assigned green / Unassigned red), the assigned staff avatar+name (with **Change**) or an orange **"+ Assign staff"** CTA. A sticky **"Save allocation"** bar (navy-on-orange) at the bottom.
- **Given** Suresh is on the Cleaning / Hygiene · Morning shift with staff rostered to his team, **Then** the screen lists his Game Zone's cleaning areas with their current allocation; staff on other areas show as **Busy** in the picker.
- **When** Suresh taps **"+ Assign staff"** or **Change** on an area, **Then** the **assign-cleaning-staff bottom sheet** opens (`bottom-sheets/assign-staff.html`) listing on-shift cleaning staff (free vs already-assigned); picking one and confirming sets that area's allocation and flips its badge to **Assigned**.
- **When** Suresh taps **Save allocation**, **Then** `POST /me/cleaning-assignments` persists the staff→area map for the shift; an `AuditLog` row is written.
- **Scope / RBAC:** allocation is **own Game Zone + own cleaning team** only; an out-of-scope staff/area id → 403 (`states/forbidden.html`). This is a **scoped "assign cleaning work" right** (CL-5, `FOUNDATION_SPEC §5`) — it does **not** grant approve/verify, roster-edit, or template-assign rights. It does **not** change the roster or the approval cascade; the cleaning checklist still submits up to the Team Leader.
- **Given** no cleaning staff are on shift yet, **Then** show an empty state ("No cleaning staff on this shift yet").
- Offline → in-memory allocation draft + retry (L10), consistent with the rest of the pack (online-only).

## `CS-ROSTER-OWN` — My Roster (`screens/08-my-roster.html`) — reached via **More** (CL-5)

- **Purpose:** Suresh's own shifts — **read-only** (CS cannot edit rosters, `§3`). Since CL-5 added the **Assign** slot, Roster no longer has a dedicated bottom-nav slot — it is reached from the **More** tab.
- **Given** the day/week toggle, **Then** show own `RosterEntry` rows (cleaning area + 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, cleaning areas; logout.
- **Given** the profile, **Then** show name, role ("Cleaning Supervisor"), Game Zone, reports-to ("Priya Nair · Team Leader"), and assigned cleaning areas ("Washrooms · Café · Courts"). 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 → Cleaning Supervisor Home; no nav/permission is hardcoded; slot 2 = "Cleaning", **slot 3 = "Assign" (CL-5); Roster under More.**
- [ ] Today's list shows exactly the auto-assigned **cleaning/hygiene** checklists for Suresh'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.
- [ ] Per-item time-stamp is **server time** and initials are **auto-derived** ("SY") — no manual/device entry (§9 #9).
- [ ] Submit is blocked (UI **and** server 422) unless every item is answered, **≥1 completion photo** exists (§9 #7), and every A item has its issue photo.
- [ ] On submit: state → Submitted, TL notified, status timeline shows "awaiting TL".
- [ ] Sent-back returns to Suresh with a red banner + reason + Re-fill.
- [ ] CS sees the real sub-state of own instances (never "Pending"); roster is read-only.
- [ ] `CS-ASSIGN` (CL-5): CS can allocate cleaning staff to areas/zones (own GZ + own team) and **Save allocation**; the assign-staff sheet picks on-shift staff; allocation does **not** grant approve/roster-edit/template rights, and the CS still **fills** the checklist himself.
- [ ] Offline retry preserves the in-memory draft; out-of-scope access → 403.
