diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx index 57c5c69..095922b 100644 --- a/src/data/mockStore.jsx +++ b/src/data/mockStore.jsx @@ -9140,6 +9140,31 @@ const DEFAULT_EMPTY_PROFILE = { const MockStoreContext = createContext(); +// --------------------------------------------------------------------------- +// Estimate Wizard → CRM lead persistence. +// The public DIY Estimate Wizard runs in a separate session from the authed CRM, +// so its leads must survive a reload. They're persisted to localStorage and +// rehydrated into the leads list on store init, deduped by wizard session id. +// --------------------------------------------------------------------------- +const WIZARD_LEADS_KEY = 'lup_estimate_wizard_leads_v1'; + +function loadWizardLeads() { + try { + const raw = localStorage.getItem(WIZARD_LEADS_KEY); + const list = raw ? JSON.parse(raw) : []; + return Array.isArray(list) ? list : []; + } catch { return []; } +} + +function persistWizardLead(lead) { + try { + const list = loadWizardLeads(); + const sid = lead?.estimateWizard?.sessionId; + if (sid && list.some(l => l?.estimateWizard?.sessionId === sid)) return; // already saved + localStorage.setItem(WIZARD_LEADS_KEY, JSON.stringify([lead, ...list])); + } catch { /* quota */ } +} + export const MockStoreProvider = ({ children }) => { const [properties, setProperties] = useState([]); const [users, setUsers] = useState(MOCK_USERS); @@ -9154,7 +9179,7 @@ export const MockStoreProvider = ({ children }) => { const [projects, setProjects] = useState(MOCK_PROJECTS); const [orders, setOrders] = useState(MOCK_ORDERS); const [vendorInvoices, setVendorInvoices] = useState(MOCK_VENDOR_INVOICES); - const [leads, setLeads] = useState(() => seedLeadsFromAttribution()); + const [leads, setLeads] = useState(() => [...loadWizardLeads(), ...seedLeadsFromAttribution()]); const [dispatchLeads, setDispatchLeads] = useState(DISPATCH_LEADS_INITIAL); const [templates, setTemplates] = useState(MASTER_TEMPLATES_INITIAL); const [estimates, setEstimates] = useState(MOCK_ESTIMATES_INITIAL); @@ -9979,6 +10004,21 @@ export const MockStoreProvider = ({ children }) => { return newLead; }, + // Lead created by the public Estimate Wizard. Persisted to localStorage + // (survives reload) and deduped by wizard session id. + addWizardLead: (lead) => { + const created = { + ...lead, + id: lead.id || `lead_wiz_${Date.now()}`, + createdAt: lead.createdAt || new Date().toISOString(), + status: 'New', + }; + const sid = created.estimateWizard?.sessionId; + setLeads(prev => (sid && prev.some(l => l?.estimateWizard?.sessionId === sid)) ? prev : [created, ...prev]); + persistWizardLead(created); + return created; + }, + // Templates templates, addTemplate: (tpl) => { diff --git a/src/modules/estimateWizard/CustomerEstimateWizard.jsx b/src/modules/estimateWizard/CustomerEstimateWizard.jsx index 24c3132..dd98808 100644 --- a/src/modules/estimateWizard/CustomerEstimateWizard.jsx +++ b/src/modules/estimateWizard/CustomerEstimateWizard.jsx @@ -19,8 +19,8 @@ const CompletionScreen = () => { Thanks{inputs.name ? `, ${inputs.name.split(' ')[0]}` : ''}!

- Your roof options report is being prepared. The branded report, AI recommendation, and - booking step are delivered in Phase 5. + Your roof options report is ready and your details have been sent to our team — + a rep will reach out to confirm your free inspection.

+