feat(leads): enrich lead data + add clickable lead detail page mirroring the create-lead form
This commit is contained in:
+139
-24
@@ -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,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user