MIF_E31221222/sigap-website/app/_components/map/pop-up.tsx

185 lines
6.0 KiB
TypeScript

import { Popup } from 'react-map-gl/mapbox';
import { useState, useEffect } from 'react';
import { X } from 'lucide-react';
import { DistrictFeature } from './layers/district-layer';
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@/app/_components/ui/skeleton";
import { getCrimeRateInfo } from "@/app/_utils/common";
import { CrimeIncident } from './markers/crime-marker';
interface MapPopupProps {
longitude: number
latitude: number
onClose: () => void
children: React.ReactNode
title?: string
}
export function MapPopup({
longitude,
latitude,
onClose,
children,
title
}: MapPopupProps) {
return (
<Popup
longitude={longitude}
latitude={latitude}
closeOnClick={false}
onClose={onClose}
anchor="bottom"
offset={20}
className="z-10"
>
<div className="p-3 min-w-[200px] max-w-[300px]">
{title && <h3 className="text-sm font-medium border-b pb-1 mb-2 pr-4">{title}</h3>}
<button
className="absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100"
onClick={onClose}
>
<X className="h-3 w-3" />
</button>
{children}
</div>
</Popup>
)
}
// Function to fetch district details - this would typically be implemented
// to fetch more detailed information about a district
const getDistrictDetails = async (districtId: string, year?: string, month?: string) => {
// This would be an API call to get district details
// For now, we'll return mock data
return {
category_breakdown: [
{ category: "Theft", count: 12 },
{ category: "Assault", count: 5 },
{ category: "Vandalism", count: 8 }
]
};
};
export function DistrictPopup({
longitude,
latitude,
onClose,
district,
year,
month
}: {
longitude: number
latitude: number
onClose: () => void
district: DistrictFeature
year?: string
month?: string
}) {
const { data: districtDetails, isLoading } = useQuery({
queryKey: ['district-details', district.id, year, month],
queryFn: () => getDistrictDetails(district.id, year, month),
enabled: !!district.id
});
const rateInfo = getCrimeRateInfo(district.level);
return (
<MapPopup
longitude={longitude}
latitude={latitude}
onClose={onClose}
title="District Information"
>
<div className="space-y-3">
<h3 className="font-medium text-base">{district.name}</h3>
{district.number_of_crime !== undefined && (
<div className="text-sm text-gray-600">
<span className="font-medium">Total Incidents:</span> {district.number_of_crime}
</div>
)}
{district.level && (
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Crime Rate:</span>
<span className={`inline-flex items-center rounded-full ${rateInfo.color} px-2.5 py-0.5 text-xs font-medium`}>
{rateInfo.text}
</span>
</div>
)}
{year && (
<div className="text-xs text-gray-500">
<span className="font-medium">Year:</span> {year}
{month && <>, Month: {month}</>}
</div>
)}
{isLoading ? (
<div className="h-20 flex items-center justify-center">
<Skeleton className="h-16 w-full" />
</div>
) : districtDetails?.category_breakdown && districtDetails.category_breakdown.length > 0 ? (
<div className="mt-2">
<h4 className="text-sm font-medium mb-1">Crime Categories:</h4>
<div className="space-y-1">
{districtDetails.category_breakdown.map((cat, idx) => (
<div key={idx} className="flex justify-between text-xs">
<span>{cat.category}</span>
<span className="font-medium">{cat.count}</span>
</div>
))}
</div>
</div>
) : null}
<div className="text-xs text-gray-500">
<span className="font-medium">District ID:</span> {district.id}
</div>
</div>
</MapPopup>
)
}
export function CrimePopup({
longitude,
latitude,
onClose,
crime
}: {
longitude: number
latitude: number
onClose: () => void
crime: CrimeIncident
}) {
return (
<MapPopup
longitude={longitude}
latitude={latitude}
onClose={onClose}
title="Crime Incident"
>
<div className="space-y-2">
<div className="text-sm">
<span className="font-medium">Type:</span> {crime.category}
</div>
{crime.timestamp && (
<div className="text-sm">
<span className="font-medium">Date:</span> {new Date(crime.timestamp).toLocaleDateString()}
</div>
)}
{crime.description && (
<div className="text-sm">
<span className="font-medium">Description:</span>
<p className="text-xs mt-1">{crime.description}</p>
</div>
)}
<div className="text-xs text-gray-500 mt-2">
<span className="font-medium">ID:</span> {crime.id}
</div>
</div>
</MapPopup>
)
}