new lead created by estimate wizard now list in leads page

This commit is contained in:
Mayur Shinde
2026-06-09 22:43:01 +05:30
parent 075d2dd60f
commit 5cf34da58b
6 changed files with 191 additions and 11 deletions
@@ -4,11 +4,12 @@
* the inspection, and fires the CRM + nurture handoff (once) with the immutable
* compliance record (spec §18 G1).
*/
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { Download, Printer, Mail, Share2, CalendarCheck, FileText, Loader2, Check } from 'lucide-react';
import { useEstimateWizard } from '../../EstimateWizardContext';
import { useMockStore } from '../../../../data/mockStore';
import { buildReportModel, downloadReportPdf, getReportBlobUrl } from '../../utils/wizardReportExport';
import { submitToCrm } from '../../api/crmHandoff';
import { submitToCrm, buildLeadFromSession } from '../../api/crmHandoff';
import { formatUSD } from '../../engine/pricing';
const PKG_LABEL = { GOOD: 'Good', BETTER: 'Better', BEST: 'Best' };
@@ -29,18 +30,33 @@ const ActionButton = ({ icon: Icon, label, onClick, busy, primary }) => (
const ReportStep = () => {
const { session, tenantConfig, patchSession, logEvent } = useEstimateWizard();
const { addWizardLead } = useMockStore();
const model = buildReportModel(session, tenantConfig);
const [busy, setBusy] = useState(null);
const [booked, setBooked] = useState(!!session.report?.appointmentBooked);
const firedRef = useRef(false);
// Fire CRM + nurture handoff once (spec §13 / §17 E2).
// Fire CRM + nurture handoff once (spec §13 / §17 E2). The ref guard prevents a
// duplicate lead under React StrictMode's double-invoked mount effect.
useEffect(() => {
if (session.report) return;
const reportMeta = { reportId: model.reportId, version: 1, generatedAt: model.generatedAt, deliveryStatus: 'created' };
if (firedRef.current || session.report) return;
firedRef.current = true;
// Create a real lead in the global CRM mock store so the estimate shows up
// in the pipeline (addLead injects id / createdAt / status: 'New').
const lead = addWizardLead(buildLeadFromSession(session, tenantConfig, {
reportId: model.reportId,
estimatedTotal: model.totals.total,
}));
const reportMeta = {
reportId: model.reportId, version: 1, generatedAt: model.generatedAt,
deliveryStatus: 'created', crmLeadId: lead?.id || null, estimatedTotal: model.totals.total,
};
patchSession({ report: reportMeta });
submitToCrm(session, tenantConfig, reportMeta);
logEvent('report_generated', { reportId: model.reportId });
logEvent('crm_handoff', { trigger: 'wizard_completed' });
logEvent('crm_handoff', { trigger: 'wizard_completed', crmLeadId: lead?.id || null });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -4,7 +4,7 @@
* Consent is required before proceeding (spec §15 Privacy / §3 compliance).
*/
import React from 'react';
import { User, MapPin, Phone, Mail } from 'lucide-react';
import { User, MapPin, Phone, Mail, Wand2 } from 'lucide-react';
import { useEstimateWizard } from '../../EstimateWizardContext';
const Field = ({ icon: Icon, label, children }) => (
@@ -21,10 +21,18 @@ const inputCls =
'text-zinc-900 dark:text-white placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-amber-500';
const StartStep = () => {
const { inputs, setInput } = useEstimateWizard();
const { inputs, setInput, prefillDemo } = useEstimateWizard();
return (
<div className="space-y-4">
<button
type="button"
onClick={prefillDemo}
className="w-full flex items-center justify-center gap-2 rounded-xl border border-dashed border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/10 px-3 py-2.5 text-sm font-medium text-amber-700 dark:text-amber-300 hover:border-amber-400"
>
<Wand2 size={15} /> Try a demo address (auto-fills every answer)
</button>
<Field icon={User} label="Name">
<input
className={inputCls}