# Store Manager (Web) — Developer Task Breakdown

> Source-of-truth task list for the Store Manager web slot (dev-mode v1.0, Step 3b). One row per screen + shared dependencies. Surface = Next.js web, desktop 1440×900.
> **Team for this slot:** Web (Next.js) FE + Backend (shared with TL/OH cascade + auto-assign) + Tester. No mobile-FE (web-primary role; the mobile companion is P2).
> **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, **multi-branch scoping**, auto-assign engine (mapping-aware for CL-1 authored templates), approval cascade + `renderStatusForViewer()`, **WorkOrder entity + state machine + single-track HELD hold (CL-2)**, `/me`, photo viewer endpoint, web design-system, generated TS 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 / modals | The `states/` + `modals/` variants to wire. |
| Dependencies | What must exist first. |
| Web-FE tasks | Next.js page/component/data-hook work. |
| Backend tasks | Endpoint(s) + logic + migration notes (most are Wave-0 shared). |
| Pri | P0 / P1. |

---

## Auth + bootstrap + shell

### AUTH-LOGIN — Login
| Field | Value |
|---|---|
| Screen file | `screens/01-login.html` |
| States / modals | `states/locked.html`, `states/error.html` |
| Dependencies | Wave-0 auth + `/me` |
| Web-FE tasks | Centered login card (email/phone + password, reveal toggle); `POST /auth/login`; store tokens; `GET /me` → route by role; locked/error states; navy-on-orange Sign In (44px); Maniax wordmark lockup. |
| Backend tasks | (Wave 0) `POST /auth/login` (bcrypt, JWT, rate-limit→423/429); refresh/logout/forgot/reset. |
| Pri | P0 |

### SM-DASH — Store Manager Dashboard (role-resolved + shell)
| Field | Value |
|---|---|
| Screen file | `screens/02-dashboard.html` |
| States / modals | `states/loading.html`, `states/empty.html`, `states/session-expired.html` |
| Dependencies | AUTH-LOGIN; Wave-0 `/me`; cascade; auto-assign |
| Web-FE tasks | Build the **web shell** (240px navy sidebar from `/me.nav` with orange active accent + sticky topbar with **branch badge** (read-only) + bell + profile) once, reused by all SM pages; KPI tiles (Awaiting my approval / Compliant / Overdue / Gaps); "Awaiting approval" + "Roster gaps" panels; loading skeleton; empty all-clear. |
| Backend tasks | `GET /me/dashboard` (own GZ; counts by state + gaps + awaiting-SM queue). |
| Pri | P0 |

## People / users

### SM-USERS — Team / Users
| Field | Value |
|---|---|
| Screen file | `screens/03-users.html` |
| States / modals | `states/loading.html`, `states/empty.html`, `states/forbidden.html` |
| Dependencies | shell; Wave-0 users + scoping |
| Web-FE tasks | `DataTable` (Name/Role/Reports-to/Certified rides/Status/actions) + search + role/ride filter chips; **+ Onboard staff** primary → `SM-USER-NEW`; row View → detail drawer, Edit → edit. |
| Backend tasks | `GET /users` (own GZ, paginated, filterable). |
| Pri | P0 |

### SM-USER-NEW — Onboard Staff (Limited)
| Field | Value |
|---|---|
| Screen file | `screens/04-user-new.html` |
| States / modals | validation (inline) |
| Dependencies | SM-USERS; `/rides` |
| Web-FE tasks | `Stepper` (Details → Role(≤SM) → Reports-to → Certified rides); GZ auto-set + locked; reports-to picker filtered to valid managers; cert-rides multiselect chips; `POST /users`; 422 handling. |
| Backend tasks | `POST /users` (gameZoneId forced; **roleCode ∉ {OH,SM} else 422**; bcrypt/invite; audit). |
| Pri | P0 |

### SM-USER-EDIT — Edit Staff
| Field | Value |
|---|---|
| Screen file | `screens/05-user-edit.html` |
| States / modals | `modals/reassign-manager.html`, deactivate confirm |
| Dependencies | SM-USER-DETAIL |
| Web-FE tasks | Edit form (role≤SM, reports-to, certs, active); **reassign-manager confirm modal**; deactivate confirm; `PATCH /users/:id` (`If-Match`); 409/422 handling. |
| Backend tasks | `PATCH /users/:id` (role≤SM guard, reassign audit, deactivate, version). |
| Pri | P0 |

### SM-USER-DETAIL — Staff Detail (drawer)
| Field | Value |
|---|---|
| Screen file | `screens/06-user-detail.html` |
| States / modals | right-side drawer |
| Dependencies | SM-USERS |
| Web-FE tasks | 420px right drawer: profile + sub-tree chips + recent checklist history (StatusBadge); Edit button → SM-USER-EDIT. |
| Backend tasks | `GET /users/:id` (own GZ; subtree + history). |
| Pri | P1 |

### SM-HIER-TREE — Hierarchy Tree
| Field | Value |
|---|---|
| Screen file | `screens/07-hierarchy.html` |
| States / modals | expand/collapse |
| Dependencies | shell; Wave-0 hierarchy |
| Web-FE tasks | `HierarchyTree` rooted at SM (TL → Monitors/CS), expand/collapse, role-colored chips; node → SM-USER-DETAIL drawer. |
| Backend tasks | `GET /me/hierarchy` (own GZ tree). |
| Pri | P1 |

## Roster (the build→publish chain)

### SM-ROSTER — Roster Builder (CRITICAL)
| Field | Value |
|---|---|
| Screen file | `screens/08-roster.html` |
| States / modals | `modals/publish-confirm.html`, conflict + gap inline, leave overlay |
| Dependencies | shell; shift templates; users + rides; Wave-0 roster |
| Web-FE tasks | `Calendar/Roster grid` (day/week/month tabs; rides×days; Morning light-blue / Evening blue blocks); staff palette + **drag-to-cell** (or click-assign fallback); opener toggle; conflict warning; gap-alert badge; header actions (Upload/Templates/Leave/**Publish**). |
| Backend tasks | `GET /roster`; `POST/PATCH/DELETE /roster/entries` (own GZ, conflict 422). |
| Pri | P0 |

### SM-ROSTER-UPLOAD — Roster Bulk Upload (CRITICAL)
| Field | Value |
|---|---|
| Screen file | `screens/09-roster-upload.html` |
| States / modals | `modals/upload-errors.html`, `states/error.html` |
| Dependencies | SM-ROSTER |
| Web-FE tasks | `Stepper` (Upload drag-drop + template download → Validate/dry-run → Review errors → Commit); **upload-errors modal** (row/field/message); commit gated until clean (or valid-only mode); on commit route to SM-ROSTER. |
| Backend tasks | `POST /roster/upload?dryRun=true` (parse + per-row validate, out-of-GZ/unknown-ride/dup → errors, no writes); `POST /roster/upload` (commit DRAFT, mode); `GET /roster/upload/template`. |
| Pri | P0 |

### SM-ROSTER-TEMPLATES — Shift Templates
| Field | Value |
|---|---|
| Screen file | `screens/10-shift-templates.html` |
| States / modals | add/edit template modal |
| Dependencies | shell |
| Web-FE tasks | List of ShiftTemplate rows; add/edit modal (name/start/end); deactivate. |
| Backend tasks | `GET/POST/PATCH /shift-templates` (own GZ; unique name+start+end). |
| Pri | P1 |

### SM-ROSTER-LEAVE — Leave & Absence
| Field | Value |
|---|---|
| Screen file | `screens/11-leave.html` |
| States / modals | approve/deny, gap-alert |
| Dependencies | shell; roster |
| Web-FE tasks | Leave list (person/date/reason/status) + Approve/Deny; show gap-alert when leave uncovers a ride+shift. |
| Backend tasks | `GET /leave`; `POST /leave/:id/approve` (recompute gaps). |
| Pri | P1 |

### SM-ROSTER-PUBLISH — Publish Roster
| Field | Value |
|---|---|
| Screen file | `screens/12-roster-publish.html` |
| States / modals | `modals/publish-confirm.html`, re-publish warn |
| Dependencies | SM-ROSTER; **auto-assign engine (Wave 0)** |
| Web-FE tasks | Period summary (entries/rides/gaps/already-published); **Publish period** primary → publish-confirm modal (explicit auto-assign side effect); `POST /roster/publish` (`If-Match`); success toast; 409 handling. |
| Backend tasks | `GET /roster/publish/preview`; `POST /roster/publish` (entries→PUBLISHED + **run auto-assign**, idempotent; `ROSTER_PUBLISHED` audit + notifications). |
| Pri | P0 |

## Checklist (overview → assign → approve)

### SM-CHK-OVERVIEW — Checklist Status
| Field | Value |
|---|---|
| Screen file | `screens/13-checklist-overview.html` |
| States / modals | `states/loading.html`, `states/empty.html`, drilldown drawer |
| Dependencies | shell; cascade + `renderStatusForViewer()` (Wave 0) |
| Web-FE tasks | `DataTable` (Template/Ride/Shift/Filler/Cascade/Status/Due) + Active/Overdue tabs + filters; `StatusBadge` via `renderStatusForViewer(SM)` (Pending); row → drilldown drawer (timeline + photos), "Open approval" if at SM level. |
| Backend tasks | `GET /checklists` (own GZ; statusForViewer=SM; filters/tabs/pagination). |
| Pri | P0 |

### SM-CHK-ASSIGN — Checklist Assignment / Override
| Field | Value |
|---|---|
| Screen file | `screens/14-checklist-assign.html` |
| States / modals | `modals/assign-override.html`, override confirm |
| Dependencies | SM-CHK-OVERVIEW; auto-assign |
| Web-FE tasks | Auto-assignment trace table (template/roster-entry/reason); **+ Assign** → assign/override modal (template + target role/ride/employee/shift); reassign filler; override confirm. |
| Backend tasks | `GET /checklist-assignments`; `POST /checklist-assignments`; `PATCH …/reassign` (own GZ/team; cross-GZ 422; audit). |
| Pri | P1 |

### SM-CHK-APPROVE — Store Manager Approval (CRITICAL)
| Field | Value |
|---|---|
| Screen file | `screens/15-checklist-approve.html` |
| States / modals | `modals/sendback-reason.html`, photo lightbox, approve confirm, `states/error.html` |
| Dependencies | SM-CHK-OVERVIEW; cascade (Wave 0); photo viewer |
| Web-FE tasks | Filled checklist (G/A + note + **read-only** server time/initials + per-A photo); **completion-photo gallery + lightbox** (`PhotoUpload` viewer mode); `ApprovalTimeline`; **Approve** (green, → `POST …/approve` level SM) + **Send-back** (red-orange → send-back-reason modal, → `POST …/sendback`); 409/403 inline; on approve leave queue. |
| Backend tasks | `GET /me/approvals?level=SM`; `…/timeline`; `…/photos`; **`POST …/approve` (level 2 → SM_APPROVED, notify OH)**; **`POST …/sendback` (level 2 → SENT_BACK to original filler, reason required, restart-from-TL)**; audit. |
| Pri | P0 |

### SM-CHK-UPLOAD — Upload Check List (list + create) — CL-1
| Field | Value |
|---|---|
| Screen file | `screens/16-checklist-upload.html` |
| States / modals | builder stepper, duplicate, deactivate |
| Dependencies | shell; seeded templates (Wave 0); rides; ChecklistBuilder (`DESIGN_SYSTEM §6.21`) |
| Web-FE tasks | Two-group list (seeded read-only + own-GZ editable); **+ New check list** → **ChecklistBuilder** `Stepper` (Details: name/frequency incl. opening/closing/ride/fill-role/GZ-scope-locked → Sections&Items: section/item tree + item editor with text/test-method/G-A/requires-photo-on-A → Assign → Review); save draft / publish; duplicate; deactivate; cross-GZ scope blocked (422/403). |
| Backend tasks | `GET /checklist-templates?scope=`; `GET …/:id`; `POST /checklist-templates` (gameZoneId forced; authorId=me); `PATCH …/:id` (versioned, If-Match); `POST …/:id/duplicate`; `PATCH …/:id/active`; audit. |
| Pri | P0 |

### SM-CHK-TPL-ASSIGN — Assign Check List — CL-1
| Field | Value |
|---|---|
| Screen file | `screens/20-checklist-tpl-assign.html` |
| States / modals | assign mapping, scope picker, match preview |
| Dependencies | SM-CHK-UPLOAD; auto-assign engine; rides + shift templates |
| Web-FE tasks | Template recap + mapping form (fill role / ride / shift / opener-only chips); roster **match preview**; existing-mappings table with Remove; cross-GZ target blocked (422/403). |
| Backend tasks | `GET …/:id/mappings`; `POST …/:id/mappings` (own GZ, feeds §3 auto-assign); `DELETE …/mappings/:mappingId`. |
| Pri | P1 |

### SM-CHK-TPL-EDIT — Edit Check List — CL-1
| Field | Value |
|---|---|
| Screen file | `screens/21-checklist-tpl-edit.html` |
| States / modals | builder stepper (Sections&Items step), change-items confirm modal |
| Dependencies | SM-CHK-UPLOAD (reuses ChecklistBuilder) |
| Web-FE tasks | Reopen the builder on an own-GZ template; edit items/mapping; **change-items confirm** (creates a new version, in-progress instances keep old, new applies on next auto-assign); `PATCH /checklist-templates/:id` (If-Match); seeded/out-of-GZ → 403; 409 stale. |
| Backend tasks | `PATCH /checklist-templates/:id` (versioned; own-GZ + author guard; audit). |
| Pri | P1 |

### SM-WO-OUTSOURCE — Outsource / Action-needed Inbox — CL-2 (CRITICAL)
| Field | Value |
|---|---|
| Screen file | `screens/22-wo-outsource.html` |
| States / modals | `modals/outsource-confirm.html`, photo lightbox, `states/empty.html`, `states/forbidden.html` |
| Dependencies | shell; WorkOrder entity + state machine (Wave 0, CL-2); photo viewer; `maintenance-tl-web` escalation (`MTL-WO-ESCALATE`) |
| Web-FE tasks | DataTable of `OUTSOURCED` WOs (own GZ; awaiting/outsourced/closed tabs; `wo-overdue` solid-red); **WorkOrderDetail** (`§6.22`): source A-item + issue photos (lightbox) + Technician return reason + MTL note + **WorkOrder ApprovalTimeline** (`§6.14`); action bar **Confirm outsource** (→ outsource-confirm modal, required note) + **Return to Maintenance TL**; HELD-hold copy; non-`OUTSOURCED`/out-of-zone → 403. |
| Backend tasks | `GET /work-orders?state=OUTSOURCED` (own GZ; other states denied); `GET /work-orders/:id`; `POST …/:id/outsource` (note required → notify MTL, keep parent HELD); `POST …/:id/return-to-maintenance`; audit. |
| Pri | P0 |

## Reports

### SM-REP-POSITIVE — Positive Report
| Field | Value |
|---|---|
| Screen file | `screens/17-report-positive.html` |
| States / modals | `states/loading.html`, `states/empty.html` |
| Dependencies | shell; cascade/reports (Wave 0) |
| Web-FE tasks | DataTable of compliant instances + date-range + ride/shift filters + **Export ▾**; Reports nav lands here with Tabs → Negative/Overdue. |
| Backend tasks | `GET /reports/positive` (own GZ); `GET /reports/positive/export`. |
| Pri | P0 |

### SM-REP-NEGATIVE — Negative Report
| Field | Value |
|---|---|
| Screen file | `screens/18-report-negative.html` |
| States / modals | photo lightbox, export |
| Dependencies | SM-REP-POSITIVE; photo viewer |
| Web-FE tasks | A-item list (template/ride/shift/date/item/note/**issue photo thumbnail**/filler) with red-orange accent; lightbox; filters; Export. |
| Backend tasks | `GET /reports/negative` (own GZ; item + photo join); export. |
| Pri | P0 |

### SM-REP-OVERDUE — Overdue Report
| Field | Value |
|---|---|
| Screen file | `screens/19-report-overdue.html` |
| States / modals | export, empty |
| Dependencies | SM-REP-POSITIVE; overdue sweep (Wave 0) |
| Web-FE tasks | DataTable of overdue instances (solid-red Overdue badge + escalate-up indicator); filters; Export. |
| Backend tasks | `GET /reports/overdue` (own GZ; `now()>dueAt && <SUBMITTED`); export. |
| Pri | P1 |

---

## Build order (topological)
1. **(Wave 0)** auth, `/me`, RBAC resolver, **multi-branch scoping**, schema, auto-assign, cascade + `renderStatusForViewer()`, photo viewer, web DS, TS models.
2. `AUTH-LOGIN` → `SM-DASH` (**the web shell** — sidebar from `/me.nav`, branch badge, role-resolve).
3. People: `SM-USERS` → `SM-USER-NEW` / `SM-USER-EDIT` (Limited onboard) → `SM-USER-DETAIL`, `SM-HIER-TREE`.
4. Roster: `SM-ROSTER` → `SM-ROSTER-UPLOAD` / `SM-ROSTER-TEMPLATES` / `SM-ROSTER-LEAVE` → **`SM-ROSTER-PUBLISH` (fires auto-assign)**.
5. Checklist: `SM-CHK-OVERVIEW` → `SM-CHK-ASSIGN` → **`SM-CHK-APPROVE` (the 2nd-level cascade touchpoint)**.
6. Upload Check List (CL-1): **`SM-CHK-UPLOAD` (ChecklistBuilder)** → `SM-CHK-TPL-ASSIGN` → `SM-CHK-TPL-EDIT`.
7. Outsource Inbox (CL-2): **`SM-WO-OUTSOURCE`** (after the WorkOrder entity + `maintenance-tl-web` escalation land in Wave 0).
8. Reports: `SM-REP-POSITIVE` → `SM-REP-NEGATIVE` → `SM-REP-OVERDUE` (can run in parallel once overview lands).

> The SM demo gate: **Rohit logs in → onboards Ramesh (Limited) → builds + bulk-uploads + publishes the roster (auto-assign fires) → watches Trampoline Daily climb to his queue → opens the approval, views the photos → Approve passes to OH (or Send-back returns to Ramesh, reason required, cascade restarts from TL) → the Negative report lists the A items with photos.** When that closes end-to-end against real APIs, the SM web pack is DONE.
