Files
LynkedUpPro_CRM/docs/superpowers/plans/2026-05-29-plan2-unify-routing.md
T

10 KiB
Raw Blame History

Plan 2 — Unify Lead↔Project + Routing/Naming

For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development. Steps use checkbox (- [ ]). Follow the execution protocol (2026-05-29-execution-protocol.md) — parallel only where lanes are disjoint.

Goal: Make a won kanban lead the same entity as a project — one data model, one rich detail page, clean /…/projects/:id routes, and unified PRJ-2026-### ids.

Architecture: Migrate proj_###PRJ-2026-### (with inbound FKs), convert the 16 won kanban leads (kl_023kl_038) into full projects records (normalizing field names + back-filling missing project fields) linked back to their kanban lead, route all won jobs to the existing rich OwnerProjectDetail under owner/admin/field-agent prefixes, and relink the kanban drawer + project list. Pre-sale leads (kl_001022, 039044) stay as leads on the lighter LeadProjectPage.

Tech stack: React 18, Vite 7, mock context store, pnpm. No test harness → verify via npm run build + grep/reconciliation + dev-run.

Spec/master: docs/superpowers/specs/2026-05-29-demo-data-coherence-design.md, docs/superpowers/2026-05-29-MASTER-PLAN.md.


Lane / ownership map (for safe parallelism)

Task Lane (exclusive files) Phase
T1 Id migration src/data/mockStore.jsx A (sequential)
T2 Convert won leads → projects src/data/mockStore.jsx A (sequential, after T1)
T3 Detail page role access src/pages/owner/OwnerProjectDetail.jsx B (parallel)
T4 Routes src/App.jsx B (parallel)
T5 Kanban relink src/components/kanban/LeadInfoDrawer.jsx, src/pages/KanbanPage.jsx B (parallel)
T6 Project list relink src/pages/owner/OwnerProjectList.jsx B (parallel)
T7 Verify (read-only) C

Phase A (T1, T2) is strictly sequential — both edit mockStore.jsx. Phase B (T3T6) may run in parallel — disjoint files, and all depend only on Phase A being committed. T7 runs last.


Field normalization map (T2 — kanban project data → project schema)

Kanban (KANBAN_PROJECT_DATA / lead) Project schema
estimatedAmount budget and approvedBudget
completionPct completionPercentage
workTimeline[] activityTimeline[] (same item shape)
milestone {date} milestone {dueDate} (keep id/name/status; assignedTo = contractor id)
invoice {description, paidDate} invoice {submittedBy, datePaid} (keep id/amount/status/dueDate; submittedBy = contractor/sub id)
rfi {openedDate, closedDate} rfi {dateOpened, dateClosed}
changeOrder {requestedBy, date} changeOrder {dateSubmitted} (keep id/title/amount/status/description)
contractorName/contractorPhone contractorId (map "Mike Brennan"→con_001; keep others if a different contractor)
lead name/address/phone/email becomes the project's customer/address; keep a customer block

Back-fill (required so the rich tabs aren't broken/empty): ownerId (assign own_001 or own_002), phase, committedCost (≈ between actualCost and budget), spent=actualCost, variancePercent=round((actualCostbudget)/budget×100,1), margin, commissionType/commissionRate/commission, teamMembers[] (≥ sales rep p1/p2 + contractor con_001), subcontractorIds[], riskLog/issueLog (may be []), paymentSchedule[], openRFIs/changeOrderCount/pendingInvoiceCount (computed from the arrays), budgetBreakdown[] (Plan-1 invariants: allocated→budget, committed→committedCost, actual→actualCost, committed≥actual), startDate/endDate, healthScore. Reuse the existing KANBAN_PROJECT_DATA numbers; only fill genuine gaps.


Task 1: Migrate project ids proj_###PRJ-2026-###

Files: src/data/mockStore.jsx only.

  • Step 1: git grep -n "proj_0" -- src/data/mockStore.jsx to enumerate every proj_### — both the projects[].id definitions AND inbound FKs (projectId: 'proj_###') in tasks, schedule, MOCK_SUBCONTRACTOR_TASKS, and anywhere else.
  • Step 2: Replace each proj_001PRJ-2026-001, … proj_012PRJ-2026-012, consistently across the whole file (definitions + all FKs) so every reference still resolves. Use replace_all per id.
  • Step 3 (verify): git grep -n "proj_0" -- src/data/mockStore.jsx → NO matches. Every projectId FK now reads PRJ-2026-###.
  • Step 4: npm run build✓ built, zero errors.
  • Step 5 (reconcile FKs): confirm no subcontractor task / schedule entry references a projectId that doesn't exist in projects (grep the new ids; each FK must match a project id).
  • Step 6: commit git commit -m "refactor(data): migrate project ids to PRJ-2026-### (with inbound FKs)".

Task 2: Convert the 16 won kanban leads into project records

Files: src/data/mockStore.jsx only. Runs after T1.

  • Step 1: Read KANBAN_LEADS_INITIAL for kl_023kl_038 and their KANBAN_PROJECT_DATA entries.
  • Step 2: For each of the 16, append a new object to the projects array with id PRJ-2026-013PRJ-2026-028, mapping fields per the Normalization Map and back-filling per the Back-fill list. Add sourceLeadId: '<kl_id>' to the project, and projectId: '<PRJ id>' to the corresponding kanban lead (so the board links to the project). Keep the kanban lead's columnId (it still shows in Signed/Progress/Complete).
  • Step 3: Assign realistic ownerId (split across own_001/own_002), teamMembers (canonical people), and a contractorId (con_001 unless the data names another). Vary phase/status to match the lead's stage (signed→Scope Approved/Contract Signed, progress→Work In Progress, complete→Completed).
  • Step 4 (reconcile): every new project satisfies Plan-1 financial invariants (Σbreakdown columns == totals; committed≥actual; spent==actualCost; variancePercent sign correct). Write a throwaway node check to /tmp if helpful; do NOT commit it.
  • Step 5: npm run build✓ built, zero errors.
  • Step 6: commit git commit -m "feat(data): convert won kanban leads (kl_023-038) into unified project records".

Task 3: Let owner/admin/field-agent view a project (relax owner-only lookup)

Files: src/pages/owner/OwnerProjectDetail.jsx only. Phase B (parallel).

  • Step 1: Find the lookup projects.find(p => p.id === projectId && p.ownerId === user?.id) (~line 296). Relax it so ADMIN/OWNER/FIELD_AGENT can view: match by id, and only enforce ownerId when user.role === 'OWNER' (owners see their own; admin/field-agent see any). Keep the not-found fallback.
  • Step 2: npm run build✓ built.
  • Step 3: commit git commit -m "fix(projects): allow admin/field-agent to open project detail, not just owner".

Task 4: Add the unified /projects/:projectId route (Option B)

Files: src/App.jsx only. Phase B (parallel).

We adopt the final Option-B scheme for the projects route now — a single, role-prefix-free URL — so won leads and projects share one page. The attribute-based access guard arrives in Phase 8; for the interim, gate with a coarse allowedRoles.

  • Step 1: Add a route /projects/:projectIdOwnerProjectDetail, gated allowedRoles={['OWNER','ADMIN','FIELD_AGENT']} (interim coarse guard — Phase 8 replaces it with the ABAC policy guard). Leave the existing /owner/projects/:projectId route in place for now (Phase 8 removes the duplicate). Do NOT add /admin/projects / /emp/fa/projects role-prefixed copies.
  • Step 2: npm run build✓ built.
  • Step 3: commit git commit -m "feat(routes): add unified /projects/:id route for the shared detail page".

Files: src/components/kanban/LeadInfoDrawer.jsx, src/pages/KanbanPage.jsx. Phase B (parallel).

  • Step 1: In LeadInfoDrawer.jsx (~line 302) the "More Details" button does navigate(\${basePath}/leads/${lead.id}`). Change it so: if the lead has a projectId(won), navigate to the unified`/projects/${lead.projectId}`(no basePath); else keep`${basePath}/leads/${lead.id}`` (pre-sale leads stay role-prefixed until Phase 8).
  • Step 2: Check KanbanPage.jsx for any other lead→detail navigation and apply the same conditional.
  • Step 3: npm run build✓ built.
  • Step 4: commit git commit -m "feat(kanban): won leads open the unified project page; pre-sale leads stay on lead page".

Files: src/pages/owner/OwnerProjectList.jsx only. Phase B (parallel).

  • Step 1: This page lists both construction projects and pipeline leads (links ~lines 280/366 and 499/565). Point ALL project links AND won pipeline leads (those with a projectId) at the unified /projects/:id. Pre-sale leads keep /owner/leads/:id.
  • Step 2: npm run build✓ built.
  • Step 3: commit git commit -m "feat(projects): project list links won pipeline jobs to the unified project page".

Task 7: Verify (read-only)

  • git grep -n "proj_0" -- src → none (ids fully migrated).
  • Every kanban lead kl_023038 has a projectId resolving to a real project; every new project has a sourceLeadId resolving to a kanban lead.
  • All projects (old + new) satisfy Plan-1 financial invariants (dispatch a read-only reviewer to re-check sums, as in Plan 1).
  • npm run build clean.
  • Dev run: from the kanban board, opening a Signed/In-Progress/Complete lead's "More Details" lands on /…/projects/PRJ-2026-### showing the full rich detail (10 tabs); opening a pre-sale lead still shows the lighter lead page. Owner, admin, and field-agent can each open a project.

Self-review checklist

  • No proj_### left anywhere in src.
  • 16 new projects appended; ids PRJ-2026-013..028; each linked to its kanban lead both ways.
  • Financial invariants hold for all ~28 projects.
  • Build clean; won leads route to project page; pre-sale leads route to lead page; all three roles can view.

Notes for Plan 3

After this lands, Plan 3 targets the unified model: kanban depth (realistic details on the pre-sale leads), the 10 curated threaded demo-case scenarios (chosen from the ~28 projects), and computed selectors.js dashboards. The lighter LeadProjectPage is now pre-sale-only; converging it fully into one component is deferred (Phase 4 adds the lead-side extras — stage mover, hail history, progression — onto the unified page).