Files
LynkedUpPro_CRM/docs/superpowers/specs/2026-05-29-lead-verification-design.md
T

88 lines
7.8 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.
# Lead Verification — Design Spec
**Status:** Approved direction (adapted port from `gitea/bugfix/project-refinement`). Feeds an implementation plan.
## Purpose
Add the **pre-pipeline lead-verification stage** the CRM is missing. Inbound leads (door-knock, storm canvass, web form, call center, referral) land in a **verification queue** where a verification/call-center team confirms they're real — identity, insurance, reachability — before they enter the sales pipeline. This is a **B2B-first** internal tool (the roofing company's verification desk), not a customer-facing surface.
It exists on the old lineage (`gitea/bugfix/project-refinement`, commits `567b29c` "adds lead verification module" + `8a4d577`) as a self-contained module. Our `revamp` branch has diverged heavily (Plans 17), so this is an **adapted port** — bring the UI + flow, re-author the data against our canonical model, wire it to our existing `leads` pipeline.
## Why it fits (no duplication)
`verifyLead(id)` in the source module **auto-converts a verified record into a New Lead** pushed onto the `leads` array. That `leads` array is exactly the one we enriched (Plan 3) and made clickable with a detail page (Plan 6). So the chain becomes:
```
Lead Verification queue → (verify) → New Lead in `leads` → Kanban pipeline → Project (PRJ-2026-###)
```
It sits *upstream* of everything we've built and reuses our pipeline as its output. Nothing we built is duplicated or replaced.
## The flow & states
Two status axes (both preserved from the source):
- **Coarse `status`** (drives summary tiles + primary badge): `Pending → Assigned → In Progress → Verified | Unverified`.
- **Granular `verificationStatus`** (the work detail): `Unassigned`, `Pending Outreach`, `Awaiting Confirmation`, `Verifying Identity`, `Reviewing Insurance`, `Emergency — Awaiting Verifier`, `Failed Contact`, `Verified`.
Transitions:
- A new record is `Pending` / `Unassigned`.
- **Assign** to a verifier → `Assigned` / `Pending Outreach`.
- Verifier works it → `In Progress` with a granular `verificationStatus` (Verifying Identity / Reviewing Insurance / etc.).
- **Verify** → `Verified` (record leaves the active queue) **and a New Lead is pushed to `leads`** (linked back via `verificationId`).
- **Unverify** (e.g. Failed Contact) → `Unverified` with a reason note.
- Notes can be added at any point (audit trail).
## Data model (into `src/data/mockStore.jsx`)
- `MOCK_LEAD_VERIFICATIONS` — ~15 records, re-keyed to our world. Per record (shape from source, adapted): `{ id, leadId, customerName, phone, email, address, source, status, verificationStatus, assigneeId, assigneeName, assignedAt, verifiedAt, urgency, verificationNotes, history:[{ at, by, action }] }`.
- **Assignees/verifiers** = our canonical **admins** as the verification desk: a1 Wade Hollis, a2 Darlene Brooks, a3 Roy Schaefer (field agents may also appear). No fake names.
- **Sources** = our real lead sources: Door Knock, Storm Canvass, Web Form, Call Center, Referral.
- **Customers** = realistic Plano homeowners (varied; not the existing pipeline customers — these are *new* inbound).
- A spread across all states so the demo shows every status (a couple Verified, a couple Unverified/Failed Contact, several in-progress/assigned/pending, one "Emergency — Awaiting Verifier").
- `leadVerifications` state + the 6 mutators on the context value:
- `assignLeadVerification(id, assigneeId, assigneeName)` → Assigned / Pending Outreach.
- `reassignLeadVerification(id, assigneeId, assigneeName)`.
- `setLeadVerificationStatus(id, newStatus, note?)`.
- `verifyLead(id)` → marks Verified + appends history + **pushes a New Lead onto `leads`** using OUR enriched lead schema (firstName/lastName, address/city/state, phones:[{number,type,isPrimary}], emails, leadSource, status:'New', createdByName:'Lead Verification', verificationId).
- `unverifyLead(id, note)`.
- `addLeadVerificationNote(id, note)`.
## UI (ported components, adapted imports)
- `src/components/ui/Select.jsx` — the dark-mode-aware select (reusable; also lets us standardise dropdowns). **New shared component.**
- `src/components/LeadVerification/``statusConfig.js`, `SummaryCards.jsx`, `LeadTableRow.jsx`, `LeadCard.jsx`, `LeadStatusBadge.jsx`, `LeadActionsMenu.jsx`, `AssignLeadModal.jsx`, `LeadDetailsModal.jsx`.
- `src/pages/LeadVerification/LeadVerificationPage.jsx` — summary tiles (counts per status) + search + filters (status / source / assignee) + a responsive table (desktop) / cards (mobile) + the assign / reassign / verify / unverify / view modals. Uses `usePermissions` (we already have the hook) for action gating.
## Routing & nav
- Add a **single unified route** `/lead-verification``LeadVerificationPage`, gated `allowedRoles={['OWNER','ADMIN','FIELD_AGENT']}` (interim coarse guard; **Plan 8 ABAC** replaces it). (Per our Plan-2 unified-route direction — not the source's three role-prefixed routes.)
- Add a nav entry ("Lead Verification") for the relevant roles in the sidebar/layout.
## Explicitly NOT ported
- The source's **access-control-matrix / RBAC** changes (commit `7b485a4`) — our **Plan 8 ABAC** supersedes them. Gate with the existing coarse `ProtectedRoute` for now.
- Their old-lineage mockStore wholesale — only the verification slice is ported, re-authored against our canonical roster.
## Grounding requirements (must be real, coherent data — not placeholders)
The source module's `MOCK_LEAD_VERIFICATIONS` is **not** copied verbatim — it is re-authored to our standards:
- **Real people, no fakes.** Verifiers/assignees are the canonical admins (Wade Hollis / Darlene Brooks / Roy Schaefer); customer names are realistic Plano/DFW homeowners (no "John Smith", "Alice Customer", movie characters, or sequential placeholders). Phones use varied real DFW area codes (972/469/214/945) formatted en-US, never `(972) 555-0NNN` sequences.
- **Real places & sources.** Addresses are real Plano street/zip combos; sources are the company's actual channels (Door Knock, Storm Canvass, Web Form, Call Center, Referral). Storm-sourced records should reference the same storm-season framing used elsewhere.
- **Computed, not hardcoded, summaries.** The summary tiles (counts per status) and any totals are derived from the `leadVerifications` array at render — never hardcoded numbers. (Same compute-don't-hardcode rule as the dashboards.)
- **Coherent dates.** `assignedAt``verifiedAt` ≤ DEMO_TODAY (2026-05-29); history timestamps strictly increasing; created dates realistic for an inbound queue.
- **Verify produces a real, coherent lead.** The New Lead pushed onto `leads` matches our enriched schema so it renders correctly in the Leads list AND opens in the lead detail page (Plan 6) with no empty/broken fields.
- **Internally consistent.** A record's `status` and `verificationStatus` agree (e.g. `Verified` coarse ↔ `Verified` granular; `In Progress` ↔ a working granular state); an `Assigned`/`In Progress` record has a real assignee; `Unassigned` has none.
## Acceptance
- Build clean (0 duplicate-key warnings).
- The page lists 15 verification records with correct per-status summary tiles; filters (status/source/assignee) work; assignees are canonical admins (no fake names).
- **Verify** a record → it leaves the active queue as Verified AND a matching New Lead appears in the Leads list (`/…/leads`) with `createdByName: 'Lead Verification'`; the new lead opens in the lead detail page (Plan 6).
- Assign / reassign / set-status / unverify / add-note all persist in session and reflect in the table + badges.
- `/lead-verification` reachable from nav for OWNER/ADMIN/FIELD_AGENT.
## Reuse / dependencies
- `usePermissions` hook — already present.
- Our enriched `leads` schema (Plan 3/6) — the verify output target.
- `components/ui/Select` — newly ported; reusable across the app.