// app.jsx — router + Tweaks + mount
(function () {
const { useState, useEffect, useCallback } = React;
const Icon = window.Icon;
const { Header, Footer, FloatActions, Home, Courses, CourseDetail, Verify, About, Gallery, Testimonials, Contact, Login, Dashboard } = window;
const { useTweaks, TweaksPanel, TweakSection, TweakColor, TweakRadio, TweakSlider } = window;
/* ---------- routing helpers ---------- */
function parseHash() {
const h = (window.location.hash || "#home").replace(/^#/, "");
const [route, slug] = h.split("/");
return { route: route || "home", slug: slug || null };
}
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#f8b518",
"heading": "Plus Jakarta Sans",
"cards": "shadowed",
"radius": 20
}/*EDITMODE-END*/;
function App() {
const [{ route, slug }, setLoc] = useState(parseHash());
const [verifyPrefill, setVerifyPrefill] = useState("");
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
const navigate = useCallback((r, s = null, prefill = "") => {
setVerifyPrefill(prefill || "");
const hash = "#" + r + (s ? "/" + s : "");
if (window.location.hash !== hash) window.location.hash = hash;
else setLoc({ route: r, slug: s });
window.scrollTo({ top: 0, behavior: "auto" });
}, []);
useEffect(() => {
const onHash = () => { setLoc(parseHash()); window.scrollTo({ top: 0 }); };
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
/* apply tweaks to :root */
useEffect(() => {
const r = document.documentElement.style;
const a = t.accent;
r.setProperty("--accent", a);
// derive a darker shade for hover/text
r.setProperty("--accent-600", shade(a, -10));
r.setProperty("--accent-700", shade(a, -22));
r.setProperty("--accent-soft", tint(a, 78));
r.setProperty("--sh-accent", "0 10px 26px " + hexA(a, .35));
}, [t.accent]);
useEffect(() => {
const r = document.documentElement.style;
r.setProperty("--r-xl", (t.radius + 8) + "px");
r.setProperty("--r-lg", t.radius + "px");
r.setProperty("--r-md", Math.max(8, t.radius - 6) + "px");
r.setProperty("--r-sm", Math.max(6, t.radius - 10) + "px");
}, [t.radius]);
useEffect(() => {
document.documentElement.style.setProperty("--font", '"' + t.heading + '", ui-sans-serif, system-ui, sans-serif');
}, [t.heading]);
useEffect(() => {
document.body.setAttribute("data-cards", t.cards);
}, [t.cards]);
let page;
switch (route) {
case "courses": page = ; break;
case "course": page = ; break;
case "verify": page = ; break;
case "about": page = ; break;
case "gallery": page = ; break;
case "testimonials": page = ; break;
case "contact": page = ; break;
case "login": page = ; break;
case "dashboard": case "admin": page = ; break;
default: page = ;
}
return (
{page}
setTweak("accent", v)} />
setTweak("heading", v)} />
setTweak("radius", v)} />
setTweak("cards", v)} />
);
}
/* ---------- color utils ---------- */
function clamp(n) { return Math.max(0, Math.min(255, n)); }
function parseHex(h) { h = h.replace("#", ""); if (h.length === 3) h = h.split("").map((c) => c + c).join(""); return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)]; }
function toHex(r, g, b) { return "#" + [r, g, b].map((x) => clamp(Math.round(x)).toString(16).padStart(2, "0")).join(""); }
function shade(hex, pct) { const [r, g, b] = parseHex(hex); const f = 1 + pct / 100; return toHex(r * f, g * f, b * f); }
function tint(hex, amt) { const [r, g, b] = parseHex(hex); return toHex(r + (255 - r) * amt / 100, g + (255 - g) * amt / 100, b + (255 - b) * amt / 100); }
function hexA(hex, a) { const [r, g, b] = parseHex(hex); return "rgba(" + r + "," + g + "," + b + "," + a + ")"; }
ReactDOM.createRoot(document.getElementById("root")).render();
})();