// Shared components — Nav, Footer, Buttons, Icons, FAB, Modal
const { useState, useEffect, useRef, useMemo } = React;
// ---------- Icons ----------
const Icon = ({ name, size = 20 }) => {
const s = size;
const paths = {
arrow: ,
phone: ,
wa: <>>,
mail: <>>,
pin: <>>,
calendar: <>>,
play: ,
check: ,
classroom: <>>,
art: <>>,
music: <>>,
book: <>>,
media: <>>,
field: <>>,
tree: <>>,
gym: <>>,
star: ,
globe: <>>,
leaf: <>>,
shield: ,
sparkle: <>>,
sun: <>>,
heart: ,
close: <>>,
menu: <>>,
chev: ,
chevDown: ,
instagram: <>>,
facebook: ,
};
return (
);
};
// ---------- Brand mark (mini-crest as SVG fallback so it always renders) ----------
const BrandMark = ({ size = 44 }) => (
);
// ---------- Locale switch ----------
const LocaleSwitch = ({ locale, onChange }) => (
{["EN", "AR", "RU", "ZH"].map(l => (
))}
);
// ---------- Nav ----------
const Nav = ({ route, navigate, locale, setLocale, onBookTour }) => {
const items = [
["home", "Home"],
["curriculum", "Curriculum"],
["programmes", "Programmes"],
["facilities", "Facilities"],
["branches", "Branches"],
["admissions", "Admissions"],
["about", "About"]
];
return (
);
};
// ---------- WhatsApp FAB ----------
const Fab = () => (
Chat on WhatsApp
);
// ---------- Mobile sticky CTA ----------
const MobileCta = ({ onBookTour }) => (
);
// ---------- Modal ----------
const Modal = ({ open, onClose, children }) => {
useEffect(() => {
if (!open) return;
const onKey = (e) => e.key === "Escape" && onClose();
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open, onClose]);
if (!open) return null;
return (
e.stopPropagation()}>
{children}
);
};
// ---------- Footer ----------
const Footer = ({ navigate }) => {
const D = window.OdysseyData;
return (
);
};
// ---------- Tour booking form (multi-step) ----------
const TourForm = ({ onClose, presetBranch }) => {
const D = window.OdysseyData;
const [step, setStep] = useState(0);
const [done, setDone] = useState(false);
const [data, setData] = useState({
branch: presetBranch || "",
age: "",
name: "",
phone: "",
email: "",
childAge: "",
when: "",
msg: ""
});
const update = (k, v) => setData(d => ({ ...d, [k]: v }));
const steps = [
{ title: "Which branch?", help: "Pick the campus that's most convenient." },
{ title: "About your child", help: "We'll match the right age group and key person." },
{ title: "Your contact", help: "We reply within one working day." }
];
const next = () => setStep(s => Math.min(s + 1, 2));
const back = () => setStep(s => Math.max(s - 1, 0));
const submit = () => setDone(true);
if (done) {
return (
Tour request received
Thank you, {data.name || "friend"}. A member of the Odyssey admissions team will reply within one working day.
);
}
const canNext =
(step === 0 && data.branch) ||
(step === 1 && data.age) ||
(step === 2 && data.name && data.phone);
return (
Book a Tour · Step {step + 1} of 3
{[0, 1, 2].map(i => (
))}
{steps[step].title}
{steps[step].help}
{step === 0 && (
{D.branches.map(b => (
))}
)}
{step === 1 && (
{D.ageGroups.map(a => (
))}
update("childAge", e.target.value)} />
)}
{step === 2 && (
)}
{step < 2 ? (
) : (
)}
);
};
Object.assign(window, { Icon, BrandMark, Nav, Footer, Fab, MobileCta, Modal, TourForm });