/* =========================================================================
app.jsx — main app: hash routing, language switching, page composition
========================================================================= */
const { useState, useEffect, useMemo, useRef } = React;
/* -------- Hash routing helpers -------- */
const ROUTES = {
"": "home",
"/": "home",
"/cash": "cash",
"/waqf-services": "waqf",
"/government-employee":"gov",
"/real-estate-liquidity": "liq"
};
const useHashRoute = () => {
const [route, setRoute] = useState(() => {
const h = window.location.hash.replace(/^#/, "");
return ROUTES[h] || "home";
});
useEffect(() => {
const onChange = () => {
const h = window.location.hash.replace(/^#/, "");
setRoute(ROUTES[h] || "home");
window.scrollTo({ top: 0, behavior: "instant" });
track("page_view", { route: h || "/" });
};
window.addEventListener("hashchange", onChange);
track("page_view", { route: window.location.hash.replace(/^#/, "") || "/" });
return () => window.removeEventListener("hashchange", onChange);
}, []);
return route;
};
/* -------- Tracking placeholders (Meta / Snapchat / GA) --------
In production these IDs are filled and the SDKs are loaded in
.
Here we log to console so the wiring is visible.
*/
function track(event, payload = {}) {
const tagged = { event, ...payload, _ts: Date.now() };
// Meta Pixel
if (window.fbq) window.fbq("trackCustom", event, payload);
// Snapchat Pixel
if (window.snaptr) window.snaptr("track", event, payload);
// GA4
if (window.gtag) window.gtag("event", event, payload);
// visible log for the mockup
console.log("[track]", tagged);
}
window.track = track;
/* -------- Language hook -------- */
const useLang = () => {
const [lang, setLang] = useState(() => localStorage.getItem("sn_lang") || "ar");
useEffect(() => {
localStorage.setItem("sn_lang", lang);
document.body.dir = window.I18N[lang].dir;
document.body.dataset.lang = lang;
document.documentElement.lang = lang;
}, [lang]);
return [lang, setLang];
};
/* -------- UI atoms -------- */
const Logo = ({ size = 36 }) => (
);
const TopBar = ({ lang, setLang, t }) => (
);
/* -------- Hero -------- */
const Hero = ({ route, t, page }) => {
const onWhatsApp = (e) => {
track("whatsapp_click", { from: "hero", route });
e.preventDefault();
window.location.hash = window.location.hash; // no-op nav; in real site → wa.me link
window.open("https://wa.me/966550650034", "_blank", "noopener");
};
const onApply = () => {
track("cta_apply_click", { from: "hero", route });
const el = document.getElementById("lead-form");
if (el) {
const y = el.getBoundingClientRect().top + window.scrollY - 60;
window.scrollTo({ top: y, behavior: "smooth" });
}
};
return (
{route !== "home" && (
{t.common.adTag} · {route.toUpperCase()}
)}
{page.eyebrow}
{page.h1_pre}{" "}
{page.h1_pop}
{page.h1_post ? <>
{page.h1_post}> : null}
{page.sub}
{page.artImg ? (

) : (
{page.artTag}
{page.artLabel}
)}
{page.amount}
{page.amountLbl}
);
};
/* -------- Services -------- */
const Services = ({ t, route }) => {
const activeId = route === "home" ? null : route;
return (
{t.sections.servicesEyebrow}
{t.sections.servicesTitle}
{t.sections.servicesLede}
);
};
/* -------- Steps -------- */
const Steps = ({ t }) => (
{t.sections.stepsEyebrow}
{t.sections.stepsTitle}
{t.sections.stepsLede}
{t.steps.map((s, i) => (
{i + 1}
{s.title}
{s.sub}
))}
);
/* -------- Lead form -------- */
const LeadForm = ({ t, route }) => {
const [name, setName] = useState("");
const [mobile, setMobile] = useState("");
const [hasMortgage, setHM] = useState(null);
const [employer, setEmp] = useState(null);
const [errors, setErrors] = useState({});
const [sending, setSending] = useState(false);
const [sent, setSent] = useState(false);
// Pre-select gov for the government-employee landing
useEffect(() => {
if (route === "gov") setEmp("gov");
if (route === "cash" || route === "liq") setHM(true);
}, [route]);
const submit = (e) => {
e.preventDefault();
const errs = {};
if (!name.trim()) errs.name = t.common.required;
if (!/^05\d{8}$/.test(mobile.trim().replace(/\s|-/g, "")))
errs.mobile = t.common.invalidMobile;
setErrors(errs);
if (Object.keys(errs).length) {
track("form_error", { errors: Object.keys(errs), route });
return;
}
setSending(true);
track("form_submit", {
route,
hasMortgage: hasMortgage === null ? "unset" : hasMortgage,
employer: employer || "unset",
name_provided: !!name,
mobile_provided: !!mobile
});
// simulated network — real backend would replace this
setTimeout(() => {
setSending(false);
setSent(true);
track("form_submit_success", { route });
}, 900);
};
if (sent) {
return (
);
}
return (
);
};
/* -------- Trust + Contact -------- */
const Trust = ({ t }) => {
const onPhone = () => track("phone_click", { from: "trust" });
const onWA = () => track("whatsapp_click", { from: "trust" });
return (
{t.sections.trustEyebrow}
{t.sections.trustTitle}
{t.sections.trustLede}
{t.trust.map((x, i) => (
))}
);
};
/* -------- Bottom nav rail (mockup-only — lets reviewers click between ad pages) -------- */
const NavRail = ({ route, t }) => {
const items = [
{ key: "home", hash: "#/", label: t.nav.home, icon: "house" },
{ key: "cash", hash: "#/cash", label: t.nav.cash, icon: "cash" },
{ key: "waqf", hash: "#/waqf-services", label: t.nav.waqf, icon: "unlock" },
{ key: "gov", hash: "#/government-employee", label: t.nav.gov, icon: "badge" },
{ key: "liq", hash: "#/real-estate-liquidity", label: t.nav.liq, icon: "trending" },
];
return (
);
};
/* -------- Footer -------- */
const Foot = ({ t }) => (
);
/* -------- App root -------- */
const App = () => {
const route = useHashRoute();
const [lang, setLang] = useLang();
const t = window.I18N[lang];
const page = t.pages[route] || t.pages.home;
return (
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render();