import React, { useState } from 'react'; import HomePage from './components/HomePage'; import ImpactDashboard from './components/ImpactDashboard'; import TopicNavigation from './components/TopicNavigation'; import DecisionCard from './components/shared/DecisionCard'; import SplitScreenView from './components/SplitScreenView'; import NonprofitCard from './components/NonprofitCard'; import { metadata } from './data/dashboardData'; import { Home, Grid, Building2, Heart, Church, Search, X, Calendar, Filter, MapPin, Edit2, Lightbulb } from 'lucide-react'; export default function App() { const [viewMode, setViewMode] = useState('home'); // 'home', 'impact', 'browse', 'split-screen' const [exploreMode, setExploreMode] = useState('decisions'); // 'decisions', 'organizations', or 'causes' const [sectorView, setSectorView] = useState('all'); // 'all', 'public', 'nonprofits', 'churches' const [selectedPersona, setSelectedPersona] = useState(null); const [selectedTopic, setSelectedTopic] = useState(null); const [selectedDecision, setSelectedDecision] = useState(null); const [selectedTopics, setSelectedTopics] = useState([]); const [selectedPatterns, setSelectedPatterns] = useState([]); const [selectedResources, setSelectedResources] = useState([]); const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [quickDateFilter, setQuickDateFilter] = useState('all'); // 'all', '7days', '30days', '90days' const [quickTopicFilter, setQuickTopicFilter] = useState('all'); // 'all', 'health', 'education', 'infrastructure' const [jurisdictionType, setJurisdictionType] = useState('city'); // 'nation', 'state', 'county', 'city', 'school-district' const [jurisdictionName, setJurisdictionName] = useState('Tuscaloosa'); const [jurisdictionState, setJurisdictionState] = useState('AL'); const [showLocationModal, setShowLocationModal] = useState(false); const [tempJurisdictionType, setTempJurisdictionType] = useState('city'); const [tempJurisdictionName, setTempJurisdictionName] = useState(''); const [tempJurisdictionState, setTempJurisdictionState] = useState(''); const handlePersonaSelect = (persona, topic) => { setSelectedPersona(persona); setSelectedTopic(topic); setViewMode('impact'); }; const handleTopicSelect = (topicId) => { setSelectedTopics([topicId]); setViewMode('browse'); }; const handleTopicToggle = (topicId) => { setSelectedTopics(prev => prev.includes(topicId) ? prev.filter(t => t !== topicId) : [...prev, topicId] ); }; const handlePatternToggle = (patternId) => { setSelectedPatterns(prev => prev.includes(patternId) ? prev.filter(p => p !== patternId) : [...prev, patternId] ); }; const handleResourceToggle = (resourceId) => { setSelectedResources(prev => prev.includes(resourceId) ? prev.filter(r => r !== resourceId) : [...prev, resourceId] ); }; const handleClearFilters = () => { setSelectedTopics([]); setSelectedPatterns([]); setSelectedResources([]); setStartDate(null); setEndDate(null); setSearchQuery(''); setQuickDateFilter('all'); setQuickTopicFilter('all'); }; const handleQuickDateFilter = (filter) => { setQuickDateFilter(filter); const today = new Date(); switch(filter) { case '7days': const sevenDaysAgo = new Date(today); sevenDaysAgo.setDate(today.getDate() - 7); setStartDate(sevenDaysAgo.toISOString().split('T')[0]); setEndDate(today.toISOString().split('T')[0]); break; case '30days': const thirtyDaysAgo = new Date(today); thirtyDaysAgo.setDate(today.getDate() - 30); setStartDate(thirtyDaysAgo.toISOString().split('T')[0]); setEndDate(today.toISOString().split('T')[0]); break; case '90days': const ninetyDaysAgo = new Date(today); ninetyDaysAgo.setDate(today.getDate() - 90); setStartDate(ninetyDaysAgo.toISOString().split('T')[0]); setEndDate(today.toISOString().split('T')[0]); break; case 'all': default: setStartDate(null); setEndDate(null); break; } }; const handleQuickTopicFilter = (filter) => { setQuickTopicFilter(filter); if (filter === 'all') { setSelectedTopics([]); } else { setSelectedTopics([filter]); } }; const handleBackToHome = () => { setViewMode('home'); setSelectedPersona(null); setSelectedTopic(null); setSelectedDecision(null); }; const handleDecisionClick = (decision) => { setSelectedDecision(decision); setViewMode('split-screen'); }; const handleSectorSelect = (sector) => { setViewMode('browse'); // Set explore mode and sector based on selection if (sector === 'public') { setExploreMode('decisions'); setSectorView('public'); } else if (sector === 'nonprofits' || sector === 'churches') { setExploreMode('organizations'); setSectorView(sector); } else if (sector === 'all') { setExploreMode('organizations'); setSectorView('all'); } }; // Example decision data - would come from Python export const exampleDecisions = [ { decision_summary: "Approval of $850,000 athletic turf replacement project", outcome: "Approved", primary_rationale: "Athletic facilities are essential for student engagement and community pride. The current turf is beyond its useful life and poses safety concerns.", supporters: [ { name: "Board Member Johnson", role: "Board Member" }, { name: "Athletic Director Smith", role: "Staff" } ], opponents: [ { name: "Parent Coalition for Health", role: "Public" } ], vote_result: "6-1", meeting_date: "2026-03-15", tradeoffs_discussed: ["Athletic facilities vs. health screening programs"], evidence_cited: [{ source: "Athletic Department Report" }], policy_domain: "facilities", ntee_code: "N20" // Recreation, Sports, and Athletics }, { decision_summary: "Tabled decision on dental screening partnership with West Alabama Dental Clinic", outcome: "Tabled for further study", primary_rationale: "Risk management concerns require additional legal review and liability analysis before proceeding with external health partnerships.", supporters: [ { name: "Patricia Johnson, Risk Manager", role: "Staff" } ], opponents: [ { name: "Dr. Sarah Martinez", role: "Public" }, { name: "Parent Teacher Association", role: "Public" }, { name: "Board Member Williams", role: "Board Member" } ], vote_result: "5-2 to table", meeting_date: "2026-01-18", tradeoffs_discussed: ["Preventive care vs. perceived liability risk"], evidence_cited: [{ source: "Risk Management Memo" }], policy_domain: "health", ntee_code: "E32", // School-Based Health Care community_gap: { description: "100% of surveyed parents want dental screenings for students", nonprofit_filling_gap: true } }, { decision_summary: "Reduction of nursing staff from 5 FTE to 3 FTE", outcome: "Approved", primary_rationale: "Budget constraints necessitate cost reductions. Nursing positions will be restructured to focus on emergency response rather than preventive services.", supporters: [ { name: "Superintendent Brown", role: "Staff" }, { name: "Board Chair Thompson", role: "Board Member" } ], opponents: [ { name: "School Nurses Association", role: "Public" }, { name: "Board Member Lee", role: "Board Member" } ], vote_result: "4-3", meeting_date: "2026-02-20", tradeoffs_discussed: ["Cost savings vs. preventive health services"], evidence_cited: [{ source: "FY2026 Budget Proposal" }], policy_domain: "budget", ntee_code: "E40", // Health - General and Rehabilitative community_gap: { description: "Students lost access to preventive health services", nonprofit_filling_gap: true } } ]; // Example nonprofit data - would come from IRS/GuideStar API const exampleNonprofits = [ { name: "West Alabama Dental Initiative", ein: "63-1234567", ntee_code: "E32", ntee_description: "School-Based Health Care", mission: "Providing free dental screenings and preventive care to underserved students in West Alabama", services: [ "Mobile dental unit visits to schools", "Free toothbrush and fluoride kits", "Dental education workshops for parents" ], annual_budget: 125000, students_served: 2400, contact: { website: "https://wadaldentalinitiative.org", email: "info@wadaldentalinitiative.org", phone: "(205) 555-0123" }, volunteer_opportunities: true, accepting_board_members: true }, { name: "Tuscaloosa Family Health Network", ein: "63-7654321", ntee_code: "E40", ntee_description: "Health - General and Rehabilitative", mission: "Connecting low-income families with preventive health services and wellness programs", services: [ "Health screenings at community centers", "Nutrition education programs", "Mental health counseling referrals" ], annual_budget: 280000, families_served: 850, contact: { website: "https://tuscfamilyhealth.org", email: "contact@tuscfamilyhealth.org", phone: "(205) 555-0456" }, volunteer_opportunities: true, accepting_board_members: false }, { name: "After School Champions", ein: "63-9876543", ntee_code: "O50", ntee_description: "Youth Development Programs", mission: "Providing safe, enriching after-school programs that support academic success and healthy development", services: [ "Homework help and tutoring", "Healthy snacks and meals", "Physical activity and sports", "Health and wellness workshops" ], annual_budget: 340000, youth_served: 450, contact: { website: "https://afterschoolchamps.org", email: "info@afterschoolchamps.org", phone: "(205) 555-0789" }, volunteer_opportunities: true, accepting_board_members: true }, { name: "First Baptist Church Tuscaloosa - Health Ministry", ein: "63-2345678", ntee_code: "X20", ntee_description: "Christian", mission: "Faith-based health outreach serving Tuscaloosa families through free dental kits, health screenings, and nutrition education", services: [ "Free dental hygiene kits distribution", "Health screenings after Sunday service", "Nutrition education classes", "Mobile health unit partnership" ], annual_budget: 45000, families_served: 450, contact: { website: "https://fbctuscaloosa.org/health", email: "health@fbctuscaloosa.org", phone: "(205) 555-0200" }, volunteer_opportunities: true, accepting_board_members: false }, { name: "Tuscaloosa County Interfaith Dental Initiative", ein: "63-3456789", ntee_code: "X20", ntee_description: "Christian", mission: "Multi-faith collaboration providing free dental care to low-income students across Tuscaloosa County schools", services: [ "Mobile dental unit serving Title I schools", "Free toothbrush and fluoride programs", "Parent education workshops", "Dental emergency fund for families" ], annual_budget: 180000, students_served: 1600, contact: { website: "https://tuscaloosainterfaithdental.org", email: "contact@tuscaloosainterfaithdental.org", phone: "(205) 555-0300" }, volunteer_opportunities: true, accepting_board_members: true }, { name: "Catholic Social Services - Dental Outreach", ein: "63-4567890", ntee_code: "X20", ntee_description: "Christian", mission: "Diocese of Birmingham outreach providing dental care and health services to underserved communities", services: [ "Quarterly dental clinics at parish hall", "School dental screening partnerships", "Dental supply distribution", "Financial assistance for dental emergencies" ], annual_budget: 95000, families_served: 320, contact: { website: "https://cssalabama.org/dental", email: "dental@cssalabama.org", phone: "(205) 555-0400" }, volunteer_opportunities: true, accepting_board_members: false } ]; // Filter decisions based on search, topics, patterns, resources, and date range const filteredDecisions = exampleDecisions.filter(decision => { const matchesSearch = searchQuery === '' || decision.decision_summary.toLowerCase().includes(searchQuery.toLowerCase()) || decision.primary_rationale.toLowerCase().includes(searchQuery.toLowerCase()); // Map policy_domain to topic IDs const topicMap = { 'health': 'public-health', 'facilities': 'infrastructure', 'budget': 'education' // Example mapping }; const decisionTopic = topicMap[decision.policy_domain] || decision.policy_domain; const matchesTopic = selectedTopics.length === 0 || selectedTopics.includes(decisionTopic); // Date filtering const decisionDate = new Date(decision.meeting_date); const matchesStartDate = !startDate || decisionDate >= new Date(startDate); const matchesEndDate = !endDate || decisionDate <= new Date(endDate); // TODO: Add pattern matching based on decision.patterns field // TODO: Add resource matching based on decision.available_resources field return matchesSearch && matchesTopic && matchesStartDate && matchesEndDate; }); // Filter nonprofits and churches based on search and topic const filteredNonprofits = exampleNonprofits.filter(org => { const matchesSearch = searchQuery === '' || org.name.toLowerCase().includes(searchQuery.toLowerCase()) || org.mission.toLowerCase().includes(searchQuery.toLowerCase()) || org.services.some(service => service.toLowerCase().includes(searchQuery.toLowerCase())) || org.ntee_description.toLowerCase().includes(searchQuery.toLowerCase()); // Map NTEE codes to topic IDs const nteeTopicMap = { 'E': 'public-health', // Health 'F': 'public-health', // Mental Health 'K': 'public-health', // Food/Nutrition 'O': 'education', // Youth Development 'P': 'education', // Human Services 'X': 'public-health' // Religious (often health ministries) }; const orgTopic = nteeTopicMap[org.ntee_code?.[0]] || 'public-health'; const matchesTopic = selectedTopics.length === 0 || selectedTopics.includes(orgTopic); return matchesSearch && matchesTopic; }); return (
{/* Sticky Header */}
{/* Logo and Title Row */}
{/* CommunityOne Logo */} CommunityOne window.scrollTo({ top: 0, behavior: 'smooth' })} style={{ height: 100, cursor: 'pointer', objectFit: 'contain' }} /> {/* User Location Display */}
{jurisdictionType === 'nation' ? 'United States' : jurisdictionType === 'state' ? jurisdictionState : jurisdictionType === 'county' ? `${jurisdictionName} County, ${jurisdictionState}` : jurisdictionType === 'school-district' ? `${jurisdictionName} (${jurisdictionState})` : `${jurisdictionName}, ${jurisdictionState}`}
{jurisdictionType === 'school-district' ? 'School District' : jurisdictionType}

{metadata.title}

{metadata.description}

{/* Global Search Bar */}
setSearchQuery(e.target.value)} style={{ width: '100%', padding: '10px 40px 10px 40px', fontSize: 14, border: '1px solid #ddd', borderRadius: 6, outline: 'none', transition: 'border-color 0.2s' }} onFocus={(e) => e.target.style.borderColor = '#059669'} onBlur={(e) => e.target.style.borderColor = '#ddd'} /> {searchQuery && ( )}
{/* View Mode Toggle */}
{/* Main Content Area */}
{/* Search Bar & Quick Filters (show in browse view) */} {viewMode === 'browse' && (
{/* Quick Filters */}
{/* Date Range Quick Filter */}
Date:
{['all', '7days', '30days', '90days'].map(filter => ( ))}
{/* Topic Quick Filter */}
Topic:
{[ { id: 'all', label: 'All' }, { id: 'public-health', label: 'Health' }, { id: 'education', label: 'Education' }, { id: 'infrastructure', label: 'Infrastructure' }, { id: 'safety', label: 'Safety' } ].map(topic => ( ))}
{/* Clear All Filters */} {(searchQuery || quickDateFilter !== 'all' || quickTopicFilter !== 'all' || selectedPatterns.length > 0 || selectedResources.length > 0) && ( )}
)} {/* Filters (show in browse view for decisions only) */} {viewMode === 'browse' && exploreMode === 'decisions' && ( )} {/* Content */} {viewMode === 'home' ? ( !org.ntee_code.startsWith('X')).length} churchesCount={exampleNonprofits.filter(org => org.ntee_code.startsWith('X')).length} /> ) : viewMode === 'impact' ? (
) : viewMode === 'split-screen' ? (
Split-Screen Analysis

Government Decision ↔ Community Response

) : (

{exploreMode === 'decisions' ? 'Explore Decisions' : exploreMode === 'organizations' ? 'Explore Organizations' : 'Explore Causes'} {sectorView !== 'all' && sectorView !== 'public' && exploreMode !== 'causes' && ( {' '}› {sectorView === 'nonprofits' ? 'Nonprofits' : 'Churches'} )}

{/* Sector Filter Buttons */} {exploreMode === 'decisions' && (
)} {exploreMode === 'organizations' && (
)} {/* Public Sector Decisions */} {exploreMode === 'decisions' && (
{filteredDecisions.length === 0 ? (

No government decisions match your filters

) : ( <>
Showing {filteredDecisions.length} decision{filteredDecisions.length !== 1 ? 's' : ''}
{filteredDecisions.map((decision, i) => ( handleDecisionClick(decision)} /> ))} )}
)} {/* Nonprofits Section */} {exploreMode === 'organizations' && (sectorView === 'all' || sectorView === 'nonprofits') && (
{sectorView === 'all' && (

Nonprofit Organizations

)} {filteredNonprofits.filter(org => !org.ntee_code.startsWith('X')).length === 0 ? (

No nonprofits match your search

) : ( filteredNonprofits .filter(org => !org.ntee_code.startsWith('X')) .map((nonprofit, i) => ( )) )}
)} {/* Churches Section */} {exploreMode === 'organizations' && (sectorView === 'all' || sectorView === 'churches') && (
{sectorView === 'all' && (

Faith-Based Organizations

)} {filteredNonprofits.filter(org => org.ntee_code.startsWith('X')).length === 0 ? (

No churches match your search

) : ( filteredNonprofits .filter(org => org.ntee_code.startsWith('X')) .map((church, i) => ( )) )}
)} {/* Causes Section */} {exploreMode === 'causes' && (

Explore Root Causes

Discover the underlying factors driving policy decisions in your community. Understand the connections between social determinants, community needs, and government actions.

Coming soon: Interactive cause analysis and trend mapping

)}
)}
{/* Location Selection Modal */} {showLocationModal && (

Select Your Jurisdiction

Choose the level and location to see relevant decisions and organizations.

{tempJurisdictionType !== 'nation' && (
setTempJurisdictionName(e.target.value)} placeholder={ tempJurisdictionType === 'state' ? 'e.g., Alabama' : tempJurisdictionType === 'county' ? 'e.g., Tuscaloosa' : tempJurisdictionType === 'school-district' ? 'e.g., Tuscaloosa City Schools' : 'e.g., Tuscaloosa' } style={{ width: '100%', padding: '10px 12px', fontSize: 15, border: '1px solid #ddd', borderRadius: 6, outline: 'none' }} onFocus={(e) => e.target.style.borderColor = '#059669'} onBlur={(e) => e.target.style.borderColor = '#ddd'} />
)} {tempJurisdictionType !== 'nation' && tempJurisdictionType !== 'state' && (
setTempJurisdictionState(e.target.value)} placeholder="e.g., AL" maxLength="2" style={{ width: '100%', padding: '10px 12px', fontSize: 15, border: '1px solid #ddd', borderRadius: 6, outline: 'none', textTransform: 'uppercase' }} onFocus={(e) => e.target.style.borderColor = '#059669'} onBlur={(e) => e.target.style.borderColor = '#ddd'} />
)} {tempJurisdictionType === 'state' && (
setTempJurisdictionState(e.target.value)} placeholder="e.g., AL" maxLength="2" style={{ width: '100%', padding: '10px 12px', fontSize: 15, border: '1px solid #ddd', borderRadius: 6, outline: 'none', textTransform: 'uppercase' }} onFocus={(e) => e.target.style.borderColor = '#059669'} onBlur={(e) => e.target.style.borderColor = '#ddd'} />
)}
)}
); }