feat: enhance district layer and popup with improved data handling and logging
This commit is contained in:
parent
0d6f9acf66
commit
fdc0403b81
|
@ -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 && (
|
||||||
|
|
|
@ -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}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue