/* global React, Arrow, BedIcon, BathIcon, AreaIcon, WhatsAppIcon, PhoneIcon */
// ============================================================
// Espace — forms + cards
// ============================================================
// ── Generic form runner ───────────────────────────────────────
function useForm(initial, validators = {}) {
const [values, setValues] = React.useState(initial);
const [errors, setErrors] = React.useState({});
const [submitted, setSubmitted] = React.useState(false);
const set = (k, v) => setValues(prev => ({ ...prev, [k]: v }));
const validate = () => {
const errs = {};
for (const k of Object.keys(validators)) {
const msg = validators[k](values[k], values);
if (msg) errs[k] = msg;
}
setErrors(errs);
return Object.keys(errs).length === 0;
};
const submit = (e) => {
e.preventDefault();
if (validate()) setSubmitted(true);
};
return { values, errors, submitted, set, submit };
}
const required = (v) => (!v || (typeof v === "string" && !v.trim())) ? "Required" : "";
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const validEmail = (v) => !v ? "Required" : !emailRe.test(v) ? "Enter a valid email" : "";
const validPhone = (v) => !v ? "Required" : (v.replace(/[^0-9]/g, "").length < 7) ? "Enter a valid phone" : "";
// ── Field ─────────────────────────────────────────────────────
function Field({ label, error, children, full }) {
return (
{children}
{error &&
{error}
}
);
}
// ── Viewing form ──────────────────────────────────────────────
function ViewingForm({ dark, communityId, agentId, listingRef, compact }) {
const f = useForm(
{ name: "", phone: "", email: "", listingRef: listingRef || "", date: "", time: "", msg: "", _gotcha: "" },
{ name: required, phone: validPhone, email: validEmail }
);
if (f.submitted) return ;
return (
);
}
// ── Valuation form ────────────────────────────────────────────
function ValuationForm({ dark }) {
const f = useForm(
{ name: "", phone: "", email: "", type: "Villa", community: "", beds: "", size: "", purpose: "Sell", _gotcha: "" },
{ name: required, phone: validPhone, email: validEmail }
);
if (f.submitted) return ;
return (
);
}
// ── Success panel ─────────────────────────────────────────────
function SuccessPanel({ dark, title, body, whatsapp, kind }) {
const msg = encodeURIComponent(`Hi Espace, I just requested a ${kind}.`);
return (
);
}
// ── Listing card (placeholder feed) ───────────────────────────
function ListingCard({ listing, onBook }) {
const statusLabel = { sale: "For sale", rent: "For rent", luxury: "Luxury", offplan: "Off-plan" }[listing.status] || listing.status;
return (
onBook && onBook(listing)}>
{statusLabel}
Photo loads from feed
Property image · {listing.ref}
{listing.ref}
{listing.title}
{(window.COMMUNITIES.find(c => c.id === listing.community) || {}).name}
{listing.beds} bed
{listing.baths} bath
{listing.sizeSqft.toLocaleString()} sqft
{listing.price}
);
}
// ── Agent card ────────────────────────────────────────────────
function AgentCard({ agent, onOpen }) {
return (
onOpen && onOpen(agent.id)}>
{agent.name}
{agent.role}
{agent.langs}
);
}
// ── Community card ────────────────────────────────────────────
function CommunityCard({ community, span, featured, onOpen }) {
const className = `community-card ${featured ? "featured" : ""} grid-span-${span}`;
return (
onOpen && onOpen(community.id)} role="link" tabIndex={0}
onKeyDown={(e) => { if (e.key === "Enter") onOpen && onOpen(community.id); }}>
{community.name}
{community.type} · {community.agents} consultants
);
}
// ── Communities grid (editorial bento) ───────────────────────
function CommunitiesGrid({ items, onOpen, layout }) {
// editorial bento pattern for 22 items
// 0: 6, 1: 6, 2: 4, 3: 4, 4: 4, 5: 3, 6: 3, 7: 3, 8: 3, then 4s
if (layout === "uniform") {
return (
{items.map(c => (
))}
);
}
const spans = [6, 6, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 6, 6, 4, 4, 4, 4];
return (
{items.map((c, i) => (
= 6}
onOpen={onOpen}
/>
))}
);
}
Object.assign(window, {
useForm, required, validEmail, validPhone, Field,
ViewingForm, ValuationForm, SuccessPanel,
ListingCard, AgentCard, CommunityCard, CommunitiesGrid,
});