import React, { useState, useEffect, useRef } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Dashboard } from './pages/Dashboard'; import { Checklist } from './pages/Checklist'; import { Documents } from './pages/Documents'; import { Scanner } from './pages/Scanner'; import { IncidentCenter } from './pages/IncidentCenter'; import { Settings } from './pages/Settings'; import { Education } from './pages/Education'; import { Navigation } from './components/Navigation'; import { ComplianceProvider } from './context/ComplianceContext'; import { useConfig, isDevMode, PLAN_OVERRIDE_KEY, dispatchPlanChange, externalApiFetch } from './hooks/useApi'; import type { DashboardStats } from '@cra-compliance/types'; type Page = 'dashboard' | 'checklist' | 'documents' | 'scanner' | 'incidents' | 'education' | 'settings'; export function App() { const config = useConfig(); const isPremium = config.plan !== 'free'; const [currentPage, setCurrentPage] = useState('dashboard'); const planOverride = isDevMode() ? localStorage.getItem(PLAN_OVERRIDE_KEY) : null; const statsQuery = useQuery({ queryKey: ['dashboard'], enabled: isPremium, queryFn: async () => { const res = await externalApiFetch<{ data: DashboardStats }>('/api/dashboard'); return res.data; }, staleTime: 5 * 60 * 1000, refetchInterval: 5 * 60 * 1000, }); const navBadges = isPremium && statsQuery.data ? { incidents: statsQuery.data.overdue_incidents || 0, scanner: statsQuery.data.critical_vulnerabilities || 0, } : undefined; // Prefetch scanner and incident data so pages load instantly const queryClient = useQueryClient(); useEffect(() => { if (!isPremium) return; void queryClient.prefetchQuery({ queryKey: ['plugins'], queryFn: () => externalApiFetch<{ data: unknown[] }>('/api/plugins').then((r) => r.data ?? []), staleTime: 60_000, }); void queryClient.prefetchQuery({ queryKey: ['vulnerabilities'], queryFn: () => externalApiFetch<{ data: unknown[] }>('/api/vulnerabilities').then((r) => r.data ?? []), staleTime: 60_000, }); void queryClient.prefetchQuery({ queryKey: ['incidents'], queryFn: () => externalApiFetch<{ data: unknown[] }>('/api/dashboard?action=incidents_list').then((r) => r.data ?? []), staleTime: 60_000, }); }, [isPremium, queryClient]); useEffect(() => { // Read initial page from hash const hash = window.location.hash.replace('#/', '').replace('#', ''); if (hash && isValidPage(hash)) { setCurrentPage(hash as Page); } const handleHashChange = () => { const newHash = window.location.hash.replace('#/', '').replace('#', ''); if (newHash && isValidPage(newHash)) { setCurrentPage(newHash as Page); } }; window.addEventListener('hashchange', handleHashChange); return () => window.removeEventListener('hashchange', handleHashChange); }, []); const mainRef = useRef(null); // Scroll to top on page change — try the main content area first, then window useEffect(() => { if (mainRef.current) { mainRef.current.scrollTop = 0; } window.scrollTo(0, 0); }, [currentPage]); const navigate = (page: Page) => { setCurrentPage(page); window.location.hash = `#/${page}`; }; return (
{planOverride && (
Plan override: {planOverride.toUpperCase()}
)}
{currentPage === 'dashboard' && } {currentPage === 'checklist' && } {currentPage === 'documents' && } {currentPage === 'scanner' && } {currentPage === 'incidents' && } {currentPage === 'education' && } {currentPage === 'settings' && }
); } function isValidPage(page: string): page is Page { return ['dashboard', 'checklist', 'documents', 'scanner', 'incidents', 'education', 'settings'].includes(page); }