/* global React, ReactDOM, Nav, Footer, GalleryPage, ProductPage, SampleRequestPage, CompareModal, Icon, useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakSelect */ const { useState, useEffect, useMemo, useCallback } = React; // ===== Tweak defaults ===== const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "density": "default", "gridType": "editorial", "filterPos": "left-rail", "accent": "terracotta" }/*EDITMODE-END*/; // ===== Simple hash router ===== function useRouter() { const [path, setPath] = useState(() => parsePath()); useEffect(() => { const onHash = () => setPath(parsePath()); window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); const navigate = useCallback((to) => { window.location.hash = "#" + to; window.scrollTo({ top: 0, behavior: "instant" }); }, []); return [path, navigate]; } function parsePath() { const raw = (window.location.hash || "").replace(/^#/, "") || "/gallery"; const [pathname, queryStr] = raw.split("?"); const query = {}; if (queryStr) { queryStr.split("&").forEach(pair => { const [k, v] = pair.split("="); if (k) query[decodeURIComponent(k)] = decodeURIComponent(v || ""); }); } return { pathname: pathname || "/gallery", query }; } function App() { const [{ pathname, query }, navigate] = useRouter(); const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); const [catalog, setCatalog] = React.useState(window.PAPERS || []); const [catalogLoading, setCatalogLoading] = React.useState(true); const [catalogError, setCatalogError] = React.useState(null); React.useEffect(function() { if (window.IndieAPI) { window.IndieAPI.getCatalog() .then(function(data) { if (data && data.length > 0) setCatalog(data); setCatalogLoading(false); }) .catch(function(err) { setCatalogError(err.message); setCatalogLoading(false); }); } else { setCatalog(window.PAPERS || []); setCatalogLoading(false); } }, []); // Apply accent variant useEffect(() => { const root = document.documentElement; if (tweaks.accent === "yellow-ink") { root.style.setProperty("--accent", "#1A1A18"); root.style.setProperty("--accent-hover", "#000"); root.style.setProperty("--terracotta", "#BFA024"); root.style.setProperty("--terracotta-deep", "#9A8014"); } else if (tweaks.accent === "ink-only") { root.style.setProperty("--accent", "#1A1A18"); root.style.setProperty("--accent-hover", "#000"); root.style.setProperty("--terracotta", "#1A1A18"); root.style.setProperty("--terracotta-deep", "#000"); } else { // terracotta default — clear overrides root.style.removeProperty("--accent"); root.style.removeProperty("--accent-hover"); root.style.removeProperty("--terracotta"); root.style.removeProperty("--terracotta-deep"); } }, [tweaks.accent]); // ----- Cart / Compare state (in-memory) ----- const [cart, setCart] = useState([]); const [cartOpen, setCartOpen] = useState(false); const removeFromCart = useCallback((index) => { setCart(c => c.filter((_, i) => i !== index)); }, []); const updateCartQty = useCallback((index, qty) => { setCart(c => c.map((item, i) => i === index ? {...item, qty} : item)); }, []); const [lastOrderNumber, setLastOrderNumber] = useState(null); // ── Auth state ────────────────────────────────────────── const [authUser, setAuthUser] = useState(null); const [authProfile, setAuthProfile] = useState(null); const [authModalOpen, setAuthModalOpen] = useState(false); React.useEffect(() => { if (window.IndieAuth) { window.IndieAuth.init(); return window.IndieAuth.onAuthChange(({ session, profile }) => { setAuthUser(session?.user || null); setAuthProfile(profile || null); }); } }, []); const [compare, setCompare] = useState([]); const [compareOpen, setCompareOpen] = useState(false); const addToCart = useCallback((paper, opts = {}) => { setCart(c => [...c, { paper, format: opts.format || (Object.keys(paper.price || {})[0]) || "A4", qty: opts.qty || (paper.moq || 10), at: Date.now() }]); flashToast(`✓ Ditambahkan: ${paper.name}`); }, []); const toggleCompare = useCallback((paper) => { setCompare(c => { if (c.find(x => x.id === paper.id)) return c.filter(x => x.id !== paper.id); if (c.length >= 3) { flashToast("Maksimum 3 kertas untuk dibandingkan"); return c; } return [...c, paper]; }); }, []); const addSample = useCallback((paper) => { try { const cur = JSON.parse(localStorage.getItem("ip_samples") || "[]"); if (!cur.find(x => x.id === paper.id)) { cur.push({ id: paper.id, brand: paper.brand, name: paper.name, gsm: paper.gsm, finish: paper.finish, tone: paper.swatchTone }); localStorage.setItem("ip_samples", JSON.stringify(cur)); } } catch (e) {} }, []); // ----- Route dispatch ----- let page = null; if (pathname.startsWith("/product/")) { const id = pathname.replace("/product/", ""); page = ; } else if (pathname === "/sample-request") { page = ; } else if (pathname === "/profile") { page = ; } else if (pathname === "/orders") { page = ; } else if (pathname === "/about") { page = ; } else if (pathname === "/contact") { page = ; } else if (pathname === "/checkout") { page = { setLastOrderNumber(num); setCart([]); }} authProfile={authProfile} />; } else if (pathname.startsWith("/order-success")) { page = ; } else if (pathname.startsWith("/order-failed")) { page = ; } else { page = setCompareOpen(true)} query={query} catalog={catalog} catalogLoading={catalogLoading} catalogError={catalogError} />; } return ( <>