9.6 KiB
Demo Data Coherence & Simulation Depth — Design Spec
Date: 2026-05-29 Branch: revamp Goal: Turn the CRM's mock data into one coherent fake roofing company ("LynkedUp Pro Roofing") that works as both a live sales demo (believable, no empty/broken views, numbers that feel real) and an investor pitch (breadth, depth, analytics wow). Coherence end-to-end is the wow: drilling from any dashboard KPI lands on the exact records that compose it.
Approach: A — Canonical-company foundation, then layer up. Analytics are computed from the canonical data (with one curated exception: purely-visual long-range trend lines).
1. Canonical identity (foundation)
Problem (from audit): the same people exist under three incompatible ID schemes, so cross-module lookups silently fail; Owner #2 has two names; admins have placeholder names; subcontractor tasks reference non-existent ids (owner_001, ADM01).
Decision: unify on the MOCK_USERS scheme (already used by auth and projects):
- Owners:
own_001(Justin Johnson),own_002(Diana Reeves) - Admins:
a1(Adam Admin),a2(Amanda Manager),a3(Arthur Director) - Field agents / canvassers:
e1–e5(Frank Agent, Fiona Field, Fred Flyer, Felicity Fast, Felix Fixer) - Sales reps / personnel:
p1(Jesus Gonzales),p2(Sarah Sales) - Contractor:
con_001(Mike Contractor); Subcontractors:sub_001–sub_005
Changes:
- Remap
orgMembers[].userId:owner_001→own_001,owner_002→own_002,FA00N→eN,ADM0N→aN. KeeporgMembers[].roleKeyas the org-role taxonomy (OWNER/ADMIN/SALES_REP/CANVASSER/SUBCONTRACTOR). - Reconcile names/emails:
own_002= Diana Reeves everywhere; admins = real names (not "Admin One/Two/Three"); Justin's email consistent (justin@lynkeduppro.com). - Remap subcontractor-task references:
assignedBy/actorId/senderIdowner_001→own_001,ADM01→a1; correct the denormalized*Namestrings to match. - Add a
resolvePerson(id)selector in the store (returns{ id, name, email, role, avatar? }from a single merged index) so every module resolves people the same way.
Acceptance: no reference to owner_001/FA0*/ADM0* as an id anywhere; every userId/assignedBy/teamMembers[].userId/ownerId/contractorId/subcontractorIds[] resolves via resolvePerson/the relevant collection; own_002 shows one name across all screens.
2. Coherence fixes (the four approved tiers)
2a. Feature-breaking data
- Leads List page: seed the store's
leadsarray (currentlyuseState([])) with ~28–35 records in the page's shape (firstName/lastName,phones[],urgency,status,leadSource,canvasserName,createdByName,createdAt,stormSource). Source = the existing storm-attribution leads, expanded. Page must never show the empty state on load. - Subcontractor access: add login user records for
sub_002–sub_005(typesubcontractor) so each subcontractor's tasks/notifications are reachable. Keep existing task ownership. - Task → project association: every subcontractor task points to a project whose
addressmatches the tasklocation(residential roofing/electrical tasks → residential projects, not the commercial facade/hotel projects). FixSTATUS_CONFIG/STATUS_STYLESto includeOn Hold.
2b. Financial coherence
- proj_008: reduce paid invoices so paid ≤
committedCost(52,000) and ≤actualCost(44,500). - proj_005: set
committedCostto the breakdown's real sum 28,850 (work was committed; stored total was stale); verify variance/margin recompute. - Completed projects (proj_002, proj_010): paid invoices ≤
actualCost(no overpay vs spend). - proj_004: fix the contingency line where
committed < actual; keep it as the deliberate over-budget case. - Missing
budgetBreakdown: add coherent breakdowns to proj_006, 008, 009, 010, 011, 012 —allocatedsums to budget,committedsums tocommittedCost,actualsums toactualCost,committed ≥ actualper line.
2c. Pipeline / Kanban
- Add a structured numeric
value(estimate) to every kanban lead (not just signed+), so a pipeline $ total and weighted funnel can be computed. - Fix Complete-stage leads (kl_034–038): real
actualCostandcompletionPct: 100. - Re-anchor the hardcoded "today" (
KanbanCard.jsx:14) — derive from a singleDEMO_TODAYconstant (= a fixed recent date) used app-wide, orDate.now(); dates stay ordered and not-future relative to it. - Populate
stormSource: trueon storm-canvassed leads so the wired badge shows. - kl_044 (Signed) gets a project record or moves to a pre-signed stage.
3. Five–six fully-threaded scenario projects (real-world spread)
Instead of one hero, 6 projects are threaded end-to-end across modules, each representing a distinct real-world case. Every thread shares consistent identities, addresses, and dates from lead → project → subcontractor task → invoices/payments → commission.
| # | Project | Scenario it demonstrates | Thread highlights |
|---|---|---|---|
| proj_001 | Roof Replacement (active, healthy) | Storm-driven win, on track | Storm lead (Frank/e1 canvasses) → Jesus/p1 closes → active project, Carlos/sub_001 electrical task → progress invoices → commission accrues |
| proj_004 | Interior Reno (over budget) | Problem job / margin loss | Scope-creep change orders → costs exceed budget → at-risk health → owner sees negative net profit |
| proj_007 | Commercial Roof (large, active) | Big commercial, multi-phase | Payment schedule by phase, multiple subs, larger commission |
| proj_008 | Foundation (88%, healthy) | Near-complete, healthy | Most milestones done, final invoices pending, payout imminent |
| proj_010 | Parking Lot (completed) | Closed-won, full lifecycle | Lead won months ago → completed → all invoices paid → commission paid out |
| proj_011 | Facade/Disputed | Disputed / on-hold | Dispute timeline, paused payments, low health score |
Each thread requires: a matching lead (pipeline stage consistent with project status; stormSource where relevant), at least one subcontractor task (address-matched), invoices + paymentSchedule that reconcile with actualCost, a commission config + (for completed) payout, and consistent team/owner/dates.
4. Deeper project detail tables
Every project carries the full coherent set so no tab is empty: budgetBreakdown, changeOrders, rfis, riskLog, issueLog, activityTimeline, milestones, invoices, paymentSchedule, documents, teamMembers. Thin/empty arrays get realistic content sized to the project's status and scenario.
5. Broader pipeline + leaderboard
- More leads spread realistically across stages with $ values and storm attribution.
- Canvasser leaderboard stats derive from the leads each agent sourced (knocks → leads → won), so the leaderboard ties to the pipeline.
6. Computed analytics (investor wow)
Add a src/data/selectors.js (pure functions over store arrays) deriving:
- Pipeline: total value, weighted value by stage, conversion funnel counts, win rate.
- Revenue/profit: total contract value, gross/net profit, margin — summed from projects (matching
OwnerProjectDetail's own formulas). - Commission: total accrued/paid by rep, by project.
- Subcontractor performance: tasks by sub, on-time %, total paid.
- Storm attribution: leads/revenue traced to storm events.
Wire these into the owner/admin dashboards (OwnerSnapshot, Dashboard, leaderboard) replacing hardcoded figures, so KPIs tie out with detail screens. Curated exception: purely-visual long-range trend charts (e.g., 12-month revenue line) where no daily source data exists — these stay hand-authored and are labeled as illustrative.
Acceptance: the owner dashboard's headline revenue/profit/pipeline numbers equal the sums of the underlying project/lead records (verified by a quick selector unit check).
Components / files touched
src/data/mockStore.jsx— identity remap, leads seed, financial fixes, breakdowns, threaded scenarios, sub logins, deeper tables, pipeline values. This file is large (~6,950 lines); changes are additive/surgical, not a rewrite.src/data/selectors.js— new, computed analytics.- Dashboards/components consuming analytics:
src/pages/owner/OwnerSnapshot.jsx,src/pages/Dashboard.jsx, leaderboard page,src/components/kanban/KanbanCard.jsx(date anchor),SubcontractorTasksPage.jsx/TaskViewModal.jsx(On Hold styling).
Out of scope (YAGNI)
- No backend/persistence changes (mock store only).
- No new routes/pages beyond what exists.
- No visual redesign of components — data + selectors + minimal wiring only.
- The dormant
project.marginfield stays but is made honest; not wired into new UI.
Build sequencing
- Canonical identity +
resolvePerson(everything depends on it). - Financial coherence + missing breakdowns (so projects reconcile).
- Feature-breaking fixes (leads seed, sub logins, task↔project).
- Threaded scenarios (lead↔project↔task↔invoice↔commission).
- Pipeline breadth + leaderboard.
selectors.js+ dashboard wiring.- Verify build + spot-check reconciliation.
Verification
npm run buildclean after each major step.- Reconciliation spot-checks: dashboard revenue == Σ project contract values; pipeline total == Σ lead values; a threaded scenario navigable lead→project→task→invoice→commission with consistent names/dates.
- Dev run: Leads page non-empty; each subcontractor login sees their tasks; no "Unknown"/empty owner labels.