import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom' import { useState, Fragment } from 'react' import { Menu, Transition } from '@headlessui/react' import { HomeIcon, MapIcon, DocumentTextIcon, BellAlertIcon, BuildingLibraryIcon, Cog6ToothIcon, ChartBarIcon, MagnifyingGlassIcon, BookOpenIcon, UserGroupIcon, AcademicCapIcon, Bars3Icon, XMarkIcon, UserCircleIcon, ArrowRightOnRectangleIcon, ChevronDownIcon, MapPinIcon, HeartIcon, CodeBracketIcon, XCircleIcon, } from '@heroicons/react/24/outline' import { useAuth } from '../contexts/AuthContext' import { useLocation as useLocationContext } from '../contexts/LocationContext' const navigation = [ { name: 'Home', href: '/', icon: HomeIcon }, { name: 'Explore Data', href: '/explore', icon: MagnifyingGlassIcon }, { name: 'Search', href: '/search', icon: MagnifyingGlassIcon }, { name: 'Jurisdictions', href: '/jurisdictions', icon: MapPinIcon }, { section: 'Families & Individuals', items: [ { name: 'Community Events', href: '/events', icon: BookOpenIcon }, { name: 'Services & Resources', href: '/services', icon: HeartIcon }, ] }, { section: 'Policy & Government', items: [ { name: 'Policy Decisions', href: '/documents', icon: DocumentTextIcon }, { name: 'Budget Analysis', href: '/analytics', icon: ChartBarIcon }, { name: 'Elected Officials', href: '/people', icon: UserGroupIcon }, { name: 'Policy Map', href: '/policy-map', icon: MapIcon }, ] }, { section: 'Community & Advocacy', items: [ { name: 'Nonprofits', href: '/nonprofits', icon: BuildingLibraryIcon }, { name: 'Advocacy Topics', href: '/advocacy-topics', icon: BellAlertIcon }, { name: 'Fact-Checking', href: '/fact-checking', icon: AcademicCapIcon }, ] }, { section: 'Developers', items: [ { name: 'Open Source', href: '/opensource', icon: CodeBracketIcon }, { name: 'Hackathons', href: '/hackathons', icon: AcademicCapIcon }, ] }, { name: 'Settings', href: '/settings', icon: Cog6ToothIcon }, ] export default function Layout() { const location = useLocation() const navigate = useNavigate() const [searchQuery, setSearchQuery] = useState('') const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [showLoginMenu, setShowLoginMenu] = useState(false) const { user, isAuthenticated, login, logout, isLoading, authError, clearAuthError } = useAuth() const { location: userLocation, hasLocation } = useLocationContext() // Environment-aware URLs const docsUrl = import.meta.env.PROD ? 'https://www.communityone.com/docs/intro' : 'http://localhost:3000/docs/intro' const apiDocsUrl = import.meta.env.PROD ? 'https://www.communityone.com/api/docs' : 'http://localhost:8000/docs' const handleSearch = (e: React.FormEvent) => { e.preventDefault() if (searchQuery.trim()) { navigate(`/search?q=${encodeURIComponent(searchQuery)}`) } } return (
{/* Top Header Bar */}
{/* Mobile menu button */} CommunityOne Logo

Open Navigator

{/* Global Search - Hidden on home page and mobile */} {location.pathname !== '/' && (
setSearchQuery(e.target.value)} className="w-full px-4 py-2 pl-10 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent" />
)} {/* Header Actions */}
{/* Location Banner - Compact */} {hasLocation && userLocation && (
{userLocation.city}, {userLocation.state}
{userLocation.county && (
{userLocation.county}
)}
)} {/* Authentication */} {isLoading ? (
) : isAuthenticated && user ? ( {user.avatar_url ? ( {user.full_name { // If image fails to load, hide it and show fallback e.currentTarget.style.display = 'none'; const fallback = e.currentTarget.nextElementSibling as HTMLElement | null; if (fallback) fallback.style.display = 'flex'; }} /> ) : null}
{(user.full_name || user.username || user.email).charAt(0).toUpperCase()}
{user.full_name || user.username || user.email.split('@')[0]}
{user.avatar_url ? ( {user.full_name { // If image fails to load, hide it and show fallback e.currentTarget.style.display = 'none'; const fallback = e.currentTarget.nextElementSibling as HTMLElement | null; if (fallback) fallback.style.display = 'flex'; }} /> ) : null}
{(user.full_name || user.username || user.email).charAt(0).toUpperCase()}

{user.full_name || user.username || user.email.split('@')[0]}

{user.email}

{user.oauth_provider && (
Signed in via {user.oauth_provider}
)}
{({ active }) => ( )} {({ active }) => ( )} {({ active }) => ( )}
) : (
{showLoginMenu && (

Sign in with:

)}
)} Docs e.currentTarget.style.backgroundColor = '#2e4346'} onMouseLeave={(e) => e.currentTarget.style.backgroundColor = '#354F52'} > API
{/* Sidebar */}
{/* Sidebar Footer */}
Open Data Sources
925 Jurisdictions
43,726 Nonprofits
• 6,913 Meeting Pages
362 Officials
📍 Request Jurisdiction Coverage
{/* Mobile menu overlay */} {mobileMenuOpen && (
setMobileMenuOpen(false)} /> )} {/* Main content */}
{/* Auth Error Banner - Mobile Friendly */} {authError && (

Login failed

{authError}

)}
) }