MIF_E31221222/sigap-website/app/_components/map/crime-map.tsx

184 lines
8.0 KiB
TypeScript

"use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/app/_components/ui/card"
import { Skeleton } from "@/app/_components/ui/skeleton"
import DistrictLayer, { type DistrictFeature } from "./layers/district-layer"
import MapView from "./map"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select"
import { Button } from "@/app/_components/ui/button"
import { AlertCircle, FilterX } from "lucide-react"
import { getMonthName } from "@/app/_utils/common"
import { useCrimeMapHandler } from "@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_handlers/crime-map-handlers"
import { useState } from "react"
import { CrimePopup } from "./pop-up"
import CrimeMarker, { type CrimeIncident } from "./markers/crime-marker"
import { MapLegend } from "./controls/map-legend"
const months = [
{ value: "1", label: "January" },
{ value: "2", label: "February" },
{ value: "3", label: "March" },
{ value: "4", label: "April" },
{ value: "5", label: "May" },
{ value: "6", label: "June" },
{ value: "7", label: "July" },
{ value: "8", label: "August" },
{ value: "9", label: "September" },
{ value: "10", label: "October" },
{ value: "11", label: "November" },
{ value: "12", label: "December" },
]
export default function CrimeMap() {
// Set default year to 2024 instead of "all"
const [selectedYear, setSelectedYear] = useState<number>(2024)
const [selectedMonth, setSelectedMonth] = useState<number | "all">("all")
const [selectedDistrict, setSelectedDistrict] = useState<DistrictFeature | null>(null)
const [selectedIncident, setSelectedIncident] = useState<CrimeIncident | null>(null)
const { availableYears, yearsLoading, yearsError, crimes, crimesLoading, crimesError, refetchCrimes } =
useCrimeMapHandler(selectedYear, selectedMonth)
// Extract all incidents from all districts for marker display
const allIncidents =
crimes?.flatMap((district) =>
district.incidents.map((incident) => ({
id: incident.id,
timestamp: incident.timestamp,
description: incident.description,
status: incident.status,
category: incident.category,
type: incident.type,
address: incident.address,
latitude: incident.latitude,
longitude: incident.longitude,
})),
) || []
// Handle district click
const handleDistrictClick = (feature: DistrictFeature) => {
setSelectedDistrict(feature)
}
// Handle incident marker click
const handleIncidentClick = (incident: CrimeIncident) => {
setSelectedIncident(incident)
}
// Apply filters
const applyFilters = () => {
refetchCrimes()
}
// Reset filters
const resetFilters = () => {
setSelectedYear(2024)
setSelectedMonth("all")
refetchCrimes()
}
// Determine the title based on filters
const getMapTitle = () => {
let title = `${selectedYear}`
if (selectedMonth !== "all") {
title += ` - ${getMonthName(Number(selectedMonth))}`
}
return title
}
return (
<Card className="w-full">
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>Crime Map {getMapTitle()}</CardTitle>
<div className="flex items-center gap-2">
<Select value={selectedYear.toString()} onValueChange={(value) => setSelectedYear(Number(value))}>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="Year" />
</SelectTrigger>
<SelectContent>
{/* Removed "All Years" option */}
{!yearsLoading &&
availableYears
?.filter((year) => year !== null)
.map((year) => (
<SelectItem key={year} value={year.toString()}>
{year}
</SelectItem>
))}
</SelectContent>
</Select>
<Select
value={selectedMonth.toString()}
onValueChange={(value) => setSelectedMonth(value === "all" ? "all" : Number(value))}
>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="Month" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Months</SelectItem>
{months.map((month) => (
<SelectItem key={month.value} value={month.value}>
{month.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Button variant="outline" onClick={applyFilters}>
Apply
</Button>
<Button variant="ghost" onClick={resetFilters} disabled={selectedYear === 2024 && selectedMonth === "all"}>
<FilterX className="h-4 w-4 mr-2" />
Reset
</Button>
</div>
</CardHeader>
<CardContent className="p-0">
{crimesLoading ? (
<div className="flex items-center justify-center h-96">
<Skeleton className="h-full w-full rounded-md" />
</div>
) : crimesError ? (
<div className="flex flex-col items-center justify-center h-96 gap-4">
<AlertCircle className="h-10 w-10 text-destructive" />
<p className="text-center">Failed to load crime data. Please try again later.</p>
<Button onClick={() => refetchCrimes()}>Retry</Button>
</div>
) : (
<div className="relative h-96">
<MapView mapStyle="mapbox://styles/mapbox/dark-v11" className="h-96 w-full rounded-md">
{/* Display the legend */}
{/* <MapLegend /> */}
{/* District Layer with crime data */}
<DistrictLayer
onClick={handleDistrictClick}
crimes={crimes || []}
year={selectedYear.toString()}
month={selectedMonth.toString()}
/>
{/* Display all crime incident markers */}
{allIncidents?.map((incident) => (
<CrimeMarker key={incident.id} incident={incident} onClick={handleIncidentClick} />
))}
{/* Popup for selected incident */}
{selectedIncident && (
<CrimePopup
longitude={selectedIncident.longitude}
latitude={selectedIncident.latitude}
onClose={() => setSelectedIncident(null)}
crime={selectedIncident}
/>
)}
</MapView>
</div>
)}
</CardContent>
</Card>
)
}