# Maintenance Team Leader (Web) — Developer Task Breakdown

> Source-of-truth task list for the Maintenance Team Leader (web) slot (dev-mode v1.0, Change List v1.1 CL-2). One row per screen + the shared dependencies. Surface = Next.js web, desktop 1440×900.
> **Team for this slot:** Web (Next.js) FE + Backend (shared WO state machine + RBAC) + Tester. Coordinate with the Technician (mobile) pack for the assign↔fix loop. No mobile-FE built here (web-primary role).
> **Priority:** P0 (must-ship Phase 1) · P1 (nice-to-have).
> **Dependencies** are topological — build in order. Foundation (Wave 0) precedes everything: schema (+ `WorkOrder`/`WorkOrderEvent`/`WorkOrderPhoto` + `InstanceState.HELD`), auth, runtime RBAC resolver, the WO state machine + HELD-release computation, `/me`, multi-branch scope, generated TS types.

---

## 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 route/component/data work. |
| Backend tasks | Endpoint(s) + logic + migration notes (most are Wave-0 / CL-2 shared). |
| Pri | P0 / P1. |

---

## Auth + bootstrap + shell

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

### MTL-DASH — Maintenance Dashboard (role-resolved) — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/02-dashboard.html` |
| States / modals | `states/loading.html`, `states/empty.html` |
| Dependencies | AUTH-LOGIN; Wave-0 `/me`; WO state machine; **`GET /mtl/dashboard` (flagged gap #1)** |
| Web-FE tasks | Build the **web shell** (navy 240px sidebar from `/me` nav + 64px topbar + **read-only branch badge** + breadcrumb) once, reused by all MTL screens; render WO-state KPI cards (Routed/Assigned/In-progress/Held-source/Returned/Closed-today/Overdue) with `wo-*` colors; attention strip; WO queue preview; loading skeleton; empty (all-clear). |
| Backend tasks | `GET /mtl/dashboard` (own-GZ WO aggregate; held-source = `ChecklistInstance.state=HELD` count; queue preview; overdue derive — **confirm SLA source gap #4**). |
| Pri | P0 |

## Work-Orders (the maintenance branch)

### MTL-WO-QUEUE — Work-Order Queue — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/03-wo-queue.html` |
| States / modals | `states/loading.html`, `states/no-results.html`, `states/empty.html`, `states/error.html` |
| Dependencies | MTL-DASH; WO state machine (Wave-0 / CL-2) |
| Web-FE tasks | `DataTable` (WO# / source·ride / A-item / state / technician / age·SLA / action); `StatusBadge` with `wo-*` colors; state filter chips + Overdue; secondary filters (ride/priority/technician); Routed row → Assign, Returned row → Review. |
| Backend tasks | `GET /work-orders?scope=own&state=&...` (own GZ; pagination; state + derived overdue filters). |
| Pri | P0 |

### MTL-WO-DETAIL — Work-Order Detail — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/04-wo-detail.html` |
| States / modals | `modals/photo-lightbox.html`, action bar, HELD banner |
| Dependencies | MTL-WO-QUEUE; WO timeline + photos |
| Web-FE tasks | Source A-item (read-only text/time/initials) + issue photos → lightbox; **HeldBanner** (`DESIGN_SYSTEM §6.24`); reason/return history; **WorkOrder-variant ApprovalTimeline** (`§6.14`); state-/role-gated action bar (Assign / Reassign / Escalate-Outsource). |
| Backend tasks | `GET /work-orders/:id`, `GET …/timeline` (variant=workorder), `GET …/photos` (issue/fix/return grouping — **gap #3**). |
| Pri | P0 |

### MTL-WO-ASSIGN — Assign to Technician — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/05-wo-assign.html` |
| States / modals | `modals/assign-technician.html`, `modals/reassign-confirm.html` |
| Dependencies | MTL-WO-DETAIL; technicians list |
| Web-FE tasks | Technician picker (own-team, own-GZ only) with open-WO **load badges**; search; assign confirm modal; reassign-confirm for a returned WO; `PATCH …/assign` with `If-Match`; 409/422 handling. |
| Backend tasks | `GET /technicians?scope=own`; `PATCH /work-orders/:id/assign { technicianId }` (`ROUTED/RETURNED → ASSIGNED`; notify tech; `WorkOrderEvent`; 422 if tech not on team). |
| Pri | P0 |

### MTL-WO-REVIEW — Review Returned / Done WO — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/06-wo-review.html` |
| States / modals | `modals/photo-lightbox.html`, `modals/reassign-confirm.html`, `modals/escalate-outsource-confirm.html`, `states/error.html` |
| Dependencies | MTL-WO-DETAIL; WO close transition |
| Web-FE tasks | Technician return reason + return photo; original A-item context; WorkOrder-variant timeline (Returned node + you-decide ring); decision bar — **Close internally** (green) / **Reassign** (orange) / **Escalate-Outsource** (navy); 409 refetch. |
| Backend tasks | `PATCH /work-orders/:id/close` (`RETURNED → DONE`, close at maintenance, **no SM notify**; release parent HELD if last — **gap #2**); reuse assign for reassign. |
| Pri | P0 |

### MTL-WO-ESCALATE — Escalate / Outsource Report — CRITICAL
| Field | Value |
|---|---|
| Screen file | `screens/07-wo-escalate.html` |
| States / modals | `modals/escalate-outsource-confirm.html`, `modals/photo-lightbox.html` |
| Dependencies | MTL-WO-REVIEW; outsource transition; SM `SM-WO-OUTSOURCE` exists |
| Web-FE tasks | Compose report pre-addressed to own-GZ SM; **required** reason textarea (422 if blank); carry-over issue+return photos (+add); summary panel; confirm modal; `PATCH …/outsource` with `If-Match`. |
| Backend tasks | `PATCH /work-orders/:id/outsource { note }` (`RETURNED → OUTSOURCED`; **only state that reaches SM** — surface on `SM-WO-OUTSOURCE`; release parent HELD if last; `WorkOrderEvent`; 422 blank note). |
| Pri | P0 |

## People

### MTL-TEAM — Maintenance Team
| Field | Value |
|---|---|
| Screen file | `screens/08-team.html` |
| States / modals | `states/empty.html`, filter/search |
| Dependencies | Wave-0 User reportsTo chain; WO load aggregate |
| Web-FE tasks | Per-tech load cards (Assigned/In-progress/Returned, over-SLA, available) + detailed load table; search/filter; **read-only** (no staffing actions). |
| Backend tasks | `GET /technicians?scope=own&withLoad=true` (own-GZ team + open-WO load roll-up). |
| Pri | P1 |

### SH-PROFILE — Profile / Settings
| Field | Value |
|---|---|
| Screen file | `screens/09-profile.html` |
| States / modals | — |
| Dependencies | Wave-0 `/me` |
| Web-FE tasks | Profile (name/role/reports-to=SM/GZ=own; read-only); 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 / CL-2)** auth, `/me`, RBAC resolver, schema (+ `WorkOrder`/`WorkOrderEvent`/`WorkOrderPhoto` + `InstanceState.HELD`), the **WO state machine + HELD-release computation**, multi-branch scope, TS types.
2. `AUTH-LOGIN` → `MTL-DASH` (web shell + sidebar + read-only branch badge + role-resolve).
3. `MTL-WO-QUEUE` → `MTL-WO-DETAIL` (the queue + detail spine — depends on the WO list/timeline/photos).
4. `MTL-WO-ASSIGN` (assign/reassign → ASSIGNED; needs technicians list).
5. `MTL-WO-REVIEW` → `MTL-WO-ESCALATE` (close-internal vs outsource — the decision + the only SM channel; depends on the close/outsource transitions + HELD release).
6. `MTL-TEAM`, `SH-PROFILE` (independent; parallel).

> The MTL demo gate: **Vikram logs in → sees routed WOs on the dashboard → opens WO-1042 → assigns it to a technician → (tech fixes + photo) it closes internally and, as the last WO, un-HOLDs Trampoline Daily back into the cascade → on a returned WO (WO-1039) he reviews the reason → escalates it as an Outsource report to the Store Manager (the only state that reaches the SM).** When that closes end-to-end against real APIs, the MTL slot is DONE.
