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

21 KiB
Raw Blame History

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 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, 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):

// 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:
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.
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={{...}}):

// 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.
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:

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.jsxMOCK_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:

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 — 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 amounts / 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 allocatedbudget. (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 — 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.

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_002005)

Files:

  • Modify: src/data/mockStore.jsxMOCK_USERS.

  • Step 1: Add four login user records to MOCK_USERS for sub_002sub_005, mirroring the sub_001 (Carlos Mendoza) record shape (id, name, email, empId, password: 'password', role: 'SUBCONTRACTOR', type subcontractor). 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.jsxMOCK_SUBCONTRACTOR_TASKS (projectId/location).

  • Modify: src/pages/owner/SubcontractorTasksPage.jsxSTATUS_CONFIG.

  • Modify: src/components/subcontractor/TaskViewModal.jsxSTATUS_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.

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.jsxKANBAN_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.

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, and MOCK_SUBCONTRACTORS, for each person in the Canonical Roster set the name to the roster name and set empId to the roster LUP-####. Reconcile Justin's email to justin@lynkeduppro.com. Keep password password.
  • 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 sets loginType, 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 by empId (LUP-####) and owner/contractor/sub by username. If MOCK_USERS records carry the LUP empId (set in Task R / Task 7), update the match in AuthContext.jsx only 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 in src/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_002 is "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 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.