/* ── Router + mount ── */
const PUBLIC_PAGES = NAV.map(n => n.key).concat(['register', 'login']);
const KNOWN = PUBLIC_PAGES.concat(['admin', 'article']);
function pageFromHash() {
const h = (location.hash || '').replace(/^#\/?/, '').split('?')[0];
if (!h) return 'home';
return KNOWN.includes(h) ? h : 'notfound';
}
function NotFound({ navigate }) {
return (
404
لم نجد الصفحة
Halaman Tidak Ditemukan
Maaf, halaman yang Anda cari tidak ada atau telah dipindahkan.
);
}
function AdminHome({ user, navigate, onLogout }) {
return (

Dashboard MQA
{user.name} · {user.role}
Selamat datang, {user.name.split(' ')[0]} 👋
Login berhasil melalui lapisan API (mode: {api.mode}). Modul dashboard lengkap akan di-port ke struktur no-build ini berikutnya.
{['Artikel', 'Pendaftar', 'Program', 'Donasi', 'Toko Buku', 'Pengaturan'].map(m => (
))}
);
}
function App() {
const [page, setPage] = useState(pageFromHash);
const [user, setUser] = useState(null);
const [activeArticle, setActiveArticle] = useState(null);
const [authorView, setAuthorView] = useState(null);
useFadeUp(page + ':' + (activeArticle ? activeArticle.id : ''));
const openArticle = (a) => { setActiveArticle(a); setPage('article'); location.hash = '#/article?id=' + a.id; window.scrollTo(0, 0); if (window.SEO) SEO.article(normArticle(a)); };
useEffect(() => { window.__openArticle = openArticle; }, []);
// Saat refresh di #/article?id=… → pulihkan artikel yang sedang dibaca
useEffect(() => {
if (page === 'article' && !activeArticle) {
const id = new URLSearchParams((location.hash.split('?')[1] || '')).get('id');
if (id) {
const local = ((window.MQA || {}).articles || []).find(x => String(x.id) === String(id));
if (local) setActiveArticle(local);
else api.get('articles.php?action=list').then(r => {
const found = r.ok && r.articles && r.articles.find(x => String(x.id) === String(id));
if (found) setActiveArticle(found);
});
}
}
}, [page]);
const navigate = (p) => {
setPage(p);
const hash = '#/' + p;
if (location.hash !== hash) location.hash = hash;
window.scrollTo(0, 0);
};
useEffect(() => {
const onHash = () => setPage(pageFromHash());
window.addEventListener('hashchange', onHash);
// cek sesi tersimpan
api.get('auth.php?action=me').then(r => { if (r.ok) setUser(r.user); });
return () => window.removeEventListener('hashchange', onHash);
}, []);
// Update SEO (judul + meta) tiap halaman berubah
useEffect(() => {
if (!window.SEO) return;
if (page === 'article') { if (activeArticle) SEO.article(normArticle(activeArticle)); }
else if (!['admin', 'login'].includes(page)) SEO.page(page);
}, [page, activeArticle]);
const onLogin = (u) => { setUser(u); navigate('admin'); };
const onLogout = () => { api.post('auth.php?action=logout'); setUser(null); navigate('home'); };
// Halaman admin (butuh login)
if (page === 'admin') {
if (!user) return ;
return ;
}
if (page === 'login') {
if (user) return ;
return ;
}
let content;
if (page === 'home') content = ;
else if (page === 'articles') content = ;
else if (page === 'article') content = activeArticle ? : ;
else if (page === 'programs') content = ;
else if (page === 'teachers') content = ;
else if (page === 'gallery') content = ;
else if (page === 'kegiatan') content = ;
else if (page === 'about') content = ;
else if (page === 'contact') content = ;
else if (page === 'donasi') content = ;
else if (page === 'register') content = ;
else if (page === 'toko') content = ;
else content = ;
return (
<>
{content}
{authorView && setAuthorView(null)} />}
navigate('articles')} />
>
);
}
ReactDOM.createRoot(document.getElementById('root')).render();