// Multi-step Book a Consultation flow — the conversion centrepiece.
function BookingFlow({ go, prefill }) {
const [step, setStep] = useState(0); // 0 treatment, 1 surgeon, 2 slot, 3 details, 4 success
const [data, setData] = useState({
treatment: prefill?.treatment || null,
surgeon: null,
day: null,
time: null,
name: '',
phone: '',
email: '',
countryCode: '+971',
note: '',
consent: false
});
const update = (patch) => setData(prev => ({ ...prev, ...patch }));
const steps = [
{ n: '01', label: 'Treatment area' },
{ n: '02', label: 'Surgeon or doctor' },
{ n: '03', label: 'Preferred time' },
{ n: '04', label: 'Your details' }
];
const treatments = [
{ id: 'plastic-surgery', label: 'Plastic Surgery', sub: 'Face · Breast · Body', icon: Icon.user, surgical: true },
{ id: 'skin', label: 'Skin & Aesthetics', sub: 'Injectables · Lasers · Facials', icon: Icon.sparkle, surgical: false },
{ id: 'body', label: 'Body Contouring', sub: 'CoolSculpting · Surgical', icon: Icon.body, surgical: false },
{ id: 'for-men', label: 'For Men', sub: 'Surgical & aesthetic', icon: Icon.man, surgical: true },
{ id: 'wellness', label: 'Wellness', sub: 'IV · Nutrition · Regenerative', icon: Icon.drop, surgical: false },
{ id: 'unsure', label: 'I\'m not sure yet', sub: 'A consultant will help triage', icon: Icon.alert, surgical: false }
];
// suggest surgeons based on treatment
const surgeonOptions = useMemo(() => {
const isSurgicalEnquiry = treatments.find(t => t.id === data.treatment)?.surgical;
if (data.treatment === 'unsure') return DOCTORS.slice(0, 6);
if (isSurgicalEnquiry) return DOCTORS.filter(d => d.title.toLowerCase().includes('plastic') || d.title.toLowerCase().includes('specialist'));
return DOCTORS.filter(d => !d.title.toLowerCase().includes('plastic'));
}, [data.treatment]);
// Mock 14-day calendar starting today
const days = useMemo(() => {
const out = [];
const today = new Date();
for (let i = 1; i <= 14; i++) {
const d = new Date(today);
d.setDate(today.getDate() + i);
out.push({
date: d,
dayName: d.toLocaleDateString('en-US', { weekday: 'short' }),
dayNum: d.getDate(),
month: d.toLocaleDateString('en-US', { month: 'short' }),
disabled: d.getDay() === 0 // Sundays "by appointment only"
});
}
return out;
}, []);
const times = ['10:30', '11:30', '13:00', '14:30', '16:00', '17:30', '18:30', '19:30'];
const next = () => setStep(s => Math.min(s + 1, 4));
const back = () => setStep(s => Math.max(s - 1, 0));
const canNext = (
(step === 0 && data.treatment) ||
(step === 1 && data.surgeon) ||
(step === 2 && data.day && data.time) ||
(step === 3 && data.name && data.phone && data.email && data.consent)
);
const selectedTreatment = treatments.find(t => t.id === data.treatment);
const selectedSurgeon = DOCTORS.find(d => d.slug === data.surgeon);
return (
go({ page: 'home' })} style={{ cursor: 'pointer', color: 'var(--ink-muted)' }}>Home
›
Book a Consultation
Consultation
{step < 4 ? 'A 45-minute conversation with the right consultant.' : 'Request received.'}
{step < 4 && (
We collect only what's needed to confirm your appointment. Medical history is taken
in person with the consultant, not in a form.
)}
{steps.map((s, i) => (
i ? 'done' : ''}`}>
{step > i ? : s.n}
{s.label}
))}
{step >= 1 && data.treatment && (
Your Selection
{selectedTreatment?.label}
{selectedSurgeon && (
with {selectedSurgeon.name}
)}
{data.day && data.time && (
{data.day.dayName} {data.day.dayNum} {data.day.month} · {data.time}
)}
)}
Prefer to skip the form?
WhatsApp +971 4 384 5600
Call · {CLINIC_INFO.phone}
{CLINIC_INFO.email}
{step === 0 && (
What would you like to discuss?
Pick a stream of care — we'll match you to the right consultant next.
{treatments.map(t => (
update({ treatment: t.id, surgeon: null })}
>
{t.label}
{t.sub}
))}
{selectedTreatment?.surgical && (
Surgical procedures require an in-person paid consultation, candidacy assessment and full risk disclosure before booking. Performed by DHA-licensed surgeons.
)}
Continue
)}
{step === 1 && (
Choose your consultant
We've narrowed this to the team members who handle {selectedTreatment?.label.toLowerCase()} . You can also let reception decide.
update({ surgeon: 'any' })}
>
No Preference
Reception will match
{surgeonOptions.map(s => (
update({ surgeon: s.slug })}
>
))}
Back
Continue
)}
{step === 2 && (
Pick a preferred day & time
We'll confirm the exact slot when reception calls you back. Sunday is by appointment only.
Next 14 days
{days.slice(0, 7).map((d, i) => (
update({ day: d, time: null })}
>
{d.dayName}
{d.dayNum}
))}
{days.slice(7, 14).map((d, i) => (
update({ day: d, time: null })}
>
{d.dayName}
{d.dayNum}
))}
{data.day && (
Times for {data.day.dayName} {data.day.dayNum} {data.day.month}
{times.map(t => (
update({ time: t })}
>{t}
))}
All times Gulf Standard Time (GST). Indicative — reception confirms availability.
)}
Back
Continue
)}
{step === 3 && (
Just your contact details
No medical history here — that's discussed in person, with the consultant.
Treatment
{selectedTreatment?.label}
Consultant
{data.surgeon === 'any' ? 'No preference — reception to match' : selectedSurgeon?.name}
Preferred
{data.day?.dayName} {data.day?.dayNum} {data.day?.month} · {data.time} GST
update({ consent: e.target.checked })}
/>
I consent to Aesthetics International contacting me on the details above to
confirm a consultation appointment. I understand surgical procedures require
in-person consultation and that results are individual, not guaranteed.
Back
Submit Request
)}
{step === 4 && (
Thank you, {data.name.split(' ')[0] || 'there'}.
Reception will call you back on {data.countryCode} {data.phone} within
one business day to confirm your appointment for {data.day?.dayName} {data.day?.dayNum} {data.day?.month} at {data.time} GST .
Ref
AI-{(Math.random() * 90000 + 10000).toFixed(0)}
Treatment
{selectedTreatment?.label}
Consultant
{data.surgeon === 'any' ? 'Reception to match' : selectedSurgeon?.name}
By submitting you agreed to our minimal-PHI policy. Medical history is collected
in person, not online. We do not advertise guaranteed results.
)}
);
}
Object.assign(window, { BookingFlow });