import { useEffect } from 'react'
import { useQuery } from '@tanstack/react-query'
import { Routes, Route, Navigate, useParams, useLocation } from 'react-router-dom'
import { tracer } from './instrumentation'
import Layout from './components/Layout'
import ScrollToTop from './components/ScrollToTop'
import Home from './pages/Home'
import HomeV9 from './pages/HomeV9'
import PolicyQuestionsPage from './pages/PolicyQuestionsPage'
import PolicyQuestionPage from './pages/PolicyQuestionPage'
import BrowseTopics from './pages/BrowseTopics'
import BrowseCauses from './pages/BrowseCauses'
import MoneyTalk from './pages/MoneyTalk'
import Dashboard from './pages/Dashboard'
import Analytics from './pages/Analytics'
import Heatmap from './pages/Heatmap'
import Documents from './pages/Documents'
import Opportunities from './pages/Opportunities'
import Nonprofits from './pages/Nonprofits'
import Efile990Viewer from './pages/Efile990Viewer'
import NonprofitsHF from './pages/NonprofitsHF'
import Settings from './pages/Settings'
import PeopleFinder from './pages/PeopleFinder'
import PersonDetail from './pages/PersonDetail'
import DebateFinder from './pages/DebateGrader'
import Profile from './pages/Profile'
import FeedSetup from './pages/FeedSetup'
import Explore from './pages/Explore'
import Events from './pages/Events'
import Services from './pages/Services'
import Developers from './pages/Developers'
import Support from './pages/Support'
import JurisdictionMappingQualityPage from './pages/JurisdictionMappingQualityPage'
import LighthouseReportPage from './pages/LighthouseReportPage'
import BatchJobStatusPage from './pages/BatchJobStatusPage'
import Hackathons from './pages/Hackathons'
import OpenSource from './pages/OpenSource'
import AdvocacyTopics from './pages/AdvocacyTopics'
import FactChecking from './pages/FactChecking'
import UnifiedSearch from './pages/UnifiedSearch'
import JurisdictionsSearch from './pages/JurisdictionsSearch'
import PolicyMap from './pages/PolicyMap'
import DecisionsMapPage from './pages/DecisionsMapPage'
import CensusMapPage from './pages/CensusMapPage'
import CensusDrilldownMapPage from './pages/CensusDrilldownMapPage'
import DataExplorerLayout from './components/DataExplorerLayout'
import AdminLayout from './components/AdminLayout'
import DataExplorerScorecardPage from './pages/DataExplorerScorecardPage'
import BillDetail from './pages/BillDetail'
import GrantDetail from './pages/GrantDetail'
import DecisionDetail from './pages/DecisionDetail'
import MeetingDetail from './pages/MeetingDetail'
import MeetingDocuments from './pages/MeetingDocuments'
import MeetingCompare from './pages/MeetingCompare'
import EventBillDetail from './pages/EventBillDetail'
import NotFound from './pages/NotFound'
import { DATA_EXPLORER_MAP_BASE } from './utils/dataExplorerPaths'
/** Old route-based drill-down (`/map/state/13/...`, `/map/place/13/...`) is now in-page;
* redirect those bookmarks to the unified `/map/us/{vintage}/{metric}` entry. */
function LegacyStatePlaceRedirect() {
const { vintage, metric } = useParams<{ vintage: string; metric: string }>()
const { search } = useLocation()
const v = vintage ?? '2024'
const m = metric ?? 'median_household_income'
return
}
/** `/map-v2/...` was the staging URL while the new page lived alongside the old one. */
function MapV2AliasRedirect() {
const { pathname, search } = useLocation()
const next = pathname.replace(/^\/data-explorer\/map-v2/, DATA_EXPLORER_MAP_BASE)
return
}
/** Old bookmarked URLs used `/census-map/county/...`; national view is now state-level under Data explorer. */
function CensusCountyAliasRedirect() {
const { vintage, metric } = useParams<{ vintage: string; metric: string }>()
const { search } = useLocation()
const v = vintage ?? '2024'
const m = metric ?? 'median_household_income'
return
}
function DataExplorerMapDefaultRedirect() {
const { data, isError, isPending } = useQuery({
queryKey: ['data-explorer-map-root-redirect'],
queryFn: async (): Promise => {
const rm = await fetch('/data/census-map/manifest.json')
if (!rm.ok) throw new Error('manifest')
const manifest = (await rm.json()) as {
vintage?: string
vintages?: string[]
metrics?: { slug: string }[]
}
const mv =
Array.isArray(manifest.vintages) && manifest.vintages.length > 0
? manifest.vintages
: manifest.vintage
? [manifest.vintage]
: []
let vintages = mv
const rt = await fetch('/data/census-map/state_trends.json')
if (rt.ok) {
const t = (await rt.json()) as { vintages?: string[] }
if (t.vintages?.length) vintages = t.vintages
}
const v = vintages.length ? vintages[vintages.length - 1]! : (manifest.vintage ?? '2024')
const metric = manifest.metrics?.[0]?.slug ?? 'median_household_income'
return `${DATA_EXPLORER_MAP_BASE}/us/${v}/${metric}`
},
})
if (isPending) return Loading map…
if (isError) return
return
}
/** Rewrites `/census-map/...` bookmarks to `/data-explorer/map/...`. */
function LegacyCensusMapRedirect() {
const { pathname, search } = useLocation()
if (pathname === '/census-map' || pathname === '/census-map/') {
return
}
const path = pathname.replace(/^\/census-map/, DATA_EXPLORER_MAP_BASE)
return
}
/**
* Emits a short `route.change` span on every navigation. Rendered inside the
* Router so `useLocation()` is available. Attribute is the path only (no query
* string) to keep cardinality bounded and avoid leaking search terms.
*/
function RouteChangeTracer() {
const location = useLocation()
useEffect(() => {
const span = tracer.startSpan('route.change', {
attributes: { 'route.path': location.pathname },
})
span.end()
}, [location.pathname])
return null
}
function App() {
return (
<>
{/* v9 prototype homepage (single-column; has its own header) */}
} />
{/* Previous homepage, kept reachable for comparison/rollback */}
} />
{/* All other pages with sidebar layout */}
}>
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
}>
} />
} />
} />
{/* Lighthouse report + batch jobs moved to the Admin area; keep old URLs working. */}
} />
} />
} />
} />
} />
} />
{/* Legacy staging URLs while the drilldown lived alongside the old page.
Anyone with a bookmark to /map-v2/... lands on the canonical /map/... now. */}
} />
{/* Old CensusMapPage retained at `/map-legacy/...` for one-click rollback if needed. */}
} />
} />
} />
{/* Admin area — operational tools, reachable from the logged-in profile menu. */}
}>
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
}
/>
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
}
/>
} />
} />
} />
} />
{/* 404 Page - Catch all unmatched routes */}
} />
>
)
}
export default App