// ===== Shared Components =====
const { useState, useEffect, useMemo, useRef, useCallback } = React;
// Simple hash-based router
function useHashRoute() {
const parse = () => {
const h = window.location.hash.replace(/^#\/?/, "") || "beranda";
const [path, ...rest] = h.split("/");
return { path, params: rest };
};
const [route, setRoute] = useState(parse());
useEffect(() => {
const onChange = () => {
setRoute(parse());
window.scrollTo({ top: 0, behavior: "instant" });
};
window.addEventListener("hashchange", onChange);
return () => window.removeEventListener("hashchange", onChange);
}, []);
return route;
}
function navigate(path) {
window.location.hash = "/" + path;
}
// Link component
function L({ to, children, className, onClick, ...rest }) {
const handle = (e) => {
e.preventDefault();
if (onClick) onClick(e);
navigate(to);
};
return (
{children}
);
}
// Placeholder image
function PH({ label, tint, className, style, ratio, children }) {
const tintClass = tint ? "tinted-" + tint : "";
const aspectStyle = ratio ? { aspectRatio: ratio, ...style } : style;
return (
{children || (label && {label})}
);
}
// FontAwesome icon helper
// or
function Icon({ name, size, box, boxSize, fontSize, style, className, brand }) {
const prefix = brand ? "fa-brands" : "fa-solid";
const i = ;
if (!box) {
return {i};
}
return (
{i}
);
}
// Logo / brand mark
function Brand({ size = 38 }) {
return (
BZ
BatamZoo
);
}
// Navigation
function Nav() {
const route = useHashRoute();
const [open, setOpen] = useState(false);
const links = [
{ to: "beranda", label: "Beranda" },
{ to: "fauna", label: "Fauna" },
{ to: "fasilitas", label: "Fasilitas" },
{ to: "tiket", label: "Beli Tiket" },
{ to: "edukasi", label: "Edukasi" },
{ to: "galeri", label: "Galeri" },
{ to: "tentang", label: "Tentang" },
{ to: "kontak", label: "Kontak" }
];
return (
<>
Promo Family Pack — Hemat 23% untuk pembelian online minggu ini · Berakhir Minggu pukul 23:59
>
);
}
// Footer
function Footer() {
return (
);
}
// Page head reusable
function PageHead({ crumb, title, sub, tone }) {
return (
{crumb &&
{crumb}
}
{title}
{sub &&
{sub}
}
);
}
// Section header
function SectionHeader({ eyebrow, title, sub, align = "left", action }) {
return (
{eyebrow &&
{eyebrow}
}
{title}
{sub &&
{sub}
}
{action &&
{action}
}
);
}
// Toast
function Toast({ text, onDone }) {
useEffect(() => {
const t = setTimeout(onDone, 2400);
return () => clearTimeout(t);
}, [text, onDone]);
return (
{text}
);
}
// Counter for tickets
function Counter({ value, onChange, min = 0, max = 20 }) {
return (
{value}
);
}
// Format Rupiah
function rupiah(n) {
return "Rp" + n.toLocaleString("id-ID");
}
Object.assign(window, {
useHashRoute, navigate, L, PH, Brand, Nav, Footer,
PageHead, SectionHeader, Toast, Counter, rupiah,
useState, useEffect, useMemo, useRef, useCallback
});