/* VietCanThi website shell: navbar (dropdown + auth), footer, router, toast, reveal */
const { useState: sS, useEffect: sE } = React;
const isExternal = (to) => /^https?:|\.html/.test(to);
window.go = (to) => {
if (isExternal(to)) { window.location.href = to; return; }
window.location.hash = '#' + to; window.scrollTo({ top: 0, behavior: 'auto' });
};
window.scrollToId = (id) => { const el = document.getElementById(id); if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 80, behavior: 'smooth' }); };
function siteRoute() { return (window.location.hash || '#/').replace(/^#/, '') || '/'; }
const Caret = () => ;
const Burger = () => ;
function MobileSheet({ open, onClose }) {
const goTo = (to) => { onClose(); window.go(to); };
return (
);
}
function SiteNav() {
const [menu, setMenu] = sS(false);
sE(() => { document.body.style.overflow = menu ? 'hidden' : ''; }, [menu]);
return (
setMenu(false)} />
);
}
function SiteFooter() {
return (
);
}
function Stub({ route }) {
const NAMES = {
'/phan-mem': 'Trang Phần mềm', '/dich-vu': 'Trang Dịch vụ', '/su-kien': 'Trang Sự kiện',
'/tai-nguyen': 'Tài nguyên', '/ve-chung-toi': 'Về chúng tôi', '/lien-he': 'Liên hệ',
'/can-cu-khoa-hoc': 'Căn cứ khoa học',
};
const base = '/' + route.split('/')[1];
const name = NAMES[route] || NAMES[base] || 'Trang';
return (
Đang dựng
{name}
Trang này nằm trong các bước tiếp theo của lộ trình (Phần mềm → Bước 3 · Dịch vụ/Tài nguyên/Về chúng tôi → Bước 4).
);
}
function ToastHost() {
const [items, setItems] = sS([]);
sE(() => { window.toast = (t) => { const id = Date.now() + Math.random(); setItems((p) => [...p, { ...t, id }]); setTimeout(() => setItems((p) => p.filter((x) => x.id !== id)), 4000); }; }, []);
return {items.map((t) =>
{t.title}{t.sub && {t.sub}}
)}
;
}
function useReveal(dep) {
sE(() => {
const els = document.querySelectorAll('.reveal:not(.in)');
if (!('IntersectionObserver' in window)) { els.forEach((e) => e.classList.add('in')); return; }
const io = new IntersectionObserver((ents) => ents.forEach((en) => { if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); } }), { threshold: 0.12 });
els.forEach((e) => io.observe(e)); return () => io.disconnect();
}, [dep]);
}
function renderRoute(route) {
if (route === '/') return ;
if (route === '/phan-mem') return ;
if (route === '/dich-vu') return ;
if (route.startsWith('/dich-vu/')) return ;
if (route === '/tai-nguyen') return ;
if (route === '/ve-chung-toi') return ;
if (route === '/lien-he') return ;
if (route === '/can-cu-khoa-hoc') return ;
return ;
}
function SiteApp() {
const [route, setRoute] = sS(siteRoute());
sE(() => { const on = () => setRoute(siteRoute()); window.addEventListener('hashchange', on); return () => window.removeEventListener('hashchange', on); }, []);
useReveal(route);
const showPromo = route === '/';
return (
<>
{showPromo && (
)}
{renderRoute(route)}
>
);
}
Object.assign(window, { SiteApp });