"use client" import { BASE_BEARING, BASE_DURATION, BASE_PITCH, BASE_ZOOM } from "@/app/_utils/const/map" import { createFillColorExpression, getFillOpacity, processDistrictFeature } from "@/app/_utils/map" import type { IDistrictLayerProps } from "@/app/_utils/types/map" import { useEffect } from "react" export default function DistrictFillLineLayer({ visible = true, map, onClick, onDistrictClick, // Add the new prop year, month, filterCategory = "all", crimes = [], tilesetId, focusedDistrictId, setFocusedDistrictId, crimeDataByDistrict, showFill = true, activeControl, }: IDistrictLayerProps & { onDistrictClick?: (district: any) => void }) { // Extend the type inline useEffect(() => { if (!map || !visible) return const handleDistrictClick = (e: any) => { // Only include layers that exist in the map style const possibleLayers = [ "unclustered-point", "clusters", "crime-points", "units-points", "incidents-points", "timeline-markers", "recent-incidents", ] const availableLayers = possibleLayers.filter(layer => map.getLayer(layer)) const incidentFeatures = map.queryRenderedFeatures(e.point, { layers: availableLayers, }) if (incidentFeatures && incidentFeatures.length > 0) { // Click was on a marker or cluster, so don't process it as a district click return } if (!map || !e.features || e.features.length === 0) return const feature = e.features[0] const districtId = feature.properties.kode_kec // If clicking the same district, deselect it if (focusedDistrictId === districtId) { // Add null check for setFocusedDistrictId if (setFocusedDistrictId) { setFocusedDistrictId(null) } // Reset pitch and bearing with animation map.easeTo({ zoom: BASE_ZOOM, pitch: BASE_PITCH, bearing: BASE_BEARING, duration: BASE_DURATION, easing: (t) => t * (2 - t), // easeOutQuad }) // Restore fill color for all districts when unfocusing const fillColorExpression = createFillColorExpression(null, crimeDataByDistrict) map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any) // Show all clusters again when unfocusing if (map.getLayer("clusters")) { map.setLayoutProperty("clusters", "visibility", "visible") } if (map.getLayer("unclustered-point")) { map.setLayoutProperty("unclustered-point", "visibility", "visible") } return } else if (focusedDistrictId) { // If we're already focusing on a district and clicking a different one, // we need to reset the current one and move to the new one if (setFocusedDistrictId) { setFocusedDistrictId(null) } // Wait a moment before selecting the new district to ensure clean transitions // setTimeout(() => { // const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month) // if (!district || !setFocusedDistrictId) return // setFocusedDistrictId(district.id) // // Fly to the new district // map.flyTo({ // center: [district.longitude, district.latitude], // zoom: 12.5, // pitch: 75, // bearing: 0, // duration: 1500, // easing: (t) => t * (2 - t), // easeOutQuad // }) // // Use onDistrictClick if available, otherwise fall back to onClick // if (onDistrictClick) { // onDistrictClick(district) // } else if (onClick) { // onClick(district) // } // }, 100) return } const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month) if (!district) return // Set the fill color expression immediately to show the focus const focusedFillColorExpression = createFillColorExpression(district.id, crimeDataByDistrict) map.setPaintProperty("district-fill", "fill-color", focusedFillColorExpression as any) // Add null check for setFocusedDistrictId if (setFocusedDistrictId) { setFocusedDistrictId(district.id) } // Hide clusters when focusing on a district if (map.getLayer("clusters")) { map.setLayoutProperty("clusters", "visibility", "none") } if (map.getLayer("unclustered-point")) { map.setLayoutProperty("unclustered-point", "visibility", "none") } // Animate to a pitched view focused on the district // map.flyTo({ // center: [district.longitude, district.latitude], // zoom: 12.5, // pitch: 75, // bearing: 0, // duration: 1500, // easing: (t) => t * (2 - t), // easeOutQuad // }) // Use onDistrictClick if available, otherwise fall back to onClick if (onDistrictClick) { onDistrictClick(district) } else if (onClick) { onClick(district) } } const onStyleLoad = () => { if (!map) return try { if (!map.getSource("districts")) { const layers = map.getStyle().layers let firstSymbolId: string | undefined for (const layer of layers) { if (layer.type === "symbol") { firstSymbolId = layer.id break } } map.addSource("districts", { type: "vector", url: `mapbox://${tilesetId}`, }) const fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict) // Determine fill opacity based on active control const fillOpacity = getFillOpacity(activeControl, showFill) if (!map.getLayer("district-fill")) { map.addLayer( { id: "district-fill", type: "fill", source: "districts", "source-layer": "Districts", paint: { "fill-color": fillColorExpression as any, "fill-opacity": fillOpacity, }, }, firstSymbolId, ) } if (!map.getLayer("district-line")) { map.addLayer( { id: "district-line", type: "line", source: "districts", "source-layer": "Districts", paint: { "line-color": "#ffffff", "line-width": 1, "line-opacity": 0.5, }, }, firstSymbolId, ) } map.on("mouseenter", "district-fill", () => { map.getCanvas().style.cursor = "pointer" }) map.on("mouseleave", "district-fill", () => { map.getCanvas().style.cursor = "" }) map.off("click", "district-fill", handleDistrictClick) map.on("click", "district-fill", handleDistrictClick) } else { if (map.getLayer("district-fill")) { const fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict) map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any) // Update fill opacity when active control changes const fillOpacity = getFillOpacity(activeControl, showFill) map.setPaintProperty("district-fill", "fill-opacity", fillOpacity) } } } catch (error) { console.error("Error adding district layers:", error) } } if (map.isStyleLoaded()) { onStyleLoad() } else { map.once("style.load", onStyleLoad) } return () => { if (map) { map.off("click", "district-fill", handleDistrictClick) } } }, [ map, visible, tilesetId, crimes, filterCategory, year, month, focusedDistrictId, crimeDataByDistrict, onClick, onDistrictClick, // Add to dependency array setFocusedDistrictId, showFill, activeControl, ]) // Add an effect to update the fill color and opacity whenever relevant props change useEffect(() => { if (!map || !map.getLayer("district-fill")) return try { const fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict) map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any) // Update fill opacity when active control changes const fillOpacity = getFillOpacity(activeControl, showFill) map.setPaintProperty("district-fill", "fill-opacity", fillOpacity) } catch (error) { console.error("Error updating district fill colors or opacity:", error) } }, [map, focusedDistrictId, crimeDataByDistrict, activeControl, showFill]) return null }