feat(leads): enrich lead data + add clickable lead detail page mirroring the create-lead form

This commit is contained in:
Satyam Rastogi
2026-05-29 18:18:58 +05:30
parent aeabcea850
commit a8ed0c0034
4 changed files with 504 additions and 25 deletions
+139 -24
View File
@@ -7662,12 +7662,8 @@ const MOCK_NOTIFICATIONS = [
// --- SEED HELPERS ---
/**
* Maps MOCK_STORM_ATTRIBUTION_LEADS into the shape LeadsListPage expects.
* Field coverage:
* id, firstName, lastName, address, city, state,
* urgency, status, leadSource, stormSource (truthy obj),
* phones[{ number, isPrimary }],
* canvasserName, createdByName, createdAt
* Maps MOCK_STORM_ATTRIBUTION_LEADS into the shape LeadsListPage expects,
* enriched with the full field set mirroring the CreateLeadPage form.
*/
function seedLeadsFromAttribution() {
const canvassers = [
@@ -7684,32 +7680,151 @@ function seedLeadsFromAttribution() {
'Shelby Greer',
'Dalton Pruitt',
];
const emailDomains = ['gmail.com', 'yahoo.com', 'outlook.com', 'gmail.com', 'yahoo.com'];
const planoZips = ['75023', '75024', '75025', '75074', '75075', '75093'];
const propertyTypes = ['Single Family', 'Single Family', 'Townhome', 'Single Family', 'Multi-Family', 'Single Family'];
const leadTypes = ['Insurance', 'Insurance', 'Retail', 'Insurance', 'Retail', 'Insurance'];
const workTypes = ['Roof Replacement', 'Roof Repair', 'Roof Replacement', 'Siding', 'Gutters', 'Windows'];
const insuranceCompanies = ['State Farm', 'Allstate', 'Farmers', 'USAA'];
const claimStatuses = ['Filed', 'Under Review', 'Approved'];
const adjusterNames = [
'Marcus Powell', 'Linda Forsythe', 'Craig Delgado', 'Susan Merritt',
'Jeff Okonkwo', 'Diane Castillo',
];
const adjusterAreaCodes = ['972', '469', '214'];
const repNames = ['Jesus Gonzales', 'Sarah Calloway'];
// Near-future 2026 follow-up dates, deterministic by idx
const followUpDates = [
'2026-06-04', '2026-06-06', '2026-06-09', '2026-06-11', '2026-06-13',
'2026-06-16', '2026-06-18', '2026-06-20', '2026-06-23', '2026-06-25',
'2026-06-27', '2026-06-30', '2026-07-02', '2026-07-07', '2026-07-09',
'2026-07-11', '2026-07-14', '2026-07-16', '2026-07-18', '2026-07-21',
'2026-07-23', '2026-07-25', '2026-07-28', '2026-07-30', '2026-08-01',
'2026-08-04', '2026-08-06', '2026-08-08', '2026-08-11', '2026-08-13',
'2026-08-15', '2026-08-18', '2026-08-20', '2026-08-22', '2026-08-25',
];
const canvassingNotes = [
'Homeowner showed significant granule loss on south-facing slopes; agreed to inspection.',
'Visible dents on ridge cap and gutters from recent hail event; very interested.',
'Neighbor referral — two doors down already signed. Motivated to move quickly.',
'Homeowner working with insurer; requested second opinion on adjuster estimate.',
'Front elevation has missing shingles post-storm. Homeowner confirmed claim filed.',
'Spoke with spouse — primary decision-maker out of town until next week.',
'Property flagged on storm map; homeowner unaware of damage. Scheduled walkthrough.',
'Older roof (est. 15 yr); multiple soft spots noted. Prime re-roof candidate.',
'Hail dents visible on AC unit and metal flashing. Homeowner eager to proceed.',
'HOA required repair; homeowner wants professional documentation for board approval.',
];
// Deterministic assignment based on SAL index so output is stable.
return MOCK_STORM_ATTRIBUTION_LEADS.map((sal, idx) => {
const canvasserName = canvassers[idx % canvassers.length];
const createdByName = creators[(idx + 2) % creators.length];
// Build a realistic phone number from the index
const canvasserName = canvassers[idx % canvassers.length];
const createdByName = creators[(idx + 2) % creators.length];
// Primary phone (area-code varies by parity)
const areaCode = idx % 2 === 0 ? '469' : '972';
const mid = String(500 + idx).padStart(3, '0');
const last = String(1000 + idx * 37).padStart(4, '0').slice(-4);
const phoneNumber = `(${areaCode}) ${mid}-${last}`;
const mid = String(500 + idx).padStart(3, '0');
const last = String(1000 + idx * 37).padStart(4, '0').slice(-4);
const primaryPhone = `(${areaCode}) ${mid}-${last}`;
// Second phone for every third lead
const phones = [{ id: '1', number: primaryPhone, type: 'Mobile', isPrimary: true }];
if (idx % 3 === 0) {
const homeAC = idx % 2 === 0 ? '214' : '817';
const homeMid = String(600 + idx).padStart(3, '0');
const homeLast = String(2000 + idx * 13).padStart(4, '0').slice(-4);
phones.push({ id: '2', number: `(${homeAC}) ${homeMid}-${homeLast}`, type: 'Home', isPrimary: false });
}
// Email
const domain = emailDomains[idx % emailDomains.length];
const emails = [{
email: `${sal.firstName.toLowerCase()}.${sal.lastName.toLowerCase()}@${domain}`,
isPrimary: true,
}];
// Property
const zip = planoZips[idx % planoZips.length];
const propertyType = propertyTypes[idx % propertyTypes.length];
// Job details
const leadType = leadTypes[idx % leadTypes.length];
const workType = workTypes[idx % workTypes.length];
const tradeType = 'Roofing';
const priority = sal.urgency === 'high' ? 'high' : (idx % 3 === 0 ? 'medium' : 'low');
const notes = canvassingNotes[idx % canvassingNotes.length];
// Assignment
const assignedTo = repNames[idx % repNames.length];
const followUpDate = followUpDates[idx % followUpDates.length];
// Insurance block (only for Insurance leads)
let insuranceBlock = {};
if (leadType === 'Insurance') {
const ins = insuranceCompanies[idx % insuranceCompanies.length];
const claimNum = `CLM-2026-${String(1000 + idx).padStart(4, '0')}`;
const policyNum = `POL-${String(80000 + idx * 7).padStart(6, '0')}`;
const claimStat = claimStatuses[idx % claimStatuses.length];
const adjName = adjusterNames[idx % adjusterNames.length];
const adjAC = adjusterAreaCodes[idx % adjusterAreaCodes.length];
const adjMid = String(700 + idx).padStart(3, '0');
const adjLast = String(3000 + idx * 19).padStart(4, '0').slice(-4);
insuranceBlock = {
insuranceCompany: ins,
claimNumber: claimNum,
claimStatus: claimStat,
adjusterName: adjName,
adjusterPhone: `(${adjAC}) ${adjMid}-${adjLast}`,
policyNumber: policyNum,
};
} else {
insuranceBlock = {
insuranceCompany: '',
claimNumber: '',
claimStatus: '',
adjusterName: '',
adjusterPhone: '',
policyNumber: '',
};
}
return {
id: sal.id,
firstName: sal.firstName,
lastName: sal.lastName,
address: sal.address,
city: sal.city,
state: sal.state,
urgency: sal.urgency,
status: sal.status,
leadSource: sal.leadSource,
stormSource: sal.stormSource, // truthy object renders Storm Zone badge
createdAt: sal.createdAt,
// Original fields (kept as-is for LeadsListPage card)
id: sal.id,
firstName: sal.firstName,
lastName: sal.lastName,
address: sal.address,
city: sal.city,
state: sal.state,
urgency: sal.urgency,
status: sal.status,
leadSource: sal.leadSource,
stormSource: sal.stormSource,
createdAt: sal.createdAt,
canvasserName,
createdByName,
phones: [{ number: phoneNumber, isPrimary: true }],
isQuickCapture: false,
// Enriched phone list
phones,
// Contact
emails,
// Property
zip,
propertyType,
propertyPhotos: [],
// Job Details
leadType,
workType,
tradeType,
referralNote: '',
canvasserId: '',
notes,
// Assignment
assignedTo,
priority,
followUpDate,
// Insurance
...insuranceBlock,
};
});
}