/* QuoteFlow Prototype · Customer journey (choose → configure → estimate → submit → confirm).
On submit it writes a real lead to window.QFStore, which the admin app reads. */
function CustomerApp({ onGoAdmin }) {
const { Button, Card, Badge, Switch, Input, Stepper, StatusBadge } = window.QuoteFlowDesignSystem_41788d;
const C = window.QF_CATALOG;
const [step, setStep] = React.useState('choose'); // choose | configure | estimate | done
const [system, setSystem] = React.useState(null);
const [cfg, setCfg] = React.useState({ panels: 7, battery: true, ev: false, bird: true });
const [form, setForm] = React.useState({ name: '', email: '', phone: '', postcode: '' });
const [submitted, setSubmitted] = React.useState(null);
const stepIdx = { choose: 0, configure: 1, estimate: 2, done: 2 }[step];
// ---- live pricing from the shared catalogue ----
const panel = C.get(C.configurator.solar.panel);
const opt = C.get;
const output = Math.round(cfg.panels * panel.watts);
const low = C.model.baseInstall
+ cfg.panels * panel.fit
+ (cfg.battery ? opt('GIV-BAT-9.5').fit : 0)
+ (cfg.ev ? opt('ZAPPI-2-7TW').fit : 0)
+ (cfg.bird ? opt('MESH-GUARD').fit : 0);
const high = Math.round(low * (1 + C.model.rangeUplift));
const annualSaving = Math.round(output * C.model.perKwhSaving);
const SYSTEMS = [
{ name: 'Solar & Battery Storage', cat: 'solar', label: 'Solar & Battery', badge: 'Most popular',
desc: 'Generate your own clean electricity, cut grid dependence, and store solar for evening use.' },
{ name: 'Standalone Battery Storage', cat: 'battery', label: 'Battery Storage',
desc: 'Retrofit premium battery banks onto existing solar or load-shift cheap off-peak electricity.' },
{ name: 'EV Smart Chargers', cat: 'ev', label: 'EV Charger',
desc: 'Install a fast, smart chargepoint that can divert excess daytime solar to your car.' },
{ name: 'Air Source Heat Pumps', cat: 'heat', label: 'Heat Pump', soon: true,
desc: 'Transition away from fossil fuels with an efficient air-to-water heat pump.' },
];
function submit() {
var lead = window.QFStore.addLead({
name: form.name || 'New enquiry', email: form.email, phone: form.phone, postcode: form.postcode,
loc: form.postcode ? form.postcode.split(' ')[0] : '—',
system: system.label, cat: system.cat,
value: Math.round((low + high) / 2), low: low, high: high,
config: Object.assign({}, cfg),
});
setSubmitted(lead);
setStep('done');
window.scrollTo(0, 0);
}
// ---------- shared header ----------
const Header = () => (
Greenline Energy
01305 123 456
);
// ---------- step: choose ----------
const Choose = () => (
Choose your system upgrade
Select a technology to generate your personalised instant quote.
{SYSTEMS.map((s, i) => (
{ if (!s.soon) { setSystem(s); setStep('configure'); window.scrollTo(0, 0); } }}
style={{ cursor: s.soon ? 'default' : 'pointer', opacity: s.soon ? 0.7 : 1 }}>
{s.name}
{s.badge && {s.badge} }
{s.soon && Coming soon }
{s.desc}
{!s.soon &&
Get instant quote }
))}
);
// ---------- step: configure ----------
const Row = ({ label, sub, children }) => (
);
const Configure = () => (
{ setStep('choose'); window.scrollTo(0, 0); }} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', background: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-muted)', marginBottom: 14 }}>
Back to systems
{system.name} Tune your system — the estimate updates live.
Hardware package
setCfg(Object.assign({}, cfg, { panels: +e.target.value }))} aria-label="Panels" style={{ width: 170 }} />
{cfg.panels}
setCfg(Object.assign({}, cfg, { battery: v }))} />
setCfg(Object.assign({}, cfg, { ev: v }))} />
Bird & squirrel protection
Mesh guard around the array
setCfg(Object.assign({}, cfg, { bird: v }))} />
{/* sticky summary */}
Your estimate Live
{[['Panels', cfg.panels + ' × ' + panel.watts + 'W'], ['Battery', cfg.battery ? '9.5kWh hybrid' : '—'], ['EV charger', cfg.ev ? '7kW smart' : '—'], ['Est. output', output.toLocaleString('en-GB') + ' kWh/yr']].map(r => (
{r[0]} {r[1]}
))}
Estimated range
{gbp(low)} – {gbp(high)}
} onClick={() => { setStep('estimate'); window.scrollTo(0, 0); }}>See full estimate
);
// ---------- step: estimate ----------
const items = [
{ label: 'Solar PV installation', sub: cfg.panels + ' × ' + panel.name + ', inverter & mounting', amount: C.model.baseInstall + cfg.panels * panel.fit },
].concat(cfg.battery ? [{ label: opt('GIV-BAT-9.5').name, sub: 'Hybrid battery with monitoring', amount: opt('GIV-BAT-9.5').fit }] : [])
.concat(cfg.ev ? [{ label: opt('ZAPPI-2-7TW').name, sub: 'Untethered smart charger', amount: opt('ZAPPI-2-7TW').fit }] : [])
.concat(cfg.bird ? [{ label: 'Bird & squirrel protection', sub: 'Mesh guard around the array', amount: opt('MESH-GUARD').fit }] : []);
const formValid = form.name && form.email && form.postcode;
const Estimate = () => (
{ setStep('configure'); window.scrollTo(0, 0); }} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', background: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-muted)', marginBottom: 14 }}>
Back to system design
Your personalised estimate {system.name} · by Greenline Energy
What's included
{items.map((it, i) => (
))}
Estimated total
{gbp(low)} – {gbp(high)}
{[['piggy-bank', gbp(annualSaving) + '/yr', 'Est. bill saving'], ['trending-up', gbp(annualSaving * 25), '25-year saving'], ['leaf', (output * 0.207 / 1000).toFixed(1) + 't', 'CO₂ saved / yr']].map((s, i) => (
))}
{/* capture */}
Estimated total
{gbp(low)} – {gbp(high)}
);
// ---------- step: done ----------
const Done = () => (
Quote sent to Greenline Energy
Thanks {submitted.name.split(' ')[0]}! Your {submitted.system} quote of {gbp(submitted.low)}–{gbp(submitted.high)} has been saved. The team will be in touch to book your free survey.
This lead is now live in the installer's pipeline as a .
{ setSystem(null); setForm({ name: '', email: '', phone: '', postcode: '' }); setStep('choose'); window.scrollTo(0, 0); }}>Start another quote
} onClick={onGoAdmin}>View it in admin →
);
return (
{step !== 'done' && (
)}
{step === 'choose' &&
}
{step === 'configure' &&
}
{step === 'estimate' &&
}
{step === 'done' &&
}
);
}
window.CustomerApp = CustomerApp;