/* global React, ReactDOM,
loadConfig, applyConfig, EBV_CONFIG_KEY,
EmergencyBar, Nav, Hero, Services, About, Hours, Testimonials, Gallery, Appointment, Contact, Footer, FAB, CallModal */
// ── Reveal on scroll ────────────────────────────────────────────────────────
function useRevealOnScroll(deps) {
React.useEffect(() => {
const io = new IntersectionObserver(
(entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add("is-in");
io.unobserve(e.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
const t = setTimeout(() => {
document.querySelectorAll(".reveal:not(.is-in)").forEach((el) => io.observe(el));
}, 50);
return () => { clearTimeout(t); io.disconnect(); };
}, deps || []);
}
function useSmoothScroll() {
React.useEffect(() => {
const onClick = (e) => {
const a = e.target.closest('a[href^="#"]');
if (!a) return;
const id = a.getAttribute("href").slice(1);
if (!id) return;
const el = document.getElementById(id);
if (!el) return;
e.preventDefault();
const top = el.getBoundingClientRect().top + window.scrollY - 80;
window.scrollTo({ top, behavior: "smooth" });
};
document.addEventListener("click", onClick);
return () => document.removeEventListener("click", onClick);
}, []);
}
// ── Live config: async load (URL ?f=slug or localStorage) ─────────────────
function useLiveConfig() {
const [cfg, setCfg] = React.useState(null);
const slug = React.useMemo(() => window.parseConfigSlug(), []);
// Initial async load
React.useEffect(() => {
let alive = true;
window.loadConfigAsync().then((c) => {
if (!alive) return;
window.applyConfig(c);
setCfg(c);
});
return () => { alive = false; };
}, []);
// Apply on changes (after initial load)
React.useLayoutEffect(() => { if (cfg) window.applyConfig(cfg); }, [cfg]);
// Listen for live edits from the ayarlar page (same browser, localStorage)
// — but only when no slug is set, since a slug means the source of truth
// is the JSON file, not localStorage.
React.useEffect(() => {
if (slug) return undefined;
const onStorage = (e) => {
if (e.key === window.EBV_CONFIG_KEY) setCfg(window.loadConfig());
};
const onCustom = () => setCfg(window.loadConfig());
const onMessage = (e) => {
if (e.data?.type === "ebv-config-change") setCfg(window.loadConfig());
};
window.addEventListener("storage", onStorage);
window.addEventListener("ebv-config-change", onCustom);
window.addEventListener("message", onMessage);
return () => {
window.removeEventListener("storage", onStorage);
window.removeEventListener("ebv-config-change", onCustom);
window.removeEventListener("message", onMessage);
};
}, [slug]);
// Update document title from config
React.useEffect(() => {
if (cfg) document.title = `${cfg.brandName} · ${cfg.brandSub} · 7/24 Açık`;
}, [cfg?.brandName, cfg?.brandSub]);
return cfg;
}
// ── App ─────────────────────────────────────────────────────────────────────
function App() {
const cfg = useLiveConfig();
const [callOpen, setCallOpen] = React.useState(false);
useSmoothScroll();
useRevealOnScroll([cfg?.palette, cfg?.fontPair, cfg?.density]);
if (!cfg) return null; // brief splash; CSS variables already applied default
const onCall = () => setCallOpen(true);
return (
<>
{cfg.showTestimonials && }
{cfg.showGallery && }
{cfg.showAppointmentForm && }
setCallOpen(false)} />
>
);
}
ReactDOM.createRoot(document.getElementById("root")).render();