Files
LynkedUpPro_CRM/docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md
2026-05-29 19:26:14 +05:30

150 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan 5 — Subcontractor Task State Machine
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`). Follow the execution protocol (`2026-05-29-execution-protocol.md`) — sequential on shared files, parallel only where lanes are disjoint.
**Goal:** Upgrade subcontractor tasks from a flat 5-status list to the full §10 **state machine**`Assigned → Pre-Work Inspection → In Progress → (On Hold ↔) → Post-Work Review → Pass→Completed / Fail→Rework Needed → In Progress → re-review` — with per-transition note + actor + timestamp (already present) plus **pre/post-inspection attribution**, a **review→rework loop**, owner-table **Category + Assigned Date** columns, and a **richer owner task modal** (stage timeline + photos).
**Architecture:** Most of the infrastructure already exists. `setSubcontractorTaskStatus(taskId, status, comment, actor, photos)` already writes `statusHistory` + `activities` + fires a notification for *any* status string, and the subcontractor's `SubcontractorTaskDetailPage` already renders a status picker, history, activity timeline, thread, and photo upload. So Plan 5 = (1) expand the status constants + add the missing `category`/`assignedDate` fields + re-stage the 10 demo tasks across the richer machine in `mockStore.jsx`; (2) add the pass/fail review gate (owner-only Completed/Rework Needed) and an inspection-capture activity; (3) surface it in the UI — owner table columns, owner modal stage timeline, subcontractor picker expanded to the new self-stages.
**Tech stack:** React 18, Vite 7, mock React-context store, pnpm. No test harness → verify via `npm run build` + grep + dev-run of stage transitions in both owner and subcontractor views.
**Spec/master:** `docs/superpowers/2026-05-29-MASTER-PLAN.md` §10 (sub-task machine), Appendix A.4 (sub-task schema), Appendix G Plan 5.
---
## Current state (verified)
- **`MOCK_SUBCONTRACTOR_TASKS`** (`src/data/mockStore.jsx`, ~line 6934): **10 tasks** `sct_001..sct_010`. Per-task fields include `status`, `priority`, `dueDate`, `projectId` (→ `PRJ-2026-###`; `sct_009` is `null`), `subcontractorId`, `assignedBy`, `photos[]`, `statusHistory[{id,status,actorId,actorName,comment,at}]`, `thread[]`, `activities[{id,type,status,note,photos[],actorId,actorName,actorRole,at}]`, `expenses[]`, `fees[]`, `paymentStatus`, `createdAt`, `updatedAt`. **Missing: `category`, `assignedDate`.** `activities` absent on `sct_004/006/010`.
- **Constants** (~line 6830): `SUBCONTRACTOR_TASK_STATUSES = ['Assigned','In Progress','On Hold','Completed','Cancelled']`; `SUBCONTRACTOR_SELF_STATUSES = ['Assigned','In Progress','On Hold','Completed']`.
- **Mutators** (on context value): `addSubcontractorTask` (creates at 'Assigned', seeds history, notifies), `setSubcontractorTaskStatus(taskId, nextStatus, comment, actor, photos)` (writes `statusHistory` + `activities` + `status_changed` notification — works for ANY status string), `addSubcontractorTaskActivity` (progress update + photos), `addSubcontractorTaskMessage`, `addSubcontractorTaskExpense/Fee`, `cancelSubcontractorTask` (**bug: does NOT write a statusHistory entry**), `reassignSubcontractorTask`.
- **Owner page** `src/pages/owner/SubcontractorTasksPage.jsx`: table columns = Task · Subcontractor · Location · Due · Priority · Status · Created · Actions. **No Category / Assigned Date columns.** Opens `TaskViewModal` (portal modal) on view.
- **Owner modal** `src/components/subcontractor/TaskViewModal.jsx` (~133 lines): **read-only** — renders title/badges/meta/description/photos only. **No status history, no stage timeline, no advance controls.**
- **Subcontractor side** `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx`: already rich — `StatusManagementCard` (buttons from `SUBCONTRACTOR_SELF_STATUSES` + confirm panel with note + photo upload), `StatusHistoryPanel`, `ActivityTimelinePanel`, `TaskThreadCard`, expenses/fees. Inbox: `SubContractorDashboard` filters `subcontractorTasks` by `subcontractorId` → routes to the detail page.
- **Notifications** `MOCK_NOTIFICATIONS`: types `task_assigned`/`message_received`/`task_updated`/`status_changed`; `addNotification` helper fired inside mutators; subcontractor consumes via `NotificationsPanel`.
## The §10 state machine (target)
Ordered statuses (keeping the existing `In Progress` label rather than renaming to avoid breaking status-color maps — §10's "Work In Progress" ≡ `In Progress`):
```
Assigned → Pre-Work Inspection → In Progress → (On Hold ↔ In Progress) → Post-Work Review
→ [Pass] Completed
→ [Fail] Rework Needed → In Progress → Post-Work Review (re-review)
Cancelled (owner/admin only, from any non-terminal stage)
```
- **Subcontractor self-transitions** (`SUBCONTRACTOR_SELF_STATUSES`): `Assigned`, `Pre-Work Inspection`, `In Progress`, `On Hold`, `Post-Work Review`. The sub advances their own work *up to submitting for review*.
- **Owner/admin-only transitions** (the review gate): from `Post-Work Review``Completed` (pass) or `Rework Needed` (fail, with note + issue photos); and `Cancelled` from any stage.
- **Attribution:** `Pre-Work Inspection` and `Post-Work Review` transitions record who performed them via the existing `actor` on `setSubcontractorTaskStatus` (actorName/actorRole land in `statusHistory` + `activities`). The pass/fail note + photos ride the same call.
---
## Lane / ownership map
| Task | Lane (exclusive files) | Phase |
|------|------------------------|-------|
| T1 Constants + schema fields + cancel fix | `src/data/mockStore.jsx` | A (seq) |
| T2 Re-stage 10 demo tasks across the machine | `src/data/mockStore.jsx` | A (seq, after T1) |
| T3 Owner table: Category + Assigned Date columns | `src/pages/owner/SubcontractorTasksPage.jsx` | B (parallel) |
| T4 Owner modal: stage timeline + photos | `src/components/subcontractor/TaskViewModal.jsx` | B (parallel) |
| T5 Subcontractor picker: new stages + review gate | `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx` | B (parallel) |
| T6 Verify | (read-only) | C |
**Phase A (T1, T2) sequential** — both edit `mockStore.jsx`. **Phase B (T3, T4, T5) parallel** — three disjoint files, all depend only on Phase A. **T6** last.
---
## Task 1: Expand status constants, add `category`/`assignedDate`, fix cancel
**Files:** `src/data/mockStore.jsx` only.
- [ ] **Step 1 — status constants** (~line 6836). Expand:
```js
export const SUBCONTRACTOR_TASK_STATUSES = [
'Assigned', 'Pre-Work Inspection', 'In Progress', 'On Hold',
'Post-Work Review', 'Rework Needed', 'Completed', 'Cancelled',
];
// Statuses a subcontractor can set themselves (no review gate, no cancel):
export const SUBCONTRACTOR_SELF_STATUSES = [
'Assigned', 'Pre-Work Inspection', 'In Progress', 'On Hold', 'Post-Work Review',
];
// Owner/admin-only transitions (the review gate + cancel):
export const SUBCONTRACTOR_REVIEW_STATUSES = ['Completed', 'Rework Needed', 'Cancelled'];
// Task categories (for the owner table + assignment):
export const SUBCONTRACTOR_TASK_CATEGORIES = [
'Roofing', 'Electrical', 'Plumbing', 'Siding', 'Gutters', 'Windows', 'Painting', 'Inspection', 'General',
];
```
- [ ] **Step 2 — add `category` + `assignedDate` to all 10 tasks.** Give each a trade-appropriate `category` (match the task title/trade, e.g. ridge-cap → 'Roofing', panel → 'Electrical') and an `assignedDate` (a real ISO date ≤ its earliest `statusHistory` entry; `createdAt`'s date is fine).
- [ ] **Step 3 — `addSubcontractorTask` factory** (~line 8084): initialise new tasks with `category: input.category || 'General'`, `assignedDate: <today ISO>`, and `activities: []` (so the 3 tasks/pages that read `activities` never hit undefined).
- [ ] **Step 4 — fix `cancelSubcontractorTask`** (~line 8404) so it routes through the same audit path: append a `statusHistory` entry (status 'Cancelled', actor, comment) — reuse `setSubcontractorTaskStatus(taskId, 'Cancelled', comment, actor)` rather than setting the field directly, so every transition is logged.
- [ ] **Step 5 (verify):** `npm run build``✓ built`. `git grep -c "category:" -- src/data/mockStore.jsx` reflects +10 (the tasks); `git grep -c "assignedDate:" ...` ≥ 10. Build clean, 0 duplicate-key warnings.
- [ ] **Step 6:** commit `git commit -am "feat(data): expand subcontractor task state machine constants + add category/assignedDate; audit cancel"`.
## Task 2: Re-stage the 10 demo tasks across the machine
**Files:** `src/data/mockStore.jsx` only. **After T1.**
Distribute the 10 tasks across the richer machine so the demo can show every stage, each with a coherent `statusHistory` (note + actor + timestamp) telling the task's story.
- [ ] **Step 1:** Assign target statuses for variety, e.g.: one `Assigned`, one `Pre-Work Inspection`, two `In Progress`, one `On Hold`, one `Post-Work Review`, one `Rework Needed`, two `Completed`, one `Cancelled`. (Adjust to fit existing data; keep `sct_003`/`sct_007` as `Completed` since they're `Paid`.)
- [ ] **Step 2:** For each task, make `statusHistory` a coherent ordered thread reaching its current status — e.g. a `Post-Work Review` task has entries: Assigned → Pre-Work Inspection (by the sub, with a note "site walk complete, materials staged") → In Progress → Post-Work Review (by the sub, "submitted for review"). A `Rework Needed` task adds: Post-Work Review → Rework Needed (by owner `Justin Johnson`/`Diana Reeves`, note "flashing detail failed; re-seal valley", with a `photos` entry). Dates strictly increasing, ≤ 2026-05-29. `actorId`/`actorName`/`actorRole` real (the assigned sub for sub-steps; the owner for review steps).
- [ ] **Step 3:** Mirror the key transitions into `activities[]` for the timeline (the existing `setSubcontractorTaskStatus` does this at runtime; for seed data, hand-author 13 `activities` per task so `ActivityTimelinePanel` shows a populated timeline). Inspection steps use `type: 'status_change'` with a note + photos; progress notes use `type: 'progress_update'`.
- [ ] **Step 4 (verify):** `npm run build``✓ built`. `git grep -ohE "status: '[^']+'" -- src/data/mockStore.jsx` over the tasks shows the new stages present (Pre-Work Inspection, Post-Work Review, Rework Needed each ≥ 1). Every `statusHistory`/`activities` actor is a real person; dates increasing.
- [ ] **Step 5:** commit `git commit -am "feat(data): re-stage subcontractor demo tasks across the full state machine with coherent history"`.
## Task 3: Owner table — Category + Assigned Date columns
**Files:** `src/pages/owner/SubcontractorTasksPage.jsx` only. **Phase B (parallel).**
- [ ] **Step 1:** Add two columns to the desktop table header (after `Task` or before `Status`): **Task Category** (renders `task.category` as a small chip) and **Assigned Date** (renders `task.assignedDate` formatted like the existing `Created` column). Add the matching `<td>` cells in the row body. Mirror into the mobile card view if present.
- [ ] **Step 2:** If the page has filters, optionally add a category filter using `SUBCONTRACTOR_TASK_CATEGORIES` (reuse the existing filter pattern; optional — don't overbuild).
- [ ] **Step 3 (verify):** `npm run build``✓ built`. Dev-run: the owner table shows Category + Assigned Date for every task.
- [ ] **Step 4:** commit `git commit -am "feat(subtasks): add Task Category + Assigned Date columns to the owner task table"`.
## Task 4: Owner modal — stage timeline + photos (+ review gate)
**Files:** `src/components/subcontractor/TaskViewModal.jsx` only. **Phase B (parallel).**
The owner modal is currently read-only. Make it show the full stage story and (optionally) let owner/admin run the review gate.
- [ ] **Step 1:** After the meta grid, add a **Stage Timeline** section rendering `task.statusHistory` (reverse-chronological): each entry shows the status (as a badge), `actorName` + `actorRole`, `comment`, and the timestamp — mirror the `StatusHistoryPanel` styling from `SubcontractorTaskDetailPage.jsx` (read it for the pattern). Render any `photos` on an entry as thumbnails.
- [ ] **Step 2:** Add an **issues/review-photos** view: surface the `Rework Needed` / `Post-Work Review` entries' notes + photos prominently (these are the "what's wrong" the owner logged).
- [ ] **Step 3 (review gate — owner/admin only):** when `task.status === 'Post-Work Review'`, show **Approve (→ Completed)** and **Send to Rework (→ Rework Needed)** buttons gated to `['OWNER','ADMIN'].includes(user?.role)`. Each opens a small note (+ optional photo) capture and calls `setSubcontractorTaskStatus(task.id, 'Completed'|'Rework Needed', note, { id: user.id, name: user.name, role: user.role }, photos)`. Pull `setSubcontractorTaskStatus` + `user` from `useMockStore()`/`useAuth()`. (If wiring photo upload is heavy, a note-only capture is acceptable — match the Phase-4 inspection-issue pattern.)
- [ ] **Step 4 (verify):** `npm run build``✓ built`. Dev-run: opening a task shows its stage timeline; a `Post-Work Review` task shows Approve/Rework; approving moves it to Completed and appends to history.
- [ ] **Step 5:** commit `git commit -am "feat(subtasks): owner task modal — stage timeline, review photos, and Post-Work review gate"`.
## Task 5: Subcontractor picker — new stages + review gate
**Files:** `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx` only. **Phase B (parallel).**
- [ ] **Step 1:** The `StatusManagementCard` builds its buttons from `SUBCONTRACTOR_SELF_STATUSES` — which now includes `Pre-Work Inspection` and `Post-Work Review`. Confirm the picker renders all five self-stages and that selecting one opens the existing confirm panel (note + photo upload) and calls `setSubcontractorTaskStatus`. Adjust any hardcoded 4-button layout to handle 5 stages.
- [ ] **Step 2:** Label the inspection stages clearly for the sub: `Pre-Work Inspection` = "Start Pre-Work Inspection" (capture a site-walk note + photos), `Post-Work Review` = "Submit for Review" (the sub hands off; they can NOT set Completed — that's the owner's gate). Ensure `Completed`/`Rework Needed`/`Cancelled` do NOT appear as sub-settable buttons (they're in `SUBCONTRACTOR_REVIEW_STATUSES`).
- [ ] **Step 3:** When `task.status === 'Rework Needed'`, show the owner's rework note/photos prominently (from the latest `Rework Needed` `statusHistory`/`activities` entry) and let the sub move back to `In Progress` to redo, then `Post-Work Review` again.
- [ ] **Step 4 (verify):** `npm run build``✓ built`. Dev-run as a subcontractor (e.g. login `maya`): advancing Assigned → Pre-Work Inspection → In Progress → Post-Work Review works with notes/photos; the sub cannot mark Completed; a Rework Needed task shows the owner's feedback and can be re-submitted.
- [ ] **Step 5:** commit `git commit -am "feat(subtasks): subcontractor picker supports pre-work/post-work stages; review gate stays owner-side"`.
## Task 6: Verify (read-only)
- [ ] `npm run build` clean, 0 duplicate-key warnings.
- [ ] All 10 tasks have `category` + `assignedDate`; the new stages (Pre-Work Inspection, Post-Work Review, Rework Needed) each appear on ≥ 1 task; every `statusHistory`/`activities` actor is a real person; dates increasing and ≤ 2026-05-29.
- [ ] `cancelSubcontractorTask` now writes a `statusHistory` entry (cancel a task in dev → history shows it).
- [ ] **Same task across logins:** open a task as owner (table → modal) and as the assigned subcontractor (`/subcontractor/tasks/:id`) — status, history, and photos agree.
- [ ] The review→rework loop works end-to-end: sub submits Post-Work Review → owner sends to Rework Needed (with note/photo) → sub sees the feedback, redoes, re-submits → owner approves → Completed. Each transition logged with actor + note + timestamp; notifications fire.
- [ ] Owner table shows Category + Assigned Date columns.
---
## Self-review checklist
- [ ] Status set is the full §10 machine; self vs review-gate split enforced (sub can't set Completed/Cancelled).
- [ ] `category` + `assignedDate` on all 10 tasks and in the `addSubcontractorTask` factory; `activities: []` initialised.
- [ ] `cancelSubcontractorTask` audited via statusHistory.
- [ ] 10 tasks re-staged across the machine with coherent histories + real actors.
- [ ] Owner table columns added; owner modal shows stage timeline + photos + review gate; sub picker supports the new stages with the review gate owner-side.
- [ ] Build clean; same task agrees across owner + subcontractor logins.
## Notes for Plan 6
Plan 6 (subcontractor portal data + project parity) builds on this: a sub viewing their task should see the **same project** the owner sees (scoped subset). The state machine + the `projectId` link on each task are the join; Plan 6 renders the permitted project view in the subcontractor pages and seeds richer portal data. ABAC visibility (Plan 8) later formalises the "permitted subset".