311 lines
14 KiB
TypeScript
311 lines
14 KiB
TypeScript
"use client"
|
|
import { Button } from "@/app/_components/ui/button"
|
|
import { ChevronLeft, Filter, Map, BarChart3, Info } from "lucide-react"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/_components/ui/tabs"
|
|
import { ScrollArea } from "@/app/_components/ui/scroll-area"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/app/_components/ui/card"
|
|
import { Separator } from "@/app/_components/ui/separator"
|
|
|
|
interface MapSidebarProps {
|
|
isOpen: boolean
|
|
onToggle: () => void
|
|
crimes?: Array<{
|
|
id: string
|
|
district_name: string
|
|
distrcit_id?: string
|
|
number_of_crime?: number
|
|
level?: "low" | "medium" | "high" | "critical"
|
|
incidents: any[]
|
|
}>
|
|
selectedYear?: number | string
|
|
selectedMonth?: number | string
|
|
}
|
|
|
|
export default function MapSidebar({ isOpen, onToggle, crimes = [], selectedYear, selectedMonth }: MapSidebarProps) {
|
|
// Calculate some statistics for the sidebar
|
|
const totalIncidents = crimes.reduce((total, district) => total + (district.number_of_crime || 0), 0)
|
|
const highRiskDistricts = crimes.filter(
|
|
(district) => district.level === "high" || district.level === "critical",
|
|
).length
|
|
const districtCount = crimes.length
|
|
|
|
return (
|
|
<div
|
|
className={`absolute top-0 left-0 h-full bg-white dark:bg-gray-900 shadow-lg z-20 transition-all duration-300 ease-in-out ${
|
|
isOpen ? "w-80" : "w-0"
|
|
} overflow-hidden`}
|
|
>
|
|
<div className="flex flex-col h-full">
|
|
<div className="flex items-center justify-between p-4 border-b">
|
|
<h2 className="font-semibold text-lg">Crime Map Explorer</h2>
|
|
<Button variant="ghost" size="icon" onClick={onToggle}>
|
|
<ChevronLeft className="h-5 w-5" />
|
|
<span className="sr-only">Close sidebar</span>
|
|
</Button>
|
|
</div>
|
|
|
|
<Tabs defaultValue="overview" className="flex-1 flex flex-col">
|
|
<TabsList className="grid grid-cols-4 mx-2 mt-2">
|
|
<TabsTrigger value="overview">
|
|
<Map className="h-4 w-4 mr-1" />
|
|
<span className="sr-only sm:not-sr-only sm:inline-block">Overview</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="filters">
|
|
<Filter className="h-4 w-4 mr-1" />
|
|
<span className="sr-only sm:not-sr-only sm:inline-block">Filters</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="stats">
|
|
<BarChart3 className="h-4 w-4 mr-1" />
|
|
<span className="sr-only sm:not-sr-only sm:inline-block">Stats</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="info">
|
|
<Info className="h-4 w-4 mr-1" />
|
|
<span className="sr-only sm:not-sr-only sm:inline-block">Info</span>
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<ScrollArea className="flex-1 p-4">
|
|
<TabsContent value="overview" className="mt-0 space-y-4">
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle>Crime Summary</CardTitle>
|
|
<CardDescription>
|
|
{selectedYear}
|
|
{selectedMonth !== "all" ? ` - Month ${selectedMonth}` : ""}
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="flex flex-col">
|
|
<span className="text-sm text-muted-foreground">Total Incidents</span>
|
|
<span className="text-2xl font-bold">{totalIncidents}</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm text-muted-foreground">High Risk Areas</span>
|
|
<span className="text-2xl font-bold">{highRiskDistricts}</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm text-muted-foreground">Districts</span>
|
|
<span className="text-2xl font-bold">{districtCount}</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm text-muted-foreground">Data Points</span>
|
|
<span className="text-2xl font-bold">
|
|
{crimes.reduce((total, district) => total + district.incidents.length, 0)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle>District Overview</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
<div className="max-h-64 overflow-y-auto">
|
|
<table className="w-full">
|
|
<thead className="sticky top-0 bg-white dark:bg-gray-900">
|
|
<tr className="border-b">
|
|
<th className="text-left p-2 text-sm">District</th>
|
|
<th className="text-right p-2 text-sm">Incidents</th>
|
|
<th className="text-right p-2 text-sm">Level</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{crimes
|
|
.sort((a, b) => (b.number_of_crime || 0) - (a.number_of_crime || 0))
|
|
.map((district) => (
|
|
<tr key={district.id} className="border-b hover:bg-muted/50">
|
|
<td className="p-2 text-sm">{district.district_name}</td>
|
|
<td className="text-right p-2 text-sm">{district.number_of_crime || 0}</td>
|
|
<td className="text-right p-2 text-sm">
|
|
<span
|
|
className={`inline-block px-2 py-0.5 rounded-full text-xs ${
|
|
district.level === "low"
|
|
? "bg-green-100 text-green-800"
|
|
: district.level === "medium"
|
|
? "bg-yellow-100 text-yellow-800"
|
|
: district.level === "high"
|
|
? "bg-orange-100 text-orange-800"
|
|
: district.level === "critical"
|
|
? "bg-red-100 text-red-800"
|
|
: "bg-gray-100 text-gray-800"
|
|
}`}
|
|
>
|
|
{district.level || "N/A"}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="filters" className="mt-0 space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Filter Options</CardTitle>
|
|
<CardDescription>Customize what you see on the map</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium">Crime Types</h3>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Theft
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Violence
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Vandalism
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Traffic
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium">Severity Levels</h3>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Low
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Medium
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
High
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Critical
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium">Display Options</h3>
|
|
<div className="grid grid-cols-1 gap-2">
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Show District Labels
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Show Incident Markers
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="justify-start">
|
|
<input type="checkbox" className="mr-2" />
|
|
Show Heatmap
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="stats" className="mt-0 space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Crime Statistics</CardTitle>
|
|
<CardDescription>Analysis of crime data</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="text-sm font-medium mb-2">Crime by Type</h3>
|
|
<div className="h-40 bg-muted rounded-md flex items-center justify-center">
|
|
<span className="text-sm text-muted-foreground">Chart Placeholder</span>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div>
|
|
<h3 className="text-sm font-medium mb-2">Crime by Time of Day</h3>
|
|
<div className="h-40 bg-muted rounded-md flex items-center justify-center">
|
|
<span className="text-sm text-muted-foreground">Chart Placeholder</span>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div>
|
|
<h3 className="text-sm font-medium mb-2">Monthly Trend</h3>
|
|
<div className="h-40 bg-muted rounded-md flex items-center justify-center">
|
|
<span className="text-sm text-muted-foreground">Chart Placeholder</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="info" className="mt-0 space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>About This Map</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
This interactive crime map visualizes crime data across different districts. Use the controls to
|
|
explore different aspects of the data.
|
|
</p>
|
|
|
|
<h3 className="text-sm font-medium mb-2">Legend</h3>
|
|
<div className="space-y-2 mb-4">
|
|
<div className="flex items-center">
|
|
<div className="w-4 h-4 bg-green-500 rounded-sm mr-2"></div>
|
|
<span className="text-sm">Low Crime Rate</span>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<div className="w-4 h-4 bg-yellow-500 rounded-sm mr-2"></div>
|
|
<span className="text-sm">Medium Crime Rate</span>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<div className="w-4 h-4 bg-orange-500 rounded-sm mr-2"></div>
|
|
<span className="text-sm">High Crime Rate</span>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<div className="w-4 h-4 bg-red-500 rounded-sm mr-2"></div>
|
|
<span className="text-sm">Critical Crime Rate</span>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 className="text-sm font-medium mb-2">Data Sources</h3>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
Crime data is collected from official police reports and updated monthly. District boundaries are
|
|
based on administrative regions.
|
|
</p>
|
|
|
|
<h3 className="text-sm font-medium mb-2">Help & Support</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
For questions or support regarding this map, please contact the system administrator.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
</ScrollArea>
|
|
</Tabs>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|