/* ============================================ App — Router between public site and admin ============================================ */ /* Global helpers for toasts and navigation */ window.toast = (msg, kind = "success") => { window.dispatchEvent(new CustomEvent("app-toast", { detail: { msg, kind } })); }; window.navTo = (page) => { window.dispatchEvent(new CustomEvent("app-nav", { detail: page })); }; function ToastStack() { const [items, setItems] = useState([]); useEffect(() => { const handler = (e) => { const id = Date.now() + Math.random(); setItems(arr => [...arr, { id, ...e.detail }]); setTimeout(() => setItems(arr => arr.filter(i => i.id !== id)), 3400); }; window.addEventListener("app-toast", handler); return () => window.removeEventListener("app-toast", handler); }, []); const icon = (k) => k === "warn" ? "AlertTriangle" : k === "info" ? "Info" : "CheckCircle2"; return (
{items.map(it => (
{it.msg}
))}
); } function PublicSite({ onAdminClick }) { const [active, setActive] = useState("inicio"); const onNav = (id) => { setActive(id); const el = document.getElementById(id); if (el) el.scrollIntoView({ behavior: "smooth", block: "start" }); }; useEffect(() => { const sections = ["inicio", "nosotros", "servicios", "galeria", "contacto"]; const obs = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting && e.intersectionRatio > 0.3) setActive(e.target.id); }); }, { threshold: [0.3, 0.6] }); sections.forEach(id => { const el = document.getElementById(id); if (el) obs.observe(el); }); return () => obs.disconnect(); }, []); return ( <>