estimate module setup and wizard ui
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* StartStep — Screen 1 "Simple Start" (spec §4 / §5).
|
||||
* Captures name, property address, mobile, email, contact method + consent.
|
||||
* Consent is required before proceeding (spec §15 Privacy / §3 compliance).
|
||||
*/
|
||||
import React from 'react';
|
||||
import { User, MapPin, Phone, Mail } from 'lucide-react';
|
||||
import { useEstimateWizard } from '../../EstimateWizardContext';
|
||||
|
||||
const Field = ({ icon: Icon, label, children }) => (
|
||||
<label className="block">
|
||||
<span className="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1.5 flex items-center gap-1.5">
|
||||
{Icon && <Icon size={15} className="text-amber-500" />} {label}
|
||||
</span>
|
||||
{children}
|
||||
</label>
|
||||
);
|
||||
|
||||
const inputCls =
|
||||
'w-full rounded-xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-zinc-900 px-3.5 py-2.5 ' +
|
||||
'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();
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Field icon={User} label="Name">
|
||||
<input
|
||||
className={inputCls}
|
||||
placeholder="First and last name"
|
||||
value={inputs.name}
|
||||
onChange={(e) => setInput('name', e.target.value)}
|
||||
autoComplete="name"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field icon={MapPin} label="Property address">
|
||||
<input
|
||||
className={inputCls}
|
||||
placeholder="Street, city, state, ZIP"
|
||||
value={inputs.address}
|
||||
onChange={(e) => setInput('address', e.target.value)}
|
||||
autoComplete="street-address"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<Field icon={Phone} label="Mobile number">
|
||||
<input
|
||||
className={inputCls}
|
||||
placeholder="(555) 123-4567"
|
||||
value={inputs.phone}
|
||||
onChange={(e) => setInput('phone', e.target.value)}
|
||||
autoComplete="tel"
|
||||
inputMode="tel"
|
||||
/>
|
||||
</Field>
|
||||
<Field icon={Mail} label="Email">
|
||||
<input
|
||||
className={inputCls}
|
||||
placeholder="you@email.com"
|
||||
value={inputs.email}
|
||||
onChange={(e) => setInput('email', e.target.value)}
|
||||
autoComplete="email"
|
||||
inputMode="email"
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span className="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1.5 block">Best contact method?</span>
|
||||
<div className="flex gap-2">
|
||||
{['text', 'email', 'phone'].map((m) => (
|
||||
<button
|
||||
key={m}
|
||||
type="button"
|
||||
onClick={() => setInput('contactMethod', m)}
|
||||
className={`flex-1 rounded-xl border px-3 py-2 text-sm font-medium capitalize transition-all
|
||||
${inputs.contactMethod === m
|
||||
? 'border-amber-500 bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300'
|
||||
: 'border-zinc-200 dark:border-white/10 text-zinc-600 dark:text-zinc-300 hover:border-amber-400'}`}
|
||||
>
|
||||
{m}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label className="flex items-start gap-2.5 rounded-xl bg-zinc-50 dark:bg-white/5 p-3.5 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-0.5 h-4 w-4 accent-amber-500"
|
||||
checked={inputs.consent}
|
||||
onChange={(e) => setInput('consent', e.target.checked)}
|
||||
/>
|
||||
<span className="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
|
||||
I agree to be contacted about my roof estimate and understand my address and any insurance
|
||||
details I provide are used to generate this educational report. This is not a binding quote.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StartStep;
|
||||
Reference in New Issue
Block a user