21 KiB
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 1–3).
Scope of this plan: Phase 1 (canonical identity), Phase 2 (financial coherence), Phase 3 (feature-breaking fixes). Phases 4–10 are separate plans.
Canonical roster (single source of truth — used throughout)
Canonical id scheme = MOCK_USERS. Real Texan names. Unified LUP-#### empIds. Shared password password.
| id | Role | Name | empId | Login type | Login id | Old id(s) | Old name |
|---|---|---|---|---|---|---|---|
| own_001 | OWNER | Justin Johnson | LUP-1001 | owner | justin | owner_001 |
— (email → justin@lynkeduppro.com) |
| own_002 | OWNER | Diana Reeves | LUP-1002 | owner | diana | owner_002 |
"Maria Garcia" |
| a1 | ADMIN | Wade Hollis | LUP-1010 | employee | LUP-1010 | ADM01 |
"Adam Admin" / "Admin One" |
| a2 | ADMIN | Darlene Brooks | LUP-1011 | employee | LUP-1011 | ADM02 |
"Amanda Manager" / "Admin Two" |
| a3 | ADMIN | Roy Schaefer | LUP-1012 | employee | LUP-1012 | ADM03 |
"Arthur Director" / "Admin Three" |
| p1 | SALES_REP | Jesus Gonzales | LUP-1030 | employee | LUP-1030 | — | — (keep) |
| p2 | SALES_REP | Sarah Calloway | LUP-1031 | employee | LUP-1031 | — | "Sarah Sales" |
| e1 | FIELD_AGENT | Cody Tatum | LUP-1040 | employee | LUP-1040 | FA001 |
"Frank Agent" |
| e2 | FIELD_AGENT | Hannah Reyes | LUP-1041 | employee | LUP-1041 | FA002 |
"Fiona Field" |
| e3 | FIELD_AGENT | Travis Boone | LUP-1042 | employee | LUP-1042 | FA003 |
"Fred Flyer" |
| e4 | FIELD_AGENT | Shelby Greer | LUP-1043 | employee | LUP-1043 | FA004 |
"Felicity Fast" |
| e5 | FIELD_AGENT | Dalton Pruitt | LUP-1044 | employee | LUP-1044 | FA005 |
"Felix Fixer" |
| con_001 | CONTRACTOR | Mike Brennan | LUP-3001 | contractor | mike | — | "Mike Contractor" |
| sub_001 | SUBCONTRACTOR | Carlos Mendoza | LUP-2001 | subcontractor | carlos | — | "Carlos Subcontractor" |
| sub_002 | SUBCONTRACTOR | Maya Patel | LUP-2002 | subcontractor | maya | — | (keep) |
| sub_003 | SUBCONTRACTOR | Dwayne Boudreaux | LUP-2003 | subcontractor | dwayne | — | (current sub_003 name) |
| sub_004 | SUBCONTRACTOR | Jennifer Castillo | LUP-2004 | subcontractor | jennifer | — | (current sub_004 name) |
| sub_005 | SUBCONTRACTOR | Greg Alston | LUP-2005 | subcontractor | greg | — | (keep) |
Org role taxonomy (orgMembers[].roleKey) stays; only the userId it points to changes. Email rule: use @lynkeduppro.com consistently.
Execution order (overrides plain numeric order; see execution protocol): 1 → 2 → R (roster rename, below) → 3 → 4 → 5 → 6 → 7 → 8 → 9 → L (login picker, below).
File structure
- Modify:
src/data/mockStore.jsx— identity remap,resolvePerson, financial fixes, breakdowns,leadsseed, sub logins, task↔project fixes, kanban lead values,DEMO_TODAY. - Modify:
src/pages/owner/SubcontractorTasksPage.jsx,src/components/subcontractor/TaskViewModal.jsx— addOn Holdstyling. - 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):
// 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.jsxreplace the hardcodednew Date('2026-04-07')(line ~14) with an import + use:
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:✓ builtwith no errors. - Step 4: Commit.
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 +resolvePersonexposed on the store context value. -
Step 1: Build a merged people index inside the provider (after the people collections are defined, before the
value={{...}}):
// 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 thevalue={{ ... }}). - Step 3: Verify build. Run:
npm run build— Expected:✓ built. - Step 4: Commit.
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— theorgMembersuseState array. -
Step 1: Apply the ID + name remap in
orgMembersusing the Canonical ID reference table:userId: 'owner_001'→'own_001'(name "Justin Johnson", emailjustin@lynkeduppro.com)userId: 'owner_002'→'own_002', name "Maria Garcia" → "Diana Reeves", email → Diana's canonical email fromMOCK_USERSuserId: '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:
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.
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(andMOCK_NOTIFICATIONSactor 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/senderNameso "Admin One" → "Adam Admin" and any owner name → "Justin Johnson".
-
Step 2: Verify no stale ids remain anywhere. Run:
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.
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— theprojectsarray.
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). RecomputevariancePercent = (14200−28000)/28000×100 = -49.3. - Step 2: proj_008 — reduce paid invoices so the paid total ≤
actualCost(44500). Adjust the invoiceamounts /statussoΣ paid ≤ 44500andΣ 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 socommitted ≥ actual(raise committed to ≥ 11500, keeping the over-budget total intact). - Step 5: Add
budgetBreakdownto proj_006, 008, 009, 010, 011, 012. Each: 4–7 line items whereallocatedsums to the projectbudget,committedsums tocommittedCost,actualsums toactualCost, andcommitted ≥ actualper 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 storedvariancePercentsign matches(actualCost−budget).) - 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 equalcommittedCost/actualCostandallocated→budget. (Manual read or a scratch node script; do not commit the script.) - Step 9: Commit.
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— theleadsuseState (currentlyuseState([])). -
Step 1: Inspect the shape the page reads. Open
src/pages/LeadsListPage.jsxand note the fields used (firstName/lastName,phones[],urgency,status,leadSource,canvasserName,createdByName,createdAt,stormSource,address, etc.). -
Step 2: Seed ~28–35 records by initializing
leadsfrom the existing storm-attribution leads (MOCK_STORM_ATTRIBUTION_LEADS), mapped into the LeadsListPage shape. Use canonicalcanvasserName/createdByName(Frank Agent, Fiona Field, … — names that exist inMOCK_USERS). SpreadcreatedAtacross recent dates relative toDEMO_TODAY; setstormSource: trueon storm-sourced ones; varyurgency/status.
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.
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_002–005)
Files:
-
Modify:
src/data/mockStore.jsx—MOCK_USERS. -
Step 1: Add four login user records to
MOCK_USERSforsub_002–sub_005, mirroring thesub_001(Carlos Mendoza) record shape (id,name,email,empId,password: 'password',role: 'SUBCONTRACTOR', typesubcontractor). Use the canonical roster names + empIds (Maya Patel/LUP-2002, Dwayne Boudreaux/LUP-2003, Jennifer Castillo/LUP-2004, Greg Alston/LUP-2005) and usernames (maya/dwayne/jennifer/greg) so ids/names match across collections and the login picker works. -
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.
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
projectIdreferences a project whoseaddressmatches the tasklocation(residential roofing/electrical tasks → residential projects, not the commercialproj_011/proj_012). Fix the 6 mismatches the audit flagged (sct_004, _005, _006, _007, _008, _010). -
Step 2: Add
On HoldtoSTATUS_CONFIGinSubcontractorTasksPage.jsx(give it an icon + amber/grey styling, consistent with siblings) and toSTATUS_STYLESinTaskViewModal.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.
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_INITIALandKANBAN_PROJECT_DATA. -
Step 1: Add a numeric
value(estimate $) to everykl_*lead. For signed+ leads reuse the existingestimatedAmountfromKANBAN_PROJECT_DATA; for earlier-stage leads assign realistic varied amounts ($6k–$42k). -
Step 2: Fix Complete-stage leads (kl_034–038): set their project
completionPct: 100and a realactualCost(≈ their estimate), since they're in the Complete column. -
Step 3: Populate
stormSource: trueon 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.
git add src/data/mockStore.jsx
git commit -m "feat(data): add lead values, fix Complete-stage data, populate stormSource on kanban leads"
Task R: Canonical roster rename (real Texan names + LUP empIds)
Runs after Task 2, before Task 3. Files: src/data/mockStore.jsx (people source records) + repo-wide name-string replacement.
- Step 1: Set names + empIds on source records. In
MOCK_USERS,MOCK_PERSONNEL/personnel,MOCK_OWNERS, andMOCK_SUBCONTRACTORS, for each person in the Canonical Roster set thenameto the roster name and setempIdto the rosterLUP-####. Reconcile Justin's email tojustin@lynkeduppro.com. Keep passwordpassword. - Step 2: Repo-wide name replace. Replace every occurrence of each old name string with its new name across the whole repo (covers denormalized copies in teamMembers, subcontractor tasks, kanban activity,
DISPATCH_REPS, and any component). Pairs:- "Frank Agent"→"Cody Tatum", "Fiona Field"→"Hannah Reyes", "Fred Flyer"→"Travis Boone", "Felicity Fast"→"Shelby Greer", "Felix Fixer"→"Dalton Pruitt"
- "Adam Admin"→"Wade Hollis", "Amanda Manager"→"Darlene Brooks", "Arthur Director"→"Roy Schaefer"
- "Admin One"→"Wade Hollis", "Admin Two"→"Darlene Brooks", "Admin Three"→"Roy Schaefer"
- "Maria Garcia"→"Diana Reeves", "Sarah Sales"→"Sarah Calloway"
- "Carlos Subcontractor"→"Carlos Mendoza", "Mike Contractor"→"Mike Brennan"
- sub_003 current name → "Dwayne Boudreaux", sub_004 current name → "Jennifer Castillo" (look up the current strings first via
git grep).
- Step 3: Verify old names gone. Run:
git grep -nE "Frank Agent|Fiona Field|Fred Flyer|Felicity Fast|Felix Fixer|Adam Admin|Amanda Manager|Arthur Director|Admin (One|Two|Three)|Maria Garcia|Sarah Sales|Carlos Subcontractor|Mike Contractor" -- src
Expected: no matches.
- Step 4: Verify build. Run:
npm run build— Expected:✓ built. - Step 5: Commit.
git add -A
git commit -m "feat(data): canonical roster — real Texan names + unified LUP-#### employee ids"
Task L: Login per-role person picker
Runs last. Files: src/pages/Login.jsx; src/context/AuthContext.jsx (only if id matching needs updating).
- Step 1: Inline demo-account list in
Login.jsx(keeps this task's lane to Login.jsx). Define from the roster:
const DEMO_ACCOUNTS = {
customer: [{ name: 'Alice Carter', loginType: 'customer', id: 'alice', pw: 'password' }],
employee: [
{ name: 'Wade Hollis (Admin)', loginType: 'employee', id: 'LUP-1010', pw: 'password' },
{ name: 'Darlene Brooks (Admin)',loginType: 'employee', id: 'LUP-1011', pw: 'password' },
{ name: 'Roy Schaefer (Admin)', loginType: 'employee', id: 'LUP-1012', pw: 'password' },
{ name: 'Cody Tatum (Agent)', loginType: 'employee', id: 'LUP-1040', pw: 'password' },
{ name: 'Hannah Reyes (Agent)', loginType: 'employee', id: 'LUP-1041', pw: 'password' },
{ name: 'Travis Boone (Agent)', loginType: 'employee', id: 'LUP-1042', pw: 'password' },
{ name: 'Shelby Greer (Agent)', loginType: 'employee', id: 'LUP-1043', pw: 'password' },
{ name: 'Dalton Pruitt (Agent)', loginType: 'employee', id: 'LUP-1044', pw: 'password' },
],
owner: [
{ name: 'Justin Johnson', loginType: 'owner', id: 'justin', pw: 'password' },
{ name: 'Diana Reeves', loginType: 'owner', id: 'diana', pw: 'password' },
],
contractor: [{ name: 'Mike Brennan', loginType: 'contractor', id: 'mike', pw: 'password' }],
subcontractor: [
{ name: 'Carlos Mendoza', loginType: 'subcontractor', id: 'carlos', pw: 'password' },
{ name: 'Maya Patel', loginType: 'subcontractor', id: 'maya', pw: 'password' },
{ name: 'Dwayne Boudreaux', loginType: 'subcontractor', id: 'dwayne', pw: 'password' },
{ name: 'Jennifer Castillo',loginType: 'subcontractor', id: 'jennifer', pw: 'password' },
{ name: 'Greg Alston', loginType: 'subcontractor', id: 'greg', pw: 'password' },
],
};
- Step 2: Replace the quick-access row so each role chip, when clicked, expands the list of people for that role; clicking a person calls a
pickDemo(acct)that setsloginType,identifier,password(and may submit). Single-person roles act as one-click. - Step 3: Ensure AuthContext accepts the ids. Confirm
login(identifier, password, loginType)matches employees byempId(LUP-####) and owner/contractor/sub by username. IfMOCK_USERSrecords carry the LUPempId(set in Task R / Task 7), update the match inAuthContext.jsxonly if needed. - Step 4: Verify in dev. Log in via the picker as an Admin (LUP-1010), an Agent (LUP-1041), the second Owner (Diana), and a Subcontractor (Maya). Each lands on the right dashboard.
- Step 5: Verify build. Run:
npm run build— Expected:✓ built. - Step 6: Commit.
git add src/pages/Login.jsx src/context/AuthContext.jsx
git commit -m "feat(login): per-role person picker for quick demo access"
Self-review checklist (run after all tasks)
git grep -nE "owner_00[12]|\bADM0[123]\b|FA00[12345]"returns no matches insrc/data/mockStore.jsx.- All old placeholder names gone (the Task R Step 3 grep → no matches); roster names appear in projects/tables/schedule/dispatch/leaderboard.
own_002is "Diana Reeves" everywhere (git grep -n "Maria Garcia"→ no matches).- Login picker: signing in as an Admin (LUP-1010), Agent (LUP-1041), Owner #2 (Diana), and a Sub (Maya) each lands on the correct dashboard.
- Every project with a
budgetBreakdownreconciles (column sums == totals). npm run buildis clean.- Dev run: Leads page non-empty; each
sub_00Nlogin sees its own tasks; On-Hold styled; no "Unknown" person labels on subcontractor tasks.
Spec coverage note
This plan covers spec Phases 1–3. Phases 4–6 (threaded scenarios, pipeline breadth, computed analytics) and 7–10 (lifecycle/inspections, sub-task stage machine, profiles) are separate plans authored after this one lands.