// Forms + Modals + Lead docks const { useState: useStateF, useEffect: useEffectF } = React; function RegisterInterestSection({ lang }) { const t = I18N[lang]; const [data, setData] = useStateF({ name: "", phone: "", email: "", residence: ["Two-bedroom"], timeline: "0-3 months", nationality: "United Arab Emirates", }); const [errors, setErrors] = useStateF({}); const [submitted, setSubmitted] = useStateF(false); const toggleRes = (key) => { const cur = data.residence; const next = cur.includes(key) ? cur.filter(k => k !== key) : [...cur, key]; setData({ ...data, residence: next }); }; const submit = (e) => { e.preventDefault(); const errs = {}; if (!data.name.trim()) errs.name = "Required"; if (!data.phone.trim() || data.phone.replace(/\D/g, "").length < 7) errs.phone = "Enter a valid phone"; if (!/^\S+@\S+\.\S+$/.test(data.email)) errs.email = "Enter a valid email"; setErrors(errs); if (Object.keys(errs).length === 0) { setSubmitted(true); } }; return (
07 — Get in

The first tranche will go to people on this list.

Specifications, floor plates, payment plans and final pricing are with the developer for sign-off. Register and we'll send the private brochure 48 hours ahead of public release — and a viewing slot reserved in your name.

LH
Louis Harding
Off-plan launch lead · Confirm current
{submitted ? (

You're on the list.

We'll be in touch within one business day — and we'll send the private brochure 48 hours before public release. Check your inbox for confirmation.

Continue on WhatsApp
) : (
Register interest — 2 minutes

Tell us a little about you.

setData({ ...data, name: e.target.value })} /> {errors.name &&
{errors.name}
}
setData({ ...data, phone: e.target.value })} /> {errors.phone &&
{errors.phone}
}
setData({ ...data, email: e.target.value })} /> {errors.email &&
{errors.email}
}
{["One-bedroom", "Two-bedroom", "Three-bedroom", "Four-bedroom · penthouse"].map(k => ( ))}

By submitting you agree to be contacted by a Betterhomes consultant about this launch. We don't share your details, and you can opt out at any time.

)}
); } // ---------- Book Viewing Modal (3-step) ---------- function BookViewingModal({ open, onClose }) { const [step, setStep] = useStateF(0); const [pick, setPick] = useStateF({ date: null, time: null, name: "", phone: "", email: "", channel: "in-person" }); const [errors, setErrors] = useStateF({}); const [done, setDone] = useStateF(false); useEffectF(() => { if (!open) { setStep(0); setDone(false); setPick({ date: null, time: null, name: "", phone: "", email: "", channel: "in-person" }); setErrors({}); } }, [open]); if (!open) return null; const today = new Date(); const dates = Array.from({ length: 14 }, (_, i) => { const d = new Date(today); d.setDate(today.getDate() + i); return d; }); const weekdayNames = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]; const times = ["10:00", "11:00", "12:00", "14:00", "15:00", "16:00", "17:00", "18:00"]; const next = () => { if (step === 0) { const errs = {}; if (!pick.date) errs.date = "Pick a date"; if (!pick.time) errs.time = "Pick a time"; setErrors(errs); if (Object.keys(errs).length) return; } if (step === 1) { const errs = {}; if (!pick.name.trim()) errs.name = "Required"; if (!pick.phone.trim()) errs.phone = "Required"; if (!/^\S+@\S+\.\S+$/.test(pick.email)) errs.email = "Valid email required"; setErrors(errs); if (Object.keys(errs).length) return; } if (step < 2) { setStep(step + 1); setErrors({}); } else { setDone(true); } }; return (
{ if (e.target === e.currentTarget) onClose(); }}>

{done ? "Viewing booked" : "Book a viewing"}

{!done && (
{[0, 1, 2].map(i => )}
)} {done ? (

{pick.date && pick.date.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long" })} · {pick.time}

You'll receive a calendar invite at {pick.email}. A consultant will WhatsApp you 24 hours before to confirm.

WhatsApp now
) : step === 0 ? ( <>
Step 1 of 3 · Pick a slot

Choose a date

{dates.map((d, i) => (
setPick({ ...pick, date: d })}>
{d.getDate()}
{weekdayNames[d.getDay()]}
))}
{errors.date &&
{errors.date}
}

Choose a time

{times.map(tm => (
setPick({ ...pick, time: tm })}>{tm}
))}
{errors.time &&
{errors.time}
}
{["in-person", "video walkthrough"].map(ch => ( ))}
) : step === 1 ? ( <>
Step 2 of 3 · Your details

Who should we expect?

setPick({ ...pick, name: e.target.value })} /> {errors.name &&
{errors.name}
}
setPick({ ...pick, phone: e.target.value })} /> {errors.phone &&
{errors.phone}
}
setPick({ ...pick, email: e.target.value })} /> {errors.email &&
{errors.email}
}
) : ( <>
Step 3 of 3 · Confirm

Confirm your viewing

Project
Butterfly Towers · Business Bay
Date
{pick.date && pick.date.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long", year: "numeric" })}
Time
{pick.time}
Format
{pick.channel}
Name
{pick.name}
Contact
{pick.phone} · {pick.email}

A consultant will contact you within one business day to confirm location, parking and what to bring.

)} {!done && (
{step > 0 ? ( ) : }
)}
); } // ---------- Lead docks ---------- function LeadBar({ visible, onBookViewing, onRegister }) { const t = I18N["en"]; return (
Butterfly Towers
Pre-launch · POA
); } function LeadRail({ visible, onBookViewing, onRegister }) { return (
); } Object.assign(window, { RegisterInterestSection, BookViewingModal, LeadBar, LeadRail });