/* ============================================================
Guero's Group — App router + i18n + Tweaks
============================================================ */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"groupColor": "#D8A046",
"sparesColor": "#C9622E",
"rentColor": "#3E84C4",
"displayFont": "Saira Condensed"
}/*EDITMODE-END*/;
const HUB_VIEWS = ["home", "group", "services", "coverage", "contact"];
const ALL_VIEWS = [...HUB_VIEWS, "spares", "rent", "basket"];
function App() {
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
const [lang, setLangState] = useState(() => {
try { return localStorage.getItem("gg_lang") || "fr"; } catch (e) { return "fr"; }
});
setActiveLang(lang); // ensure L() reflects current lang for THIS render (top-down)
const setLang = (l) => {
setActiveLang(l);
try { localStorage.setItem("gg_lang", l); } catch (e) {}
setLangState(l);
};
useEffect(() => { document.documentElement.lang = lang; }, [lang]);
const [view, setView] = useState(() => {
const h = (window.location.hash || "").replace("#", "");
return ALL_VIEWS.includes(h) ? h : "home";
});
const go = (v) => {
if (!ALL_VIEWS.includes(v)) v = "home";
if (v === view) { window.scrollTo({ top: 0, behavior: "smooth" }); return; }
setView(v);
window.location.hash = v;
window.scrollTo({ top: 0, behavior: "auto" });
};
useEffect(() => {
const onHash = () => {
const h = (window.location.hash || "").replace("#", "");
if (ALL_VIEWS.includes(h)) setView(h);
};
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
// apply tweak overrides to CSS custom properties
useEffect(() => {
const r = document.documentElement.style;
const darken = (hex, amt = 0.16) => {
const n = parseInt(hex.slice(1), 16);
let R = (n >> 16) & 255, G = (n >> 8) & 255, B = n & 255;
R = Math.round(R * (1 - amt)); G = Math.round(G * (1 - amt)); B = Math.round(B * (1 - amt));
return "#" + ((1 << 24) + (R << 16) + (G << 8) + B).toString(16).slice(1);
};
r.setProperty("--group", t.groupColor); r.setProperty("--group-d", darken(t.groupColor));
r.setProperty("--spares", t.sparesColor); r.setProperty("--spares-d", darken(t.sparesColor));
r.setProperty("--rent", t.rentColor); r.setProperty("--rent-d", darken(t.rentColor));
r.setProperty("--fdisp", `"${t.displayFont}", "Saira Condensed", sans-serif`);
}, [t]);
const Page = { home: Home, group: GroupPage, services: ServicesPage, coverage: CoveragePage, contact: ContactPage, spares: AfricaSpares, rent: RentACar, basket: BasketPage }[view] || Home;
return (
setLang(v)} />
setTweak("groupColor", v)} />
setTweak("sparesColor", v)} />
setTweak("rentColor", v)} />
setTweak("displayFont", v)} />
);
}
/* Wait for Supabase data to load (window.GG_READY from gg-supabase.js)
before mounting, so PARTS and FLEET are populated. Falls back to
mounting anyway after 5s if the network is slow. */
(function mountWhenReady() {
const mount = () => ReactDOM.createRoot(document.getElementById("root")).render();
if (window.GG_READY && typeof window.GG_READY.then === "function") {
const timeout = new Promise((res) => setTimeout(res, 5000));
Promise.race([window.GG_READY, timeout]).then(mount);
} else {
mount();
}
})();