import { useState } from 'react'; import axios from 'axios'; import './App.css'; import 'react-leaflet-cluster/dist/assets/MarkerCluster.css'; import 'react-leaflet-cluster/dist/assets/MarkerCluster.Default.css'; import FileUpload from './components/FileUpload'; import FilterPanel from './components/FilterPanel'; import MapDisplay from './components/MapDisplay'; import ErrorBoundary from './components/ErrorBoundary'; // Define types for our state export interface FilterOptions { [key: string]: string[]; } export interface HeatmapPoint { plz: string; lat: number; lon: number; count: number; attributes_summary?: Record; } export type MapMode = 'points' | 'heatmap'; function App() { const [filters, setFilters] = useState({}); const [heatmapData, setHeatmapData] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [radiusMultiplier, setRadiusMultiplier] = useState(1); const [viewMode, setViewMode] = useState('points'); const handleUploadSuccess = (newFilters: FilterOptions) => { setFilters(newFilters); setHeatmapData([]); // Clear previous heatmap data setError(null); // Automatically fetch data with no filters on successful upload handleFilterChange({}); }; const handleFilterChange = async (selectedFilters: FilterOptions) => { setIsLoading(true); setError(null); try { const response = await axios.post('/api/heatmap', { filters: selectedFilters, }); setHeatmapData(response.data); } catch (error: any) { if (axios.isAxiosError(error) && error.response) { setError(`Failed to fetch heatmap data: ${error.response.data.detail || error.message}`); } else { setError(`Failed to fetch heatmap data: ${error.message}`); } setHeatmapData([]); // Clear data on error } finally { setIsLoading(false); } }; return (

German PLZ Heatmap Tool

Map Settings

setRadiusMultiplier(parseFloat(e.target.value))} style={{ width: '100%' }} disabled={viewMode === 'heatmap'} />
{isLoading &&

Loading map data...

} {error &&

{error}

}
); } export default App;