/* 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 }) => (
{label}
{sub &&
{sub}
}
{children}
); const Configure = () => (

{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 estimateLive
{[['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)}
); // ---------- 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 = () => (

Your personalised estimate

{system.name} · by Greenline Energy

What's included

{items.map((it, i) => (
{it.label}
{it.sub}
{gbp(it.amount)}
))}
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) => (
{s[1]}
{s[2]}
))}
{/* capture */}
Estimated total
{gbp(low)} – {gbp(high)}
Book your free survey
setForm(Object.assign({}, form, { name: e.target.value }))} /> setForm(Object.assign({}, form, { email: e.target.value }))} /> setForm(Object.assign({}, form, { phone: e.target.value }))} /> setForm(Object.assign({}, form, { postcode: e.target.value }))} />

Submitting sends your quote to Greenline Energy — it appears instantly in their installer dashboard.

); // ---------- 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 .
); return (
{step !== 'done' && (
)} {step === 'choose' && } {step === 'configure' && } {step === 'estimate' && } {step === 'done' && }
); } window.CustomerApp = CustomerApp;