// Unique Furniture — main app shell. // Owns routing, cart state, tweaks (palette/font/financing/density/lang), and // the announcement marquee. const { useState, useEffect, useCallback } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "palette": "default", "displayFont": "Cormorant Garamond", "uiFont": "Manrope", "financingMode": "normal", "layoutDensity": "regular", "lang": "en", "showAnnounce": true }/*EDITMODE-END*/; const PALETTE_OPTIONS = [ { key: "default", label: "Editorial Warm", colors: ["#F4EEE3", "#6B2B2B", "#1F1A14", "#9A7B43"] }, { key: "dark", label: "Showroom Dark", colors: ["#1A1612", "#C8995A", "#F2EAD9", "#C8995A"] }, { key: "coastal", label: "Coastal Classic", colors: ["#F2EEE7", "#1F3A5F", "#14202B", "#B89968"] }, { key: "sun", label: "Miami Sun", colors: ["#F6EAD9", "#B14418", "#2A1A0E", "#B98837"] }, ]; const DISPLAY_FONTS = ["Cormorant Garamond", "Playfair Display", "DM Serif Display", "Tenor Sans"]; const UI_FONTS = ["Manrope", "DM Sans", "Outfit"]; function App() { // ── Routing (state-based, with hash sync for shareability) ────────────── const [route, setRoute] = useState(() => parseHash().route); const [productId, setProductId] = useState(() => parseHash().productId); const [shopCat, setShopCat] = useState(() => parseHash().shopCat); useEffect(() => { const h = parseHash(); setRoute(h.route); setProductId(h.productId); setShopCat(h.shopCat); const onHash = () => { const h = parseHash(); setRoute(h.route); setProductId(h.productId); setShopCat(h.shopCat); }; window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); const navigate = useCallback((to, arg) => { if (to === "product" && arg) { window.location.hash = `product/${arg}`; } else if (to === "shop" && arg) { window.location.hash = `shop/${encodeURIComponent(arg)}`; } else { window.location.hash = to; } window.scrollTo({ top: 0, behavior: "instant" }); }, []); // ── Cart ── const [cart, setCart] = useState([]); const addToCart = useCallback((item) => { setCart((c) => { const i = c.findIndex((x) => x.id === item.id && x.color === item.color && x.size === item.size); if (i >= 0) { const next = [...c]; next[i] = { ...next[i], qty: next[i].qty + item.qty }; return next; } return [...c, { id: item.id, qty: item.qty, color: item.color, size: item.size }]; }); // tiny toast via flash setToast(`Added ${item.name} to cart`); setTimeout(() => setToast(null), 2400); }, []); const [toast, setToast] = useState(null); // ── Tweaks ── const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); // Apply palette + fonts to useEffect(() => { document.documentElement.setAttribute("data-palette", t.palette); document.documentElement.style.setProperty("--display", `"${t.displayFont}", serif`); document.documentElement.style.setProperty("--ui", `"${t.uiFont}", ui-sans-serif, system-ui, sans-serif`); }, [t.palette, t.displayFont, t.uiFont]); // Translation pack const lang = t.lang in window.I18N ? t.lang : "en"; const i18n = window.I18N[lang]; const cartCount = cart.reduce((s, c) => s + c.qty, 0); // ── Announcement marquee ── const [annIdx, setAnnIdx] = useState(0); useEffect(() => { if (!t.showAnnounce) return; const id = setInterval(() => setAnnIdx((i) => (i + 1) % i18n.announce.length), 4500); return () => clearInterval(id); }, [t.showAnnounce, i18n]); // ── Render ── let page; if (route === "home") page = ; else if (route === "shop") page = ; else if (route === "product") page = ; else if (route === "cart") page = ; else if (route === "checkout") page = ; else if (route === "about") page = ; else if (route === "contact") page = ; else if (route === "collections") page = ; else if (route === "workshop") page = ; else page = ; return ( <> {t.showAnnounce && (
{i18n.announce[annIdx]}
)}
setTweak("lang", l)} t={i18n} /> {page}