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.