feat: enhance district layer and popup with improved data handling and logging

This commit is contained in:
vergiLgood1 2025-05-03 19:54:39 +07:00
parent 0d6f9acf66
commit fdc0403b81
3 changed files with 89 additions and 79 deletions

View File

@ -105,25 +105,6 @@ export default function CrimeMap() {
}) })
}, [filteredByYearAndMonth, selectedCategory]) }, [filteredByYearAndMonth, selectedCategory])
// Extract all incidents from all districts for marker display
// const allIncidents = useMemo(() => {
// if (!filteredCrimes) return []
// return filteredCrimes.flatMap((crime) =>
// crime.crime_incidents.map((incident) => ({
// id: incident.id,
// timestamp: incident.timestamp,
// description: incident.description,
// status: incident.status,
// category: incident.crime_categories.name,
// type: incident.crime_categories.type,
// address: incident.locations.address,
// latitude: incident.locations.latitude,
// longitude: incident.locations.longitude,
// }))
// )
// }, [filteredCrimes])
// Handle incident marker click // Handle incident marker click
const handleIncidentClick = (incident: CrimeIncident) => { const handleIncidentClick = (incident: CrimeIncident) => {
console.log("Incident clicked directly:", incident); console.log("Incident clicked directly:", incident);
@ -184,10 +165,12 @@ export default function CrimeMap() {
// Handle district click // Handle district click
const handleDistrictClick = (feature: DistrictFeature) => { const handleDistrictClick = (feature: DistrictFeature) => {
console.log("District clicked in CrimeMap:", feature.name);
// When a district is clicked, clear any selected incident // When a district is clicked, clear any selected incident
setSelectedIncident(null); setSelectedIncident(null);
// Set the selected district // Set the selected district (for the sidebar or other components)
setSelectedDistrict(feature); setSelectedDistrict(feature);
} }
@ -250,7 +233,16 @@ export default function CrimeMap() {
!sidebarCollapsed && isFullscreen && "ml-[400px]" !sidebarCollapsed && isFullscreen && "ml-[400px]"
)}> )}>
<MapView mapStyle="mapbox://styles/mapbox/dark-v11" className="h-[600px] w-full rounded-md"> <MapView mapStyle="mapbox://styles/mapbox/dark-v11" className="h-[600px] w-full rounded-md">
{/* District Layer with crime data */} {/* District Layer with crime data - don't pass onClick if we want internal popup */}
<DistrictLayer
crimes={filteredCrimes || []}
year={selectedYear.toString()}
month={selectedMonth.toString()}
filterCategory={selectedCategory}
/>
{/* Pass onClick if you want to handle districts externally */}
{/*
<DistrictLayer <DistrictLayer
onClick={handleDistrictClick} onClick={handleDistrictClick}
crimes={filteredCrimes || []} crimes={filteredCrimes || []}
@ -258,6 +250,7 @@ export default function CrimeMap() {
month={selectedMonth.toString()} month={selectedMonth.toString()}
filterCategory={selectedCategory} filterCategory={selectedCategory}
/> />
*/}
{/* Popup for selected incident */} {/* Popup for selected incident */}
{selectedIncident && selectedIncident.latitude && selectedIncident.longitude && ( {selectedIncident && selectedIncident.latitude && selectedIncident.longitude && (

View File

@ -12,7 +12,6 @@ import { ICrimes } from "@/app/_utils/types/crimes"
export interface DistrictFeature { export interface DistrictFeature {
id: string id: string
name: string name: string
properties: Record<string, any>
longitude: number longitude: number
latitude: number latitude: number
number_of_crime: number number_of_crime: number
@ -45,56 +44,14 @@ export interface DistrictFeature {
selectedMonth?: string selectedMonth?: string
} }
// Updated interface to match the structure in crimes.ts
// export interface ICrimeData {
// id: string
// district_id: string
// districts: {
// name: string
// geographics: {
// address: string | null
// land_area: number | null
// year: number | null
// latitude: number
// longitude: number
// }[]
// demographics: {
// number_of_unemployed: number
// population: number
// population_density: number
// year: number
// }[]
// }
// number_of_crime: number
// level: $Enums.crime_rates
// score: number
// month: number
// year: number
// crime_incidents: Array<{
// id: string
// timestamp: Date
// description: string
// status: string
// crime_categories: {
// name: string
// type: string | null
// }
// locations: {
// address: string | null
// latitude: number
// longitude: number
// }
// }>
// }
// District layer props // District layer props
export interface DistrictLayerProps { export interface DistrictLayerProps {
visible?: boolean visible?: boolean
onClick?: (feature: DistrictFeature) => void onClick?: (feature: DistrictFeature) => void
year?: string year: string
month?: string month: string
filterCategory?: string | "all" filterCategory: string | "all"
crimes?: ICrimes[] crimes: ICrimes[]
tilesetId?: string tilesetId?: string
} }
@ -179,8 +136,34 @@ export default function DistrictLayer({
}) })
const firstDistrictCrime = districtCrimes.length > 0 ? districtCrimes[0] : null const firstDistrictCrime = districtCrimes.length > 0 ? districtCrimes[0] : null
const demographics = firstDistrictCrime?.districts.demographics?.[0]
const geographics = firstDistrictCrime?.districts.geographics?.[0] const selectedYearNum = year ? parseInt(year) : new Date().getFullYear();
let demographics = firstDistrictCrime?.districts.demographics?.find(
d => d.year === selectedYearNum
);
if (!demographics && firstDistrictCrime?.districts.demographics?.length) {
demographics = firstDistrictCrime.districts.demographics
.sort((a, b) => b.year - a.year)[0];
console.log(`Tidak ada data demografis untuk tahun ${selectedYearNum}, menggunakan data tahun ${demographics.year}`);
}
let geographics = firstDistrictCrime?.districts.geographics?.find(
g => g.year === selectedYearNum
);
if (!geographics && firstDistrictCrime?.districts.geographics?.length) {
const validGeographics = firstDistrictCrime.districts.geographics
.filter(g => g.year !== null)
.sort((a, b) => (b.year || 0) - (a.year || 0));
geographics = validGeographics.length > 0 ?
validGeographics[0] :
firstDistrictCrime.districts.geographics[0];
console.log(`Tidak ada data geografis untuk tahun ${selectedYearNum}, menggunakan data ${geographics.year ? `tahun ${geographics.year}` : 'tanpa tahun'}`);
}
const clickLng = e.lngLat ? e.lngLat.lng : null const clickLng = e.lngLat ? e.lngLat.lng : null
const clickLat = e.lngLat ? e.lngLat.lat : null const clickLat = e.lngLat ? e.lngLat.lat : null
@ -198,7 +181,7 @@ export default function DistrictLayer({
const district: DistrictFeature = { const district: DistrictFeature = {
id: districtId, id: districtId,
name: feature.properties.nama || feature.properties.kecamatan || "Unknown District", name: feature.properties.nama || feature.properties.kecamatan || "Unknown District",
properties: feature.properties, // properties: feature.properties,
longitude: geographics.longitude || clickLng || 0, longitude: geographics.longitude || clickLng || 0,
latitude: geographics.latitude || clickLat || 0, latitude: geographics.latitude || clickLat || 0,
number_of_crime: crimeData.number_of_crime || 0, number_of_crime: crimeData.number_of_crime || 0,
@ -227,6 +210,7 @@ export default function DistrictLayer({
} }
selectedDistrictRef.current = district; selectedDistrictRef.current = district;
console.log("District clicked, selectedDistrictRef set to:", selectedDistrictRef.current);
if (onClick) { if (onClick) {
onClick(district); onClick(district);
@ -300,6 +284,7 @@ export default function DistrictLayer({
}, [map]); }, [map]);
const handleCloseDistrictPopup = useCallback(() => { const handleCloseDistrictPopup = useCallback(() => {
console.log("Closing district popup");
selectedDistrictRef.current = null; selectedDistrictRef.current = null;
setSelectedDistrict(null); setSelectedDistrict(null);
}, []); }, []);
@ -685,8 +670,30 @@ export default function DistrictLayer({
const districtCrime = crimes.find(crime => crime.district_id === districtId); const districtCrime = crimes.find(crime => crime.district_id === districtId);
if (districtCrime) { if (districtCrime) {
const demographics = districtCrime.districts.demographics?.[0]; const selectedYearNum = year ? parseInt(year) : new Date().getFullYear();
const geographics = districtCrime.districts.geographics?.[0];
let demographics = districtCrime.districts.demographics?.find(
d => d.year === selectedYearNum
);
if (!demographics && districtCrime.districts.demographics?.length) {
demographics = districtCrime.districts.demographics
.sort((a, b) => b.year - a.year)[0];
}
let geographics = districtCrime.districts.geographics?.find(
g => g.year === selectedYearNum
);
if (!geographics && districtCrime.districts.geographics?.length) {
const validGeographics = districtCrime.districts.geographics
.filter(g => g.year !== null)
.sort((a, b) => (b.year || 0) - (a.year || 0));
geographics = validGeographics.length > 0 ?
validGeographics[0] :
districtCrime.districts.geographics[0];
}
if (!demographics || !geographics) { if (!demographics || !geographics) {
console.error("Missing district data:", { demographics, geographics }); console.error("Missing district data:", { demographics, geographics });
@ -749,17 +756,17 @@ export default function DistrictLayer({
return ( return (
<> <>
{selectedDistrictRef.current ? ( {selectedDistrict && (
<DistrictPopup <DistrictPopup
longitude={selectedDistrictRef.current.longitude || 0} longitude={selectedDistrict.longitude || 0}
latitude={selectedDistrictRef.current.latitude || 0} latitude={selectedDistrict.latitude || 0}
onClose={handleCloseDistrictPopup} onClose={handleCloseDistrictPopup}
district={selectedDistrictRef.current} district={selectedDistrict}
year={year} year={year}
month={month} month={month}
filterCategory={filterCategory} filterCategory={filterCategory}
/> />
) : null} )}
</> </>
) )
} }

View File

@ -1,6 +1,6 @@
"use client" "use client"
import { useState, useMemo } from "react" import { useState, useMemo, useEffect } from "react"
import { Popup } from "react-map-gl/mapbox" import { Popup } from "react-map-gl/mapbox"
import { Badge } from "@/app/_components/ui/badge" import { Badge } from "@/app/_components/ui/badge"
import { Card } from "@/app/_components/ui/card" import { Card } from "@/app/_components/ui/card"
@ -46,6 +46,16 @@ export default function DistrictPopup({
}: DistrictPopupProps) { }: DistrictPopupProps) {
const [activeTab, setActiveTab] = useState("overview") const [activeTab, setActiveTab] = useState("overview")
// Add debug log when the component is rendered
useEffect(() => {
console.log("DistrictPopup mounted:", {
district: district.name,
coords: [longitude, latitude],
year,
month
});
}, [district, longitude, latitude, year, month]);
// Extract all crime incidents from the district data and apply filtering if needed // Extract all crime incidents from the district data and apply filtering if needed
const allCrimeIncidents = useMemo(() => { const allCrimeIncidents = useMemo(() => {
// Check if there are crime incidents in the district object // Check if there are crime incidents in the district object