/* App shell: hash router, navbar, footer, toasts, scroll-reveal */ const { useState: uS, useEffect: uE, useCallback: uC } = React; /* ---- routing helpers (global) ---- */ window.navigate = (path) => {window.location.hash = '#' + path;window.scrollTo({ top: 0, behavior: 'auto' });}; window.scrollToId = (id) => { if (window.location.hash && window.location.hash !== '#/' && !window.location.hash.startsWith('#/?')) { window.location.hash = '#/'; setTimeout(() => {const el = document.getElementById(id);if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 80, behavior: 'smooth' });}, 60); } else { const el = document.getElementById(id);if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 80, behavior: 'smooth' }); } }; window.flashCond = () => { const run = () => { const el = document.getElementById('dieu-kien-qua'); if (!el) return; window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 90, behavior: 'smooth' }); el.classList.remove('flash'); void el.offsetWidth; el.classList.add('flash'); }; if (window.location.hash && window.location.hash !== '#/' && !window.location.hash.startsWith('#/?')) { window.location.hash = '#/'; setTimeout(run, 80); } else { run(); } }; function parseHash() { const raw = (window.location.hash || '#/').replace(/^#/, ''); const [path, qs] = raw.split('?'); return { path: path || '/', query: new URLSearchParams(qs || '') }; } function Navbar() { const links = [ ['Giới thiệu', 'gioi-thieu'], ['Agenda', 'agenda'], ['Diễn giả', 'dien-gia'], ['Quyền lợi', 'qua-tang']]; return (
{links.map(([t, id]) => window.scrollToId(id)}>{t})} window.navigate('/trai-nghiem-phan-mem')}>Trải nghiệm phần mềm window.scrollToId('dang-ky')}>Đăng ký tham dự
); } function Footer() { const C = window.VTC.CONTACT; return ( ); } /* ---- toasts ---- */ function ToastHost() { const [items, setItems] = uS([]); uE(() => { window.toast = (t) => { const id = Date.now() + Math.random(); setItems((p) => [...p, { ...t, id }]); setTimeout(() => setItems((p) => p.filter((x) => x.id !== id)), 4200); }; }, []); return (
{items.map((t) =>
{t.title}{t.sub && {t.sub}}
)}
); } /* ---- scroll reveal ---- */ function useReveal(dep) { uE(() => { 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 App() { const [route, setRoute] = uS(parseHash()); uE(() => { const on = () => setRoute(parseHash()); window.addEventListener('hashchange', on); return () => window.removeEventListener('hashchange', on); }, []); useReveal(route.path); const { path, query } = route; const isForm = path !== '/'; let page; if (path === '/khao-sat') page = ;else if (path === '/bang-kiem-phong-kham') page = ;else if (path === '/trai-nghiem-phan-mem') page = ;else if (path === '/cam-on') page = ;else page = ; return ( <> {page}