// Facette — Modals (Book, Skin Quiz, Lightbox) const { BRANCHES: B_DATA, THERAPISTS, QUIZ_STEPS, recommend, SIGNATURES, TREATMENT_CATEGORIES } = window.FacetteData; const ALL_TREATMENTS = TREATMENT_CATEGORIES.flatMap((c) => c.items); // ────────────────────────────────────────────────────────── // Generic modal scrim // ────────────────────────────────────────────────────────── function Modal({ onClose, children, wide }) { React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); return (
e.stopPropagation()}> {children}
); } // ────────────────────────────────────────────────────────── // Lightbox // ────────────────────────────────────────────────────────── function Lightbox({ src, onClose }) { React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); return (
); } // ────────────────────────────────────────────────────────── // Booking modal // ────────────────────────────────────────────────────────── function BookModal({ branch, treatment, onClose, onBranchChange }) { const b = B_DATA[branch]; const [step, setStep] = React.useState(treatment ? 1 : 0); // Resolve treatment to full record if needed const resolvedTreatment = React.useMemo(() => { if (!treatment) return null; const found = ALL_TREATMENTS.find((t) => t.name === treatment.name || t.slug === treatment.slug); return found || treatment; }, [treatment]); const [pickedTreatment, setPickedTreatment] = React.useState(resolvedTreatment); const [therapist, setTherapist] = React.useState(null); const [date, setDate] = React.useState(null); const [time, setTime] = React.useState(null); const eligibleTherapists = THERAPISTS.filter((t) => t.branches.includes(branch) && t.role !== 'GP & Aesthetics'); // Build a fake date set (next 7 days) const dates = React.useMemo(() => { const arr = []; const today = new Date('2026-05-22'); for (let i = 0; i < 7; i++) { const d = new Date(today); d.setDate(today.getDate() + i); arr.push({ iso: d.toISOString().slice(0, 10), label: d.toLocaleDateString('en-GB', { weekday: 'short' }), day: d.getDate(), month: d.toLocaleDateString('en-GB', { month: 'short' }), }); } return arr; }, []); const times = ['10:00', '11:30', '13:00', '14:30', '16:00', '17:30', '19:00']; // Make a couple slots "booked" based on date hash const unavailable = (d, t) => { const seed = (d.charCodeAt(d.length - 1) + t.charCodeAt(0)) % 7; return seed === 0 || seed === 3; }; return (
{step === 0 && ( { setPickedTreatment(t); setStep(1); }} branch={branch} onBranchChange={onBranchChange} /> )} {step === 1 && ( setStep(2)} onBack={() => setStep(0)} /> )} {step === 2 && ( setStep(1)} onNext={() => setStep(3)} /> )} {step === 3 && ( d.iso === date)} time={time} onBack={() => setStep(2)} onConfirm={() => setStep(4)} /> )} {step === 4 && ( d.iso === date)} time={time} therapist={therapist} onClose={onClose} /> )}
); } function BookSide({ branch, b, treatment, step }) { return (
Booking · {b.short}

Book your
facial.

Four steps. About 90 seconds. Cancellation up to 24 hours before your slot.

{treatment && (
You picked
{treatment.name}
{treatment.duration} {!treatment.tbc && · {(treatment.price ?? treatment.priceFrom).toLocaleString()} AED}
)}
); } function ProgressDots({ step, total }) { const labels = ['Treatment', 'Therapist', 'Date & Time', 'Confirm']; return (
{labels.map((l, i) => (
{i < step ? '✓' : i + 1}
{l}
))}
); } function PickTreatment({ onPick, branch, onBranchChange }) { const [cat, setCat] = React.useState(TREATMENT_CATEGORIES[0].id); const current = TREATMENT_CATEGORIES.find((c) => c.id === cat); return (

Pick a facial

Filter by category. You can change this later.

{TREATMENT_CATEGORIES.map((c) => ( ))}
{current.items.map((it, i) => ( ))}
); } function PickTherapist({ therapists, selected, onSelect, onNext, onBack }) { return (

Pick a therapist

Or leave it to us — we will match by concern.

{therapists.map((t) => ( ))}
); } function PickTime({ dates, times, date, setDate, time, setTime, unavailable, onBack, onNext }) { return (

Pick a date

Live availability — confirmed when you submit.

{dates.map((d) => ( ))}
{date && (

Pick a time

7 slots shown. Tap a faded slot to be waitlisted.

{times.map((t) => { const off = unavailable(date, t); return ( ); })}
)}
); } function Confirm({ branch, treatment, therapist, date, time, onBack, onConfirm }) { const b = B_DATA[branch]; const therapistName = therapist === '__any' ? 'Any available therapist' : therapist?.name; const total = treatment?.price ?? treatment?.priceFrom ?? '—'; return (

Review your booking

Last look before we hold the slot.

Branch
Facette · {b.short}
Treatment
{treatment?.name}
With
{therapistName}
When
{date?.label}, {date?.day} {date?.month} · {time}
Duration
{treatment?.duration}
Total
{typeof total === 'number' ? `${total.toLocaleString()} AED` : 'On request'}
); } function Success({ branch, treatment, date, time, therapist, onClose }) { const b = B_DATA[branch]; return (

You are booked.

We have sent a confirmation to your inbox. Show up five minutes early — Phia at reception has your name.

{treatment?.name}
{date?.label}, {date?.day} {date?.month} · {time}
{b.short}
{therapist === '__any' ? 'Therapist TBA' : therapist?.name}
); } // ────────────────────────────────────────────────────────── // Skin Quiz modal // ────────────────────────────────────────────────────────── function QuizModal({ onClose, onBook }) { const [step, setStep] = React.useState(0); const [answers, setAnswers] = React.useState([]); const total = QUIZ_STEPS.length; const onSelect = (val) => { const next = [...answers]; next[step] = val; setAnswers(next); }; const onNext = () => { if (step < total) setStep(step + 1); }; const onBack = () => { if (step > 0) setStep(step - 1); }; const done = step === total; const rec = done ? recommend(answers) : null; const recTreatment = rec ? ALL_TREATMENTS.find((t) => t.slug === rec.slug) : null; return (
Skin quiz
{Array.from({ length: total }).map((_, i) => (
))}
{!done && (
Step {step + 1} of {total}

{QUIZ_STEPS[step].q}

{QUIZ_STEPS[step].sub}

{QUIZ_STEPS[step].options.map((o) => ( ))}
)} {done && recTreatment && (
Based on your answers, we recommend

{recTreatment.name}

Why this
{rec.reason}
)}
); } window.BookModal = BookModal; window.QuizModal = QuizModal; window.Lightbox = Lightbox;