Files
LynkedUpPro_CRM/docs/superpowers/plans/2026-05-29-demo-data-foundation.md
T

283 lines
15 KiB
Markdown
Raw 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.
# Demo Data Foundation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make the mock data internally coherent — one canonical identity scheme, reconciling financials, and no empty/broken views — as the foundation for the larger demo build.
**Architecture:** All edits are to `src/data/mockStore.jsx` (a ~6,950-line mock store) plus a few consuming components. Changes are surgical and additive. A new `resolvePerson(id)` selector gives every module one consistent way to look up people. Verification is by `npm run build` + `grep` reconciliation + dev-run, since the project has no unit-test harness.
**Tech Stack:** React 18, Vite 7, mock React-context store (`useMockStore`), pnpm.
**Spec:** `docs/superpowers/specs/2026-05-29-demo-data-coherence-design.md` (Phases 13).
**Scope of this plan:** Phase 1 (canonical identity), Phase 2 (financial coherence), Phase 3 (feature-breaking fixes). Phases 410 are separate plans.
---
## Canonical ID reference (used throughout)
The canonical scheme is the `MOCK_USERS` one. Remap everything else to it:
| Entity | Canonical id | Name | Old ids to replace |
|--------|-------------|------|--------------------|
| Owner 1 | `own_001` | Justin Johnson | `owner_001` |
| Owner 2 | `own_002` | Diana Reeves | `owner_002`, and name "Maria Garcia" |
| Admin 1 | `a1` | Adam Admin | `ADM01`, name "Admin One" |
| Admin 2 | `a2` | Amanda Manager | `ADM02`, name "Admin Two" |
| Admin 3 | `a3` | Arthur Director | `ADM03`, name "Admin Three" |
| Field agent 1 | `e1` | Frank Agent | `FA001` |
| Field agent 2 | `e2` | Fiona Field | `FA002` |
| Field agent 3 | `e3` | Fred Flyer | `FA003` |
| Field agent 4 | `e4` | Felicity Fast | `FA004` |
| Field agent 5 | `e5` | Felix Fixer | `FA005` |
| Justin email | — | `justin@lynkeduppro.com` | `justin@johnsondev.com` (reconcile to one — use lynkeduppro.com everywhere) |
Org role taxonomy (`orgMembers[].roleKey`: OWNER/ADMIN/SALES_REP/CANVASSER/SUBCONTRACTOR) stays as-is; only the `userId` it points to changes.
---
## File structure
- **Modify:** `src/data/mockStore.jsx` — identity remap, `resolvePerson`, financial fixes, breakdowns, `leads` seed, sub logins, task↔project fixes, kanban lead values, `DEMO_TODAY`.
- **Modify:** `src/pages/owner/SubcontractorTasksPage.jsx`, `src/components/subcontractor/TaskViewModal.jsx` — add `On Hold` styling.
- **Create:** none in this plan.
---
## Task 1: Add `DEMO_TODAY` constant
**Files:**
- Modify: `src/data/mockStore.jsx` (near top, after imports)
- Modify: `src/components/kanban/KanbanCard.jsx:14` (replace hardcoded date)
- [ ] **Step 1: Add the constant** near the top of `mockStore.jsx` (after the import lines):
```jsx
// Fixed "today" for the demo so seeded dates never go stale or land in the future.
export const DEMO_TODAY = new Date('2026-05-29T12:00:00');
```
- [ ] **Step 2: Use it in KanbanCard.** In `src/components/kanban/KanbanCard.jsx` replace the hardcoded `new Date('2026-04-07')` (line ~14) with an import + use:
```jsx
import { DEMO_TODAY } from '../../data/mockStore';
// ...
const NOW = DEMO_TODAY; // was: new Date('2026-04-07')
```
- [ ] **Step 3: Verify build.** Run: `npm run build` — Expected: `✓ built` with no errors.
- [ ] **Step 4: Commit.**
```bash
git add src/data/mockStore.jsx src/components/kanban/KanbanCard.jsx
git commit -m "chore(data): add DEMO_TODAY anchor and use it for kanban day-counter"
```
---
## Task 2: Add `resolvePerson` selector
**Files:**
- Modify: `src/data/mockStore.jsx` — add a memoized people index + `resolvePerson` exposed on the store context value.
- [ ] **Step 1: Build a merged people index** inside the provider (after the people collections are defined, before the `value={{...}}`):
```jsx
// One lookup for any person across users/personnel/owners/subcontractors.
const peopleIndex = useMemo(() => {
const idx = {};
const add = (p) => { if (p?.id && !idx[p.id]) idx[p.id] = p; };
[...(users || []), ...(personnel || []), ...(owners || []),
...(subcontractors || [])].forEach(add);
return idx;
}, [users, personnel, owners, subcontractors]);
const resolvePerson = (id) => {
const p = peopleIndex[id];
if (!p) return { id, name: 'Unknown', email: '', role: '' };
return { id: p.id, name: p.name || p.fullName || 'Unknown', email: p.email || p.emailAddress || '', role: p.role || '' };
};
```
- [ ] **Step 2: Expose it** on the context value object (add `resolvePerson,` to the `value={{ ... }}`).
- [ ] **Step 3: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 4: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "feat(data): add resolvePerson selector over a merged people index"
```
---
## Task 3: Remap `orgMembers` to canonical ids + fix names
**Files:**
- Modify: `src/data/mockStore.jsx` — the `orgMembers` useState array.
- [ ] **Step 1: Apply the ID + name remap** in `orgMembers` using the Canonical ID reference table:
- `userId: 'owner_001'``'own_001'` (name "Justin Johnson", email `justin@lynkeduppro.com`)
- `userId: 'owner_002'``'own_002'`, **name "Maria Garcia" → "Diana Reeves"**, email → Diana's canonical email from `MOCK_USERS`
- `userId: 'ADM01'/'ADM02'/'ADM03'``'a1'/'a2'/'a3'`, **names "Admin One/Two/Three" → "Adam Admin"/"Amanda Manager"/"Arthur Director"**
- `userId: 'FA001'..'FA005'``'e1'..'e5'` (names already match)
- [ ] **Step 2: Verify no stale ids remain.** Run:
```bash
git grep -nE "owner_00[12]|ADM0[123]|FA00[12345]" -- src/data/mockStore.jsx
```
Expected: **no matches in `orgMembers`** (matches may still exist in subcontractor tasks — fixed in Task 4). Confirm none are in the `orgMembers` block.
- [ ] **Step 3: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 4: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "fix(data): remap orgMembers to canonical ids; reconcile Owner #2 + admin names"
```
---
## Task 4: Remap subcontractor-task person references
**Files:**
- Modify: `src/data/mockStore.jsx``MOCK_SUBCONTRACTOR_TASKS` (and `MOCK_NOTIFICATIONS` actor ids).
- [ ] **Step 1: Replace ids** in every task's `assignedBy`, `actorId`, `senderId`, and status/thread rows:
- `'owner_001'``'own_001'`
- `'ADM01'``'a1'`
- Update the denormalized `assignedByName`/`actorName`/`senderName` so "Admin One" → "Adam Admin" and any owner name → "Justin Johnson".
- [ ] **Step 2: Verify no stale ids remain anywhere.** Run:
```bash
git grep -nE "owner_00[12]|\bADM0[123]\b|FA00[12345]" -- src/data/mockStore.jsx
```
Expected: **no matches** (all remapped).
- [ ] **Step 3: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 4: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "fix(data): remap subcontractor task/notification actor ids to canonical scheme"
```
---
## Task 5: Financial coherence — fix the flagged projects
**Files:**
- Modify: `src/data/mockStore.jsx` — the `projects` array.
Reference (from audit): rules = breakdown `allocated`→budget, `committed`→committedCost, `actual`→actualCost; `committed ≥ actual`; `variancePercent = (actualCost budget)/budget × 100`.
- [ ] **Step 1: proj_005** — set top-level `committedCost: 28850` (matches the breakdown's committed sum). Leave actualCost 14200 (breakdown actual sum). Recompute `variancePercent = (1420028000)/28000×100 = -49.3`.
- [ ] **Step 2: proj_008** — reduce paid invoices so the paid total ≤ `actualCost` (44500). Adjust the invoice `amount`s / `status` so `Σ paid ≤ 44500` and `Σ paid ≤ committedCost (52000)`.
- [ ] **Step 3: proj_002 & proj_010** (completed) — reduce paid invoices so `Σ paid ≤ actualCost` (6200 and 23800 respectively).
- [ ] **Step 4: proj_004** — in `budgetBreakdown`, fix the Contingency line so `committed ≥ actual` (raise committed to ≥ 11500, keeping the over-budget total intact).
- [ ] **Step 5: Add `budgetBreakdown`** to proj_006, 008, 009, 010, 011, 012. Each: 47 line items where `allocated` sums to the project `budget`, `committed` sums to `committedCost`, `actual` sums to `actualCost`, and `committed ≥ actual` per line. Use realistic vendor/category names matching the project type (e.g. for a roof job: "ABC Supply Co. — Materials", "Texas Roofing Crew A — Labor", "City of Plano — Permits").
- [ ] **Step 6: Verify variance signs** render correctly — over-budget shows `+`, under shows ``. (Display logic already does this; just confirm the stored `variancePercent` sign matches `(actualCostbudget)`.)
- [ ] **Step 7: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 8: Reconciliation spot-check.** For each project with a `budgetBreakdown`, confirm the three column sums equal `committedCost`/`actualCost` and `allocated``budget`. (Manual read or a scratch node script; do not commit the script.)
- [ ] **Step 9: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "fix(data): reconcile project financials and add missing budget breakdowns"
```
---
## Task 6: Seed the Leads List page
**Files:**
- Modify: `src/data/mockStore.jsx` — the `leads` useState (currently `useState([])`).
- [ ] **Step 1: Inspect the shape** the page reads. Open `src/pages/LeadsListPage.jsx` and note the fields used (`firstName`/`lastName`, `phones[]`, `urgency`, `status`, `leadSource`, `canvasserName`, `createdByName`, `createdAt`, `stormSource`, `address`, etc.).
- [ ] **Step 2: Seed ~2835 records** by initializing `leads` from the existing storm-attribution leads (`MOCK_STORM_ATTRIBUTION_LEADS`), mapped into the LeadsListPage shape. Use canonical `canvasserName`/`createdByName` (Frank Agent, Fiona Field, … — names that exist in `MOCK_USERS`). Spread `createdAt` across recent dates relative to `DEMO_TODAY`; set `stormSource: true` on storm-sourced ones; vary `urgency`/`status`.
```jsx
const [leads, setLeads] = useState(() => seedLeadsFromAttribution());
```
(Define `seedLeadsFromAttribution()` above the provider; map each SAL-* record to the page shape.)
- [ ] **Step 3: Verify in dev.** Run dev server, open `/emp/fa/leads` (or owner equivalent). Expected: the list is populated (no "No Leads Yet" empty state).
- [ ] **Step 4: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 5: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "fix(data): seed leads list so the Leads page is populated on load"
```
---
## Task 7: Add subcontractor logins (sub_002005)
**Files:**
- Modify: `src/data/mockStore.jsx``MOCK_USERS`.
- [ ] **Step 1: Add four login user records** to `MOCK_USERS` for `sub_002``sub_005`, mirroring the `sub_001` (Carlos) record shape (`id`, `name`, `email`, `password: 'password'`, `role: 'SUBCONTRACTOR'`, type `subcontractor`). Use the names already in `MOCK_SUBCONTRACTORS` (Maya Patel, Dwayne …, Jennifer …, Greg Alston) so ids and names match across collections.
- [ ] **Step 2: Verify in dev.** Log in as `sub_002`'s email / `password`. Expected: the subcontractor dashboard loads and shows that sub's tasks/notifications (Maya's rich set is now reachable).
- [ ] **Step 3: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 4: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "feat(data): add login records for sub_002-005 so each subcontractor inbox is reachable"
```
---
## Task 8: Fix task↔project associations + On Hold styling
**Files:**
- Modify: `src/data/mockStore.jsx``MOCK_SUBCONTRACTOR_TASKS` (projectId/location).
- Modify: `src/pages/owner/SubcontractorTasksPage.jsx``STATUS_CONFIG`.
- Modify: `src/components/subcontractor/TaskViewModal.jsx``STATUS_STYLES`.
- [ ] **Step 1: Re-point tasks** so each task's `projectId` references a project whose `address` matches the task `location` (residential roofing/electrical tasks → residential projects, not the commercial `proj_011`/`proj_012`). Fix the 6 mismatches the audit flagged (sct_004, _005, _006, _007, _008, _010).
- [ ] **Step 2: Add `On Hold`** to `STATUS_CONFIG` in `SubcontractorTasksPage.jsx` (give it an icon + amber/grey styling, consistent with siblings) and to `STATUS_STYLES` in `TaskViewModal.jsx`.
- [ ] **Step 3: Verify in dev.** Open `/owner/subcontractor-tasks`: On-Hold tasks (sct_005, sct_009) show distinct On-Hold styling (not "Assigned"); the project column shows a sensible, matching project.
- [ ] **Step 4: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 5: Commit.**
```bash
git add src/data/mockStore.jsx src/pages/owner/SubcontractorTasksPage.jsx src/components/subcontractor/TaskViewModal.jsx
git commit -m "fix(data): match subcontractor tasks to real projects; style On Hold status"
```
---
## Task 9: Add structured `value` to kanban leads + fix Complete-stage data
**Files:**
- Modify: `src/data/mockStore.jsx``KANBAN_LEADS_INITIAL` and `KANBAN_PROJECT_DATA`.
- [ ] **Step 1: Add a numeric `value`** (estimate $) to **every** `kl_*` lead. For signed+ leads reuse the existing `estimatedAmount` from `KANBAN_PROJECT_DATA`; for earlier-stage leads assign realistic varied amounts ($6k$42k).
- [ ] **Step 2: Fix Complete-stage leads** (kl_034038): set their project `completionPct: 100` and a real `actualCost` (≈ their estimate), since they're in the Complete column.
- [ ] **Step 3: Populate `stormSource: true`** on storm-canvassed kanban leads (those whose notes mention storm/hail).
- [ ] **Step 4: Verify in dev.** Open the Kanban board: cards render; storm badges appear on storm leads; Complete column leads show 100%.
- [ ] **Step 5: Verify build.** Run: `npm run build` — Expected: `✓ built`.
- [ ] **Step 6: Commit.**
```bash
git add src/data/mockStore.jsx
git commit -m "feat(data): add lead values, fix Complete-stage data, populate stormSource on kanban leads"
```
---
## Self-review checklist (run after all tasks)
- [ ] `git grep -nE "owner_00[12]|\bADM0[123]\b|FA00[12345]"` returns **no matches** in `src/data/mockStore.jsx`.
- [ ] `own_002` is "Diana Reeves" everywhere (`git grep -n "Maria Garcia"` → no matches).
- [ ] Every project with a `budgetBreakdown` reconciles (column sums == totals).
- [ ] `npm run build` is clean.
- [ ] Dev run: Leads page non-empty; each `sub_00N` login sees its own tasks; On-Hold styled; no "Unknown" person labels on subcontractor tasks.
## Spec coverage note
This plan covers spec Phases 13. Phases 46 (threaded scenarios, pipeline breadth, computed analytics) and 710 (lifecycle/inspections, sub-task stage machine, profiles) are separate plans authored after this one lands.