feat(data): lead-verification queue + mutators (verify pushes a grounded New Lead into the pipeline)

This commit is contained in:
Satyam Rastogi
2026-05-29 21:05:34 +05:30
parent 25879866ff
commit 56f6075ceb
+481
View File
@@ -6899,6 +6899,327 @@ export const SUBCONTRACTOR_FEE_TYPES = [
'Other',
];
// ---------------------------------------------------------------------------
// LEAD VERIFICATION QUEUE
// ---------------------------------------------------------------------------
// Admins: a1 Wade Hollis · a2 Darlene Brooks · a3 Roy Schaefer
// DEMO_TODAY = 2026-05-29
const MOCK_LEAD_VERIFICATIONS = [
{
id: 'lv_001', leadId: 'LD-V-001',
customerName: 'Kevin Hartley',
phone: '(972) 413-8902',
email: 'kevin.hartley@gmail.com',
address: '2814 Ravenswood Dr, Plano, TX 75023',
source: 'Door Knock',
status: 'Verified',
verificationStatus: 'Verified',
assigneeId: 'a1', assigneeName: 'Wade Hollis',
urgency: 'standard',
createdAt: '2026-05-14T09:10:00Z',
assignedAt: '2026-05-14T09:40:00Z',
verifiedAt: '2026-05-16T14:25:00Z',
verificationNotes: 'Ownership confirmed via county records. Hail damage from April 28 storm event verified.',
history: [
{ at: '2026-05-14T09:10:00Z', by: 'System', action: 'Lead submitted via door knock intake.' },
{ at: '2026-05-14T09:40:00Z', by: 'Wade Hollis', action: 'Assigned to Wade Hollis.' },
{ at: '2026-05-15T08:30:00Z', by: 'Wade Hollis', action: 'First contact made — homeowner confirmed damage.' },
{ at: '2026-05-16T14:25:00Z', by: 'Wade Hollis', action: 'Verified and pushed to New Leads.' },
],
},
{
id: 'lv_002', leadId: 'LD-V-002',
customerName: 'Sandra Nguyen',
phone: '(469) 551-7034',
email: 'sandra.nguyen@yahoo.com',
address: '4817 Shady Brook Ln, Plano, TX 75093',
source: 'Web Form',
status: 'Verified',
verificationStatus: 'Verified',
assigneeId: 'a2', assigneeName: 'Darlene Brooks',
urgency: 'standard',
createdAt: '2026-05-17T13:45:00Z',
assignedAt: '2026-05-17T14:15:00Z',
verifiedAt: '2026-05-19T11:00:00Z',
verificationNotes: 'Identity and property ownership confirmed. Adjuster appointment already scheduled.',
history: [
{ at: '2026-05-17T13:45:00Z', by: 'System', action: 'Lead submitted via website form.' },
{ at: '2026-05-17T14:15:00Z', by: 'Darlene Brooks', action: 'Assigned to Darlene Brooks.' },
{ at: '2026-05-18T09:00:00Z', by: 'Darlene Brooks', action: 'Marked In Progress — reviewing insurance docs.' },
{ at: '2026-05-19T11:00:00Z', by: 'Darlene Brooks', action: 'Verified and pushed to New Leads.' },
],
},
{
id: 'lv_003', leadId: 'LD-V-003',
customerName: 'Marcus Trevino',
phone: '(214) 837-4561',
email: 'marcus.trevino@outlook.com',
address: '1234 Oak Creek Blvd, Plano, TX 75075',
source: 'Storm Canvass',
status: 'In Progress',
verificationStatus: 'Verifying Identity',
assigneeId: 'a1', assigneeName: 'Wade Hollis',
urgency: 'standard',
createdAt: '2026-05-22T11:20:00Z',
assignedAt: '2026-05-22T12:00:00Z',
verifiedAt: null,
verificationNotes: 'Storm canvass capture. Cross-checking ownership records with county appraisal district.',
history: [
{ at: '2026-05-22T11:20:00Z', by: 'System', action: 'Lead captured during storm canvass.' },
{ at: '2026-05-22T12:00:00Z', by: 'Wade Hollis', action: 'Assigned to Wade Hollis.' },
{ at: '2026-05-23T09:15:00Z', by: 'Wade Hollis', action: 'Marked In Progress — verifying identity via CAD records.' },
],
},
{
id: 'lv_004', leadId: 'LD-V-004',
customerName: 'Brenda Kowalski',
phone: '(972) 604-2817',
email: 'b.kowalski@gmail.com',
address: '890 Custer Rd, Plano, TX 75075',
source: 'Referral',
status: 'In Progress',
verificationStatus: 'Reviewing Insurance',
assigneeId: 'a3', assigneeName: 'Roy Schaefer',
urgency: 'high',
createdAt: '2026-05-20T08:30:00Z',
assignedAt: '2026-05-20T09:00:00Z',
verifiedAt: null,
verificationNotes: 'Referred by existing customer. Adjuster supplement under review — large residential loss.',
history: [
{ at: '2026-05-20T08:30:00Z', by: 'System', action: 'Referral submitted by existing customer.' },
{ at: '2026-05-20T09:00:00Z', by: 'Roy Schaefer', action: 'Assigned to Roy Schaefer.' },
{ at: '2026-05-21T10:45:00Z', by: 'Roy Schaefer', action: 'Marked In Progress — reviewing adjuster supplement.' },
],
},
{
id: 'lv_005', leadId: 'LD-V-005',
customerName: 'Diane Castillo',
phone: '(469) 228-6390',
email: 'diane.castillo@icloud.com',
address: '321 Independence Pkwy, Plano, TX 75023',
source: 'Call Center',
status: 'Assigned',
verificationStatus: 'Pending Outreach',
assigneeId: 'a2', assigneeName: 'Darlene Brooks',
urgency: 'standard',
createdAt: '2026-05-25T14:00:00Z',
assignedAt: '2026-05-25T14:30:00Z',
verifiedAt: null,
verificationNotes: 'Inbound call center lead. First outreach attempt scheduled for tomorrow morning.',
history: [
{ at: '2026-05-25T14:00:00Z', by: 'System', action: 'Lead created from call center intake.' },
{ at: '2026-05-25T14:30:00Z', by: 'Darlene Brooks', action: 'Assigned to Darlene Brooks.' },
],
},
{
id: 'lv_006', leadId: 'LD-V-006',
customerName: 'Gerald Hoffmann',
phone: '(972) 386-9140',
email: 'gerald.hoffmann@yahoo.com',
address: '7654 Preston Rd, Plano, TX 75024',
source: 'Storm Canvass',
status: 'Assigned',
verificationStatus: 'Pending Outreach',
assigneeId: 'a3', assigneeName: 'Roy Schaefer',
urgency: 'standard',
createdAt: '2026-05-26T09:05:00Z',
assignedAt: '2026-05-26T09:40:00Z',
verifiedAt: null,
verificationNotes: 'Storm canvass — visible hail damage on south slope. Assigned for morning call.',
history: [
{ at: '2026-05-26T09:05:00Z', by: 'System', action: 'Lead captured during storm canvass.' },
{ at: '2026-05-26T09:40:00Z', by: 'Roy Schaefer', action: 'Assigned to Roy Schaefer.' },
],
},
{
id: 'lv_007', leadId: 'LD-V-007',
customerName: 'Lucinda Farrar',
phone: '(214) 509-3762',
email: 'lucinda.farrar@gmail.com',
address: '2200 Alma Dr, Plano, TX 75075',
source: 'Door Knock',
status: 'In Progress',
verificationStatus: 'Awaiting Confirmation',
assigneeId: 'a1', assigneeName: 'Wade Hollis',
urgency: 'standard',
createdAt: '2026-05-21T10:00:00Z',
assignedAt: '2026-05-21T10:30:00Z',
verifiedAt: null,
verificationNotes: 'Homeowner reachable. Confirmed damage but waiting on insurance policy number before proceeding.',
history: [
{ at: '2026-05-21T10:00:00Z', by: 'System', action: 'Lead submitted via door knock intake.' },
{ at: '2026-05-21T10:30:00Z', by: 'Wade Hollis', action: 'Assigned to Wade Hollis.' },
{ at: '2026-05-22T08:00:00Z', by: 'Wade Hollis', action: 'First contact made — awaiting homeowner insurance docs.' },
],
},
{
id: 'lv_008', leadId: 'LD-V-008',
customerName: 'Preston Okafor',
phone: '(945) 762-4830',
email: 'p.okafor@outlook.com',
address: '555 Legacy Dr, Plano, TX 75023',
source: 'Referral',
status: 'Pending',
verificationStatus: 'Unassigned',
assigneeId: null, assigneeName: null,
urgency: 'standard',
createdAt: '2026-05-27T08:15:00Z',
assignedAt: null,
verifiedAt: null,
verificationNotes: 'Referral from verified homeowner on Legacy Dr. Needs verifier assignment.',
history: [
{ at: '2026-05-27T08:15:00Z', by: 'System', action: 'Referral submitted by existing customer.' },
],
},
{
id: 'lv_009', leadId: 'LD-V-009',
customerName: 'Tamara Whitfield',
phone: '(469) 334-6017',
email: 'tamara.whitfield@gmail.com',
address: '3301 Avenue K, Plano, TX 75074',
source: 'Call Center',
status: 'Pending',
verificationStatus: 'Unassigned',
assigneeId: null, assigneeName: null,
urgency: 'standard',
createdAt: '2026-05-28T15:30:00Z',
assignedAt: null,
verifiedAt: null,
verificationNotes: 'Inbound call — homeowner reported significant granule loss. Awaiting verifier assignment.',
history: [
{ at: '2026-05-28T15:30:00Z', by: 'System', action: 'Lead created from call center intake.' },
],
},
{
id: 'lv_010', leadId: 'LD-V-010',
customerName: 'Alicia Drummond',
phone: '(972) 817-5429',
email: 'alicia.drummond@yahoo.com',
address: '6711 W Spring Creek Pkwy, Plano, TX 75024',
source: 'Web Form',
status: 'Pending',
verificationStatus: 'Unassigned',
assigneeId: null, assigneeName: null,
urgency: 'standard',
createdAt: '2026-05-29T07:45:00Z',
assignedAt: null,
verifiedAt: null,
verificationNotes: 'Web form submission this morning. No notes from intake yet.',
history: [
{ at: '2026-05-29T07:45:00Z', by: 'System', action: 'Lead submitted via website form.' },
],
},
{
id: 'lv_011', leadId: 'LD-V-011',
customerName: 'Raymond Ostrowski',
phone: '(214) 673-9045',
email: 'r.ostrowski@gmail.com',
address: '1800 Jupiter Rd, Plano, TX 75074',
source: 'Storm Canvass',
status: 'Unverified',
verificationStatus: 'Failed Contact',
assigneeId: 'a2', assigneeName: 'Darlene Brooks',
urgency: 'standard',
createdAt: '2026-05-18T16:10:00Z',
assignedAt: '2026-05-18T16:40:00Z',
verifiedAt: null,
verificationNotes: 'Four callback attempts — number goes straight to voicemail. Email bounced. Marking unverified.',
history: [
{ at: '2026-05-18T16:10:00Z', by: 'System', action: 'Lead captured during storm canvass.' },
{ at: '2026-05-18T16:40:00Z', by: 'Darlene Brooks', action: 'Assigned to Darlene Brooks.' },
{ at: '2026-05-20T09:00:00Z', by: 'Darlene Brooks', action: 'Attempt 1 — voicemail, no response.' },
{ at: '2026-05-21T10:30:00Z', by: 'Darlene Brooks', action: 'Attempt 2 — voicemail.' },
{ at: '2026-05-22T14:00:00Z', by: 'Darlene Brooks', action: 'Attempt 3 — voicemail. Email bounced.' },
{ at: '2026-05-23T11:15:00Z', by: 'Darlene Brooks', action: 'Marked Unverified — no contact after 4 attempts.' },
],
},
{
id: 'lv_012', leadId: 'LD-V-012',
customerName: 'Felicia Granger',
phone: '(469) 490-3156',
email: 'felicia.granger@gmail.com',
address: '4400 W Parker Rd, Plano, TX 75093',
source: 'Door Knock',
status: 'Unverified',
verificationStatus: 'Failed Contact',
assigneeId: 'a3', assigneeName: 'Roy Schaefer',
urgency: 'standard',
createdAt: '2026-05-16T11:00:00Z',
assignedAt: '2026-05-16T11:30:00Z',
verifiedAt: null,
verificationNotes: 'Number disconnected after two attempts. Owner may have moved. Flagged unverified.',
history: [
{ at: '2026-05-16T11:00:00Z', by: 'System', action: 'Lead submitted via door knock intake.' },
{ at: '2026-05-16T11:30:00Z', by: 'Roy Schaefer', action: 'Assigned to Roy Schaefer.' },
{ at: '2026-05-17T09:00:00Z', by: 'Roy Schaefer', action: 'Attempt 1 — number disconnected.' },
{ at: '2026-05-18T10:00:00Z', by: 'Roy Schaefer', action: 'Marked Unverified — number disconnected, no secondary contact.' },
],
},
{
id: 'lv_013', leadId: 'LD-V-013',
customerName: 'Nathaniel Crosby',
phone: '(972) 945-2807',
email: 'n.crosby@icloud.com',
address: '5920 Coit Rd, Plano, TX 75023',
source: 'Storm Canvass',
status: 'Assigned',
verificationStatus: 'Pending Outreach',
assigneeId: 'a1', assigneeName: 'Wade Hollis',
urgency: 'high',
createdAt: '2026-05-28T10:00:00Z',
assignedAt: '2026-05-28T10:35:00Z',
verifiedAt: null,
verificationNotes: 'Significant hail event last night. Homeowner flagged as high priority — visible structural damage on ridge.',
history: [
{ at: '2026-05-28T10:00:00Z', by: 'System', action: 'Lead captured during storm canvass after overnight hail.' },
{ at: '2026-05-28T10:35:00Z', by: 'Wade Hollis', action: 'Assigned to Wade Hollis — high urgency.' },
],
},
{
id: 'lv_014', leadId: 'LD-V-014',
customerName: 'Josephine Aldridge',
phone: '(214) 770-6381',
email: 'jo.aldridge@yahoo.com',
address: '3108 Hedgcoxe Rd, Plano, TX 75025',
source: 'Call Center',
status: 'In Progress',
verificationStatus: 'Reviewing Insurance',
assigneeId: 'a2', assigneeName: 'Darlene Brooks',
urgency: 'high',
createdAt: '2026-05-24T13:00:00Z',
assignedAt: '2026-05-24T13:30:00Z',
verifiedAt: null,
verificationNotes: 'Homeowner has Allstate claim already filed. Reviewing adjuster report for scope accuracy.',
history: [
{ at: '2026-05-24T13:00:00Z', by: 'System', action: 'Lead created from call center intake.' },
{ at: '2026-05-24T13:30:00Z', by: 'Darlene Brooks', action: 'Assigned to Darlene Brooks.' },
{ at: '2026-05-25T09:00:00Z', by: 'Darlene Brooks', action: 'Marked In Progress — reviewing Allstate adjuster report.' },
],
},
{
id: 'lv_015', leadId: 'LD-V-015',
customerName: 'Tyrone Beaumont',
phone: '(945) 381-7024',
email: 'tyrone.beaumont@gmail.com',
address: '4712 Mapleshade Ln, Plano, TX 75093',
source: 'Door Knock',
status: 'In Progress',
verificationStatus: 'Emergency — Awaiting Verifier',
assigneeId: 'a3', assigneeName: 'Roy Schaefer',
urgency: 'emergency',
createdAt: '2026-05-29T06:50:00Z',
assignedAt: '2026-05-29T07:00:00Z',
verifiedAt: null,
verificationNotes: 'Active roof leak with interior water intrusion. Family with young children on site. Emergency verification required immediately.',
history: [
{ at: '2026-05-29T06:50:00Z', by: 'System', action: 'Emergency intake from door knock — active leak reported.' },
{ at: '2026-05-29T07:00:00Z', by: 'Roy Schaefer', action: 'Assigned to Roy Schaefer — emergency priority.' },
{ at: '2026-05-29T07:05:00Z', by: 'Roy Schaefer', action: 'Marked In Progress — emergency verification in progress.' },
],
},
];
const MOCK_SUBCONTRACTORS = [
{
id: 'sub_001',
@@ -8678,6 +8999,7 @@ export const MockStoreProvider = ({ children }) => {
const [kanbanColumns, setKanbanColumns] = useState(KANBAN_COLUMNS_INITIAL);
const [kanbanLeads, setKanbanLeads] = useState(KANBAN_LEADS_INITIAL);
const [stormZones, setStormZones] = useState(INITIAL_STORM_ZONES);
const [leadVerifications, setLeadVerifications] = useState(MOCK_LEAD_VERIFICATIONS);
// Commission Settings Org defaults + per-user overrides
const [orgCommissionDefaults, setOrgCommissionDefaults] = useState({
@@ -9303,6 +9625,157 @@ export const MockStoreProvider = ({ children }) => {
toast.success(`Task reassigned to ${subcontractor.name}`);
};
// Lead Verification Workflow
const appendLvHistory = (lv, entry) => ({
...lv,
history: [...(lv.history || []), { at: new Date().toISOString(), ...entry }],
});
const assignLeadVerification = (id, assigneeId, assigneeName) => {
if (!assigneeId) {
toast.error('Please select an assignee.');
return;
}
setLeadVerifications(prev => prev.map(lv => {
if (lv.id !== id) return lv;
const wasUnassigned = !lv.assigneeId;
return appendLvHistory({
...lv,
assigneeId,
assigneeName,
status: (lv.status === 'Pending' || lv.status === 'Unverified') ? 'Assigned' : lv.status,
verificationStatus: wasUnassigned ? 'Pending Outreach' : lv.verificationStatus,
assignedAt: lv.assignedAt || new Date().toISOString(),
}, { by: assigneeName, action: `${wasUnassigned ? 'Assigned' : 'Reassigned'} to ${assigneeName}.` });
}));
toast.success(`Lead assigned to ${assigneeName}`);
};
const reassignLeadVerification = (id, assigneeId, assigneeName) => {
if (!assigneeId) {
toast.error('Please select an assignee.');
return;
}
setLeadVerifications(prev => prev.map(lv => {
if (lv.id !== id) return lv;
return appendLvHistory({
...lv,
assigneeId,
assigneeName,
}, { by: assigneeName, action: `Reassigned to ${assigneeName}.` });
}));
toast.success(`Lead reassigned to ${assigneeName}`);
};
const setLeadVerificationStatus = (id, newStatus, note = '') => {
setLeadVerifications(prev => prev.map(lv => {
if (lv.id !== id) return lv;
const derivedVerificationStatus =
newStatus === 'In Progress' ? 'Verifying Identity' :
newStatus === 'Assigned' ? 'Pending Outreach' :
newStatus === 'Pending' ? (lv.assigneeId ? 'Pending Outreach' : 'Unassigned') :
newStatus === 'Unverified' ? 'Failed Contact' :
lv.verificationStatus;
return appendLvHistory({
...lv,
status: newStatus,
verificationStatus: derivedVerificationStatus,
verificationNotes: note || lv.verificationNotes,
}, { by: lv.assigneeName || 'System', action: `Status changed to ${newStatus}${note ? `${note}` : ''}.` });
}));
toast.success(`Lead moved to ${newStatus}`);
};
const verifyLead = (id) => {
const lv = leadVerifications.find(x => x.id === id);
if (!lv) return;
if (lv.status === 'Verified') {
toast.info('Lead already verified.');
return;
}
const nowIso = new Date().toISOString();
setLeadVerifications(prev => prev.map(item => {
if (item.id !== id) return item;
return appendLvHistory({
...item,
status: 'Verified',
verificationStatus: 'Verified',
verifiedAt: nowIso,
}, { by: item.assigneeName || 'System', action: 'Verified and pushed to New Leads.' });
}));
// Split customerName firstName / lastName
const nameParts = (lv.customerName || '').trim().split(' ');
const firstName = nameParts[0] || 'Unknown';
const lastName = nameParts.slice(1).join(' ');
// Parse city/state from address (e.g. "... Plano, TX 75023")
const addrMatch = (lv.address || '').match(/,\s*([^,]+),\s*([A-Z]{2})\s+\d+\s*$/);
const city = addrMatch ? addrMatch[1].trim() : 'Plano';
const state = addrMatch ? addrMatch[2].trim() : 'TX';
const newLead = {
id: `lead_lv_${Date.now()}`,
firstName,
lastName,
address: lv.address || '',
city,
state,
urgency: lv.urgency || 'standard',
status: 'New',
leadSource: lv.source || 'Lead Verification',
phones: lv.phone ? [{ id: '1', number: lv.phone, type: 'Mobile', isPrimary: true }] : [],
emails: lv.email ? [{ email: lv.email, isPrimary: true }] : [],
createdAt: nowIso,
createdByName: 'Lead Verification',
verificationId: lv.id,
verificationLeadId: lv.leadId,
notes: lv.verificationNotes || '',
// Minimal enriched fields so LeadsListPage doesn't break
canvasserName: lv.assigneeName || '',
assignedTo: '',
priority: lv.urgency === 'emergency' ? 'critical' : lv.urgency === 'high' ? 'high' : 'low',
followUpDate: '',
isQuickCapture: false,
zip: (lv.address || '').match(/\d{5}$/)?.[0] || '',
propertyType: 'Single Family',
propertyPhotos: [],
leadType: 'Insurance',
workType: 'Roof Replacement',
tradeType: 'Roofing',
referralNote: '',
canvasserId: '',
insuranceCompany: '', claimNumber: '', claimStatus: '',
adjusterName: '', adjusterPhone: '', policyNumber: '',
};
setLeads(prev => [newLead, ...prev]);
toast.success(`${lv.customerName} verified — moved to New Leads`);
};
const unverifyLead = (id, note = '') => {
setLeadVerifications(prev => prev.map(lv => {
if (lv.id !== id) return lv;
return appendLvHistory({
...lv,
status: 'Unverified',
verificationStatus: 'Failed Contact',
verificationNotes: note || lv.verificationNotes,
}, { by: lv.assigneeName || 'System', action: `Marked Unverified${note ? `${note}` : ''}.` });
}));
toast.success('Lead marked as Unverified');
};
const addLeadVerificationNote = (id, note) => {
if (!note?.trim()) return;
setLeadVerifications(prev => prev.map(lv => {
if (lv.id !== id) return lv;
return appendLvHistory({
...lv,
verificationNotes: note.trim(),
}, { by: lv.assigneeName || 'System', action: `Note added: ${note.trim()}` });
}));
};
return (
<MockStoreContext.Provider value={{
properties,
@@ -9617,6 +10090,14 @@ export const MockStoreProvider = ({ children }) => {
resolveProfile,
updateOwnProfile,
setSkillScore,
// Lead Verification Queue
leadVerifications,
assignLeadVerification,
reassignLeadVerification,
setLeadVerificationStatus,
verifyLead,
unverifyLead,
addLeadVerificationNote,
}}>
{children}
</MockStoreContext.Provider>