From c282d958a59a4724833de7a5b75b0c0df3ef22b8 Mon Sep 17 00:00:00 2001 From: vergiLgood1 Date: Mon, 5 May 2025 03:42:32 +0700 Subject: [PATCH] feat: add map reset functionality and improve incident popup handling --- .../app/_components/map/crime-map.tsx | 63 +++++- sigap-website/app/_components/map/map.tsx | 31 ++- .../_components/map/pop-up/crime-popup.tsx | 196 +++++++++--------- 3 files changed, 175 insertions(+), 115 deletions(-) diff --git a/sigap-website/app/_components/map/crime-map.tsx b/sigap-website/app/_components/map/crime-map.tsx index dab9bab..b991510 100644 --- a/sigap-website/app/_components/map/crime-map.tsx +++ b/sigap-website/app/_components/map/crime-map.tsx @@ -21,6 +21,7 @@ import { CrimeTimelapse } from "./controls/bottom/crime-timelapse" import { ITooltips } from "./controls/top/tooltips" import CrimeSidebar from "./controls/left/sidebar/map-sidebar" import Tooltips from "./controls/top/tooltips" +import { BASE_BEARING, BASE_PITCH, BASE_ZOOM } from "@/app/_utils/const/map" // Updated CrimeIncident type to match the structure in crime_incidents interface ICrimeIncident { @@ -225,6 +226,57 @@ export default function CrimeMap() { }; }, []); + // Add event listener for map reset + useEffect(() => { + const handleMapReset = (e: CustomEvent) => { + const { duration } = e.detail || { duration: 1500 }; + + // Find the map instance + const mapInstance = mapContainerRef.current?.querySelector('.mapboxgl-map'); + if (!mapInstance) { + console.error("Map instance not found"); + return; + } + + // Create and dispatch the reset event that MapView will listen for + const mapboxEvent = new CustomEvent('mapbox_fly', { + detail: { + duration: duration, + resetCamera: true + }, + bubbles: true + }); + + mapInstance.dispatchEvent(mapboxEvent); + }; + + // Add event listener + document.addEventListener('mapbox_reset', handleMapReset as EventListener); + + return () => { + document.removeEventListener('mapbox_reset', handleMapReset as EventListener); + }; + }, []); + + // Update the popup close handler to reset the map view + const handlePopupClose = () => { + setSelectedIncident(null); + + // Dispatch map reset event to reset zoom, pitch, and bearing + const mapInstance = mapContainerRef.current?.querySelector('.mapboxgl-map'); + + if (mapInstance) { + const resetEvent = new CustomEvent('mapbox_reset', { + detail: { + duration: 1500, + }, + bubbles: true + }); + + mapInstance.dispatchEvent(resetEvent); + } + } + // Handle year-month timeline change const handleTimelineChange = useCallback((year: number, month: number, progress: number) => { setSelectedYear(year) @@ -262,11 +314,6 @@ export default function CrimeMap() { return title } - // Toggle sidebar function - const toggleSidebar = useCallback(() => { - setSidebarCollapsed(!sidebarCollapsed) - }, [sidebarCollapsed]) - // Handle control changes from the top controls component const handleControlChange = (controlId: ITooltips) => { setActiveControl(controlId) @@ -327,7 +374,7 @@ export default function CrimeMap() { setSelectedIncident(null)} + onClose={handlePopupClose} incident={selectedIncident} /> @@ -349,7 +396,7 @@ export default function CrimeMap() { setSelectedCategory={setSelectedCategory} availableYears={availableYears || []} categories={categories} - crimes={filteredCrimes} + crimes={filteredCrimes} /> @@ -362,7 +409,7 @@ export default function CrimeMap() { selectedMonth={selectedMonth} />
- +
diff --git a/sigap-website/app/_components/map/map.tsx b/sigap-website/app/_components/map/map.tsx index 206fcbe..f5dec46 100644 --- a/sigap-website/app/_components/map/map.tsx +++ b/sigap-website/app/_components/map/map.tsx @@ -88,16 +88,29 @@ export default function MapView({ const handleMapFly = (e: CustomEvent) => { if (!e.detail || !mapRef.current) return; - const { center, zoom, bearing, pitch, duration } = e.detail; + const { center, zoom, bearing, pitch, duration, resetCamera } = e.detail; - mapRef.current.flyTo({ - center, - zoom, - bearing, - pitch, - duration, - essential: true - }); + if (resetCamera) { + // Reset to default view + mapRef.current.flyTo({ + center: [BASE_LONGITUDE, BASE_LATITUDE], + zoom: BASE_ZOOM, + bearing: BASE_BEARING, + pitch: BASE_PITCH, + duration, + essential: true + }); + } else { + // Fly to specific location + mapRef.current.flyTo({ + center, + zoom, + bearing, + pitch, + duration, + essential: true + }); + } }; mapElement.addEventListener('mapbox_fly', handleMapFly as EventListener); diff --git a/sigap-website/app/_components/map/pop-up/crime-popup.tsx b/sigap-website/app/_components/map/pop-up/crime-popup.tsx index 87dddc4..042e95a 100644 --- a/sigap-website/app/_components/map/pop-up/crime-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/crime-popup.tsx @@ -27,31 +27,31 @@ interface IncidentPopupProps { export default function IncidentPopup({ longitude, latitude, onClose, incident }: IncidentPopupProps) { const formatDate = (date?: Date) => { - if (!date) return "Unknown date" - return new Date(date).toLocaleDateString() - } + if (!date) return "Unknown date" + return new Date(date).toLocaleDateString() + } const formatTime = (date?: Date) => { - if (!date) return "Unknown time" - return new Date(date).toLocaleTimeString() - } + if (!date) return "Unknown time" + return new Date(date).toLocaleTimeString() + } const getStatusBadge = (status?: string) => { if (!status) return Unknown - const statusLower = status.toLowerCase() - if (statusLower.includes("resolv") || statusLower.includes("closed")) { - return Resolved - } - if (statusLower.includes("progress") || statusLower.includes("invest")) { - return In Progress - } - if (statusLower.includes("open") || statusLower.includes("new")) { - return Open - } + const statusLower = status.toLowerCase() + if (statusLower.includes("resolv") || statusLower.includes("closed")) { + return Resolved + } + if (statusLower.includes("progress") || statusLower.includes("invest")) { + return In Progress + } + if (statusLower.includes("open") || statusLower.includes("new")) { + return Open + } - return {status} - } + return {status} + } // Determine border color based on status const getBorderColor = (status?: string) => { @@ -75,108 +75,108 @@ export default function IncidentPopup({ longitude, latitude, onClose, incident } - + -
- {/* Custom close button */} - + > +
+ {/* Custom close button */} + -
-

- +
+

+ {incident.category || "Unknown Incident"} -

+

{getStatusBadge(incident.status)} -
+
{incident.description && ( -
-

- +

+

+ {incident.description} -

-
- )} +

+
+ )} - + - {/* Improved section headers */} -
+ {/* Improved section headers */} +
{incident.district && ( -
-

District

-

- +

+

District

+

+ {incident.district} -

-
- )} +

+
+ )} {incident.address && ( -
-

Location

-

- +

+

Location

+

+ {incident.address} -

-
- )} +

+
+ )} {incident.timestamp && ( - <> -
-

Date

-

- + <> +

+

Date

+

+ {formatDate(incident.timestamp)} -

-
-
-

Time

-

- +

+
+
+

Time

+

+ {formatTime(incident.timestamp)} -

-
- - )} +

+
+ + )} {incident.type_category && ( -
-

Type

-

- +

+

Type

+

+ {incident.type_category} -

-
- )} -
+

+
+ )} +
-
-

- - Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} -

+
+

+ + Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} +

ID: {incident.id}

-
-
- - - ) +
+ +
+
+ ) }