/* 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 (
e.stopPropagation()}>
VietCanThi
{window.SITE.NAV.map((item) => ( goTo(item.to)}>{item.label} {item.children && item.children.map(([t, to]) => goTo(to)}>{t})} ))}
{ onClose(); window.toast({ title: 'Đăng nhập', sub: 'Chuyển tới app.vietcanthi.com (placeholder).' }); }}>Đăng nhập goTo('webinar.html#/trai-nghiem-phan-mem')}>Dùng thử miễn phí
); } function SiteNav() { const [menu, setMenu] = sS(false); sE(() => { document.body.style.overflow = menu ? 'hidden' : ''; }, [menu]); return (
{window.SITE.NAV.map((item) => ( item.children ? ( window.go(item.to)}>{item.label}
{item.children.map(([t, to]) => window.go(to)}>{t})}
) : ( window.go(item.to)}>{item.label} ) ))}
window.toast({ title: 'Đăng nhập', sub: 'Chuyển tới app.vietcanthi.com (placeholder).' })}>Đăng nhập window.go('webinar.html#/trai-nghiem-phan-mem')}>Dùng thử miễn phí
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).

window.go('/')}>← Về trang chủ window.go('webinar.html')}>Xem landing webinar (Bước 1)
); } 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 && (
Webinar KSCT — 20:00 Thứ Ba 23/06 · window.go('webinar.html')}>Đăng ký miễn phí →
)} {renderRoute(route)} ); } Object.assign(window, { SiteApp });