import { useEffect, useState } from 'react'; import { assetsUrl, isPremium } from './lib/api'; import { useScheduledScanRunner } from './hooks/useScheduledScanRunner'; import Dashboard from './components/Dashboard'; import ReportView from './components/ReportView'; import WidgetConfigurator from './components/WidgetConfigurator'; import StatementGenerator from './components/StatementGenerator'; import Settings from './components/Settings'; import MediaAccessibility from './components/MediaAccessibility'; type View = '' | 'report' | 'widget' | 'statement' | 'settings' | 'media'; // Mirrors accessmate.php's $view_map in reverse — the WP admin submenu slug // each view corresponds to, so the native sidebar can be kept in sync. const WP_SLUG_BY_VIEW: Record = { '': 'accessmate', report: 'accessmate-report', media: 'accessmate-media', widget: 'accessmate-widget', statement: 'accessmate-statement', settings: 'accessmate-settings', }; function parseHash(): View { const valid: View[] = ['', 'report', 'widget', 'statement', 'settings', 'media']; const raw = window.location.hash.replace('#/', '').replace('#', ''); // Check hash presence, not raw's truthiness — '' is itself a valid View // (Dashboard), so a falsy check here would wrongly fall through to // initialView below whenever the hash explicitly points at Dashboard. if (window.location.hash.length > 0 && valid.includes(raw as View)) return raw as View; // No hash set — use the view PHP passed when navigating from the WP sidebar. const initial = (window as any).accessmateConfig?.initialView as string | undefined; return initial && valid.includes(initial as View) ? (initial as View) : ''; } export default function App() { const [view, setView] = useState(parseHash); // Picks up a due Scheduled Scan and runs it via the same axe-core pipeline // as Bulk/Individual scans. No-ops entirely on Free. useScheduledScanRunner(); useEffect(() => { const onHashChange = () => setView(parseHash()); window.addEventListener('hashchange', onHashChange); return () => window.removeEventListener('hashchange', onHashChange); }, []); // Switching tabs here is a client-side hash change, not a real page load — // so the native WP admin sidebar (rendered once, server-side, from the URL // that was actually requested) never gets a chance to re-run its own // "which submenu item is current" logic and just keeps showing whichever // page we originally landed on. Keep it in sync manually instead. useEffect(() => { const topLevel = document.getElementById('toplevel_page_accessmate'); if (!topLevel) return; const slug = WP_SLUG_BY_VIEW[view]; topLevel.querySelectorAll(':scope > .wp-submenu > li').forEach((li) => { const href = li.querySelector('a')?.getAttribute('href') ?? ''; let page: string | null = null; try { page = new URL(href, window.location.href).searchParams.get('page'); } catch { /* ignore */ } li.classList.toggle('current', page === slug); }); }, [view]); const navigate = (v: View) => { window.location.hash = v ? `/${v}` : '/'; setView(v); }; return (
{/* Sidebar nav */} {/* Main content */}
{view === '' && } {view === 'report' && } {view === 'widget' && } {view === 'statement' && } {view === 'settings' && } {view === 'media' && isPremium && }
); } function NavItem({ active, onClick, children, }: { active: boolean; onClick: () => void; children: React.ReactNode; }) { return (
  • ); }