From 6f89892d8c29a39f1181c128c7b4e364cdd73019 Mon Sep 17 00:00:00 2001 From: vergiLgood1 Date: Mon, 28 Apr 2025 00:14:16 +0700 Subject: [PATCH] feat: add map controls and sidebar components for crime data visualization - Implemented MapControls component for selecting various crime-related metrics. - Created MapFilterControl for filtering data by year and month. - Developed MapSidebar to display crime statistics and district information. - Added SidebarToggle for opening and closing the sidebar. - Introduced SeverityIndicator to visually represent crime severity levels. - Created TimeControls for selecting time frames for data analysis. - Added useFullscreen hook for managing fullscreen functionality. --- .../crime-management/crime-overview/action.ts | 1 - .../_components/map/controls/map-control.tsx | 65 ++ .../map/controls/map-filter-control.tsx | 110 +++ .../_components/map/controls/map-legend.tsx | 2 +- .../_components/map/controls/map-sidebar.tsx | 310 ++++++++ .../_components/map/controls/map-toggle.tsx | 25 + .../map/controls/severity-indicator.tsx | 15 + .../_components/map/controls/time-control.tsx | 31 + .../app/_components/map/crime-map.tsx | 46 +- .../_components/map/layers/district-layer.tsx | 675 +++++++++++++----- sigap-website/app/_components/map/map.tsx | 195 +++-- .../_components/map/markers/crime-marker.tsx | 1 - sigap-website/app/_hooks/use-fullscreen.ts | 89 +++ .../prisma/data/jsons/sample-data.json | 299 ++++---- 14 files changed, 1481 insertions(+), 383 deletions(-) create mode 100644 sigap-website/app/_components/map/controls/map-control.tsx create mode 100644 sigap-website/app/_components/map/controls/map-filter-control.tsx create mode 100644 sigap-website/app/_components/map/controls/map-sidebar.tsx create mode 100644 sigap-website/app/_components/map/controls/map-toggle.tsx create mode 100644 sigap-website/app/_components/map/controls/severity-indicator.tsx create mode 100644 sigap-website/app/_components/map/controls/time-control.tsx create mode 100644 sigap-website/app/_hooks/use-fullscreen.ts diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action.ts b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action.ts index 6e6ad40..4e7049e 100644 --- a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action.ts +++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action.ts @@ -219,7 +219,6 @@ export async function getCrimeByYearAndMonth( }, }, }, - take: 10, }); return crimes.map((crime) => { diff --git a/sigap-website/app/_components/map/controls/map-control.tsx b/sigap-website/app/_components/map/controls/map-control.tsx new file mode 100644 index 0000000..d27b377 --- /dev/null +++ b/sigap-website/app/_components/map/controls/map-control.tsx @@ -0,0 +1,65 @@ +"use client" +import { Button } from "@/app/_components/ui/button" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/app/_components/ui/tooltip" +import { + Thermometer, + Droplets, + Wind, + Cloud, + Eye, + Clock, + AlertTriangle, + MapIcon, + BarChart3, + Users, + Siren, +} from "lucide-react" + +interface MapControlsProps { + onControlChange: (control: string) => void + activeControl: string +} + +export default function MapControls({ onControlChange, activeControl }: MapControlsProps) { + const controls = [ + { id: "crime-rate", icon: , label: "Crime Rate" }, + { id: "theft", icon: , label: "Theft" }, + { id: "violence", icon: , label: "Violence" }, + { id: "vandalism", icon: , label: "Vandalism" }, + { id: "traffic", icon: , label: "Traffic" }, + { id: "time", icon: , label: "Time Analysis" }, + { id: "alerts", icon: , label: "Alerts" }, + { id: "districts", icon: , label: "Districts" }, + { id: "statistics", icon: , label: "Statistics" }, + { id: "demographics", icon: , label: "Demographics" }, + { id: "emergency", icon: , label: "Emergency" }, + ] + + return ( +
+ + {controls.map((control) => ( + + + + + +

{control.label}

+
+
+ ))} +
+
+ ) +} diff --git a/sigap-website/app/_components/map/controls/map-filter-control.tsx b/sigap-website/app/_components/map/controls/map-filter-control.tsx new file mode 100644 index 0000000..91d923e --- /dev/null +++ b/sigap-website/app/_components/map/controls/map-filter-control.tsx @@ -0,0 +1,110 @@ +"use client" + +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select" +import { Button } from "@/app/_components/ui/button" +import { FilterX } from "lucide-react" +import { getMonthName } from "@/app/_utils/common" +import { useCallback } from "react" + +interface MapFilterControlProps { + selectedYear: number + selectedMonth: number | "all" + availableYears: (number | null)[] + yearsLoading: boolean + onYearChange: (year: number) => void + onMonthChange: (month: number | "all") => void + onApplyFilters: () => void + onResetFilters: () => void +} + +const months = [ + { value: "1", label: "January" }, + { value: "2", label: "February" }, + { value: "3", label: "March" }, + { value: "4", label: "April" }, + { value: "5", label: "May" }, + { value: "6", label: "June" }, + { value: "7", label: "July" }, + { value: "8", label: "August" }, + { value: "9", label: "September" }, + { value: "10", label: "October" }, + { value: "11", label: "November" }, + { value: "12", label: "December" }, +] + +export default function MapFilterControl({ + selectedYear, + selectedMonth, + availableYears, + yearsLoading, + onYearChange, + onMonthChange, + onApplyFilters, + onResetFilters +}: MapFilterControlProps) { + const handleYearChange = useCallback((value: string) => { + onYearChange(Number(value)) + }, [onYearChange]) + + const handleMonthChange = useCallback((value: string) => { + onMonthChange(value === "all" ? "all" : Number(value)) + }, [onMonthChange]) + + const isDefaultFilter = selectedYear === 2024 && selectedMonth === "all" + + return ( +
+
Map Filters
+ +
+ + + + +
+ + +
+
+
+ ) +} diff --git a/sigap-website/app/_components/map/controls/map-legend.tsx b/sigap-website/app/_components/map/controls/map-legend.tsx index 30d54cd..291b9d4 100644 --- a/sigap-website/app/_components/map/controls/map-legend.tsx +++ b/sigap-website/app/_components/map/controls/map-legend.tsx @@ -3,7 +3,7 @@ import { CRIME_RATE_COLORS } from "@/app/_utils/const/map" export function MapLegend() { return ( -
+
Crime Rates
diff --git a/sigap-website/app/_components/map/controls/map-sidebar.tsx b/sigap-website/app/_components/map/controls/map-sidebar.tsx new file mode 100644 index 0000000..14c95e1 --- /dev/null +++ b/sigap-website/app/_components/map/controls/map-sidebar.tsx @@ -0,0 +1,310 @@ +"use client" +import { Button } from "@/app/_components/ui/button" +import { ChevronLeft, Filter, Map, BarChart3, Info } from "lucide-react" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/_components/ui/tabs" +import { ScrollArea } from "@/app/_components/ui/scroll-area" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/app/_components/ui/card" +import { Separator } from "@/app/_components/ui/separator" + +interface MapSidebarProps { + isOpen: boolean + onToggle: () => void + crimes?: Array<{ + id: string + district_name: string + distrcit_id?: string + number_of_crime?: number + level?: "low" | "medium" | "high" | "critical" + incidents: any[] + }> + selectedYear?: number | string + selectedMonth?: number | string +} + +export default function MapSidebar({ isOpen, onToggle, crimes = [], selectedYear, selectedMonth }: MapSidebarProps) { + // Calculate some statistics for the sidebar + const totalIncidents = crimes.reduce((total, district) => total + (district.number_of_crime || 0), 0) + const highRiskDistricts = crimes.filter( + (district) => district.level === "high" || district.level === "critical", + ).length + const districtCount = crimes.length + + return ( +
+
+
+

Crime Map Explorer

+ +
+ + + + + + Overview + + + + Filters + + + + Stats + + + + Info + + + + + + + + Crime Summary + + {selectedYear} + {selectedMonth !== "all" ? ` - Month ${selectedMonth}` : ""} + + + +
+
+ Total Incidents + {totalIncidents} +
+
+ High Risk Areas + {highRiskDistricts} +
+
+ Districts + {districtCount} +
+
+ Data Points + + {crimes.reduce((total, district) => total + district.incidents.length, 0)} + +
+
+
+
+ + + + District Overview + + +
+ + + + + + + + + + {crimes + .sort((a, b) => (b.number_of_crime || 0) - (a.number_of_crime || 0)) + .map((district) => ( + + + + + + ))} + +
DistrictIncidentsLevel
{district.district_name}{district.number_of_crime || 0} + + {district.level || "N/A"} + +
+
+
+
+
+ + + + + Filter Options + Customize what you see on the map + + +
+

Crime Types

+
+ + + + +
+
+ + + +
+

Severity Levels

+
+ + + + +
+
+ + + +
+

Display Options

+
+ + + +
+
+
+
+
+ + + + + Crime Statistics + Analysis of crime data + + +
+
+

Crime by Type

+
+ Chart Placeholder +
+
+ + + +
+

Crime by Time of Day

+
+ Chart Placeholder +
+
+ + + +
+

Monthly Trend

+
+ Chart Placeholder +
+
+
+
+
+
+ + + + + About This Map + + +

+ This interactive crime map visualizes crime data across different districts. Use the controls to + explore different aspects of the data. +

+ +

Legend

+
+
+
+ Low Crime Rate +
+
+
+ Medium Crime Rate +
+
+
+ High Crime Rate +
+
+
+ Critical Crime Rate +
+
+ +

Data Sources

+

+ Crime data is collected from official police reports and updated monthly. District boundaries are + based on administrative regions. +

+ +

Help & Support

+

+ For questions or support regarding this map, please contact the system administrator. +

+
+
+
+
+
+
+
+ ) +} diff --git a/sigap-website/app/_components/map/controls/map-toggle.tsx b/sigap-website/app/_components/map/controls/map-toggle.tsx new file mode 100644 index 0000000..145e63a --- /dev/null +++ b/sigap-website/app/_components/map/controls/map-toggle.tsx @@ -0,0 +1,25 @@ +"use client" + +import { Button } from "@/app/_components/ui/button" +import { Menu } from "lucide-react" + +interface SidebarToggleProps { + isOpen: boolean + onToggle: () => void +} + +export default function SidebarToggle({ isOpen, onToggle }: SidebarToggleProps) { + if (isOpen) return null + + return ( + + ) +} diff --git a/sigap-website/app/_components/map/controls/severity-indicator.tsx b/sigap-website/app/_components/map/controls/severity-indicator.tsx new file mode 100644 index 0000000..e31253e --- /dev/null +++ b/sigap-website/app/_components/map/controls/severity-indicator.tsx @@ -0,0 +1,15 @@ +"use client" + +export default function SeverityIndicator() { + return ( +
+
+
+ Low + Medium + High +
+
+
+ ) +} diff --git a/sigap-website/app/_components/map/controls/time-control.tsx b/sigap-website/app/_components/map/controls/time-control.tsx new file mode 100644 index 0000000..f36052a --- /dev/null +++ b/sigap-website/app/_components/map/controls/time-control.tsx @@ -0,0 +1,31 @@ +"use client" +import { Checkbox } from "@/app/_components/ui/checkbox" +import { Label } from "@/app/_components/ui/label" + +interface TimeControlsProps { + onTimeChange: (time: string) => void + activeTime: string +} + +export default function TimeControls({ onTimeChange, activeTime }: TimeControlsProps) { + const times = [ + { id: "today", label: "Hari ini" }, + { id: "yesterday", label: "Kemarin" }, + { id: "week", label: "Minggu" }, + { id: "month", label: "Bulan" }, + ] + + return ( +
+
Waktu
+ {times.map((time) => ( +
+ onTimeChange(time.id)} /> + +
+ ))} +
+ ) +} diff --git a/sigap-website/app/_components/map/crime-map.tsx b/sigap-website/app/_components/map/crime-map.tsx index c2a66ed..00ab63d 100644 --- a/sigap-website/app/_components/map/crime-map.tsx +++ b/sigap-website/app/_components/map/crime-map.tsx @@ -14,6 +14,7 @@ import { useState } from "react" import { CrimePopup } from "./pop-up" import CrimeMarker, { type CrimeIncident } from "./markers/crime-marker" import { MapLegend } from "./controls/map-legend" +import MapFilterControl from "./controls/map-filter-control" const months = [ { value: "1", label: "January" }, @@ -36,6 +37,7 @@ export default function CrimeMap() { const [selectedMonth, setSelectedMonth] = useState("all") const [selectedDistrict, setSelectedDistrict] = useState(null) const [selectedIncident, setSelectedIncident] = useState(null) + const [showLegend, setShowLegend] = useState(true) const { availableYears, yearsLoading, yearsError, crimes, crimesLoading, crimesError, refetchCrimes } = useCrimeMapHandler(selectedYear, selectedMonth) @@ -83,26 +85,40 @@ export default function CrimeMap() { let title = `${selectedYear}` if (selectedMonth !== "all") { title += ` - ${getMonthName(Number(selectedMonth))}` - } + } return title } + // Create map filter controls - now MapView will only render these in fullscreen mode + const mapFilterControls = ( + + ) + return ( Crime Map {getMapTitle()}
+ {/* Regular (non-fullscreen) controls */} + +
+ + + + + + {/* Make sure customControls is displayed in fullscreen mode */} + {customControls} + + )} + + {/* Main content with left padding when sidebar is open */} +
+ setMapRef(ref)} + mapStyle={mapStyle} + mapboxAccessToken={mapboxApiAccessToken} + initialViewState={defaultViewState} + onLoad={handleMapLoad} + onMoveEnd={handleMoveEnd} + interactiveLayerIds={["district-fill", "clusters", "unclustered-point"]} + attributionControl={false} + style={{ width, height }} + > + {children} + + + + + {/* GeolocateControl only shown in fullscreen mode */} + {isFullscreen && ( + + )} + +
+ + {/* Debug indicator - remove in production */} +
+ Fullscreen: {isFullscreen ? "Yes" : "No"} +
+
+ ) +} diff --git a/sigap-website/app/_components/map/markers/crime-marker.tsx b/sigap-website/app/_components/map/markers/crime-marker.tsx index 1f7a705..c974a28 100644 --- a/sigap-website/app/_components/map/markers/crime-marker.tsx +++ b/sigap-website/app/_components/map/markers/crime-marker.tsx @@ -22,7 +22,6 @@ type CrimeMarkerProps = { export default function CrimeMarker({ incident, onClick }: CrimeMarkerProps) { - console.log("CrimeMarker", incident) return ( ) { + const [isFullscreen, setIsFullscreen] = useState(false); + + useEffect(() => { + if (!ref.current) return; + + const element = ref.current; + + const handleFullscreenChange = () => { + const fullscreenElement = + document.fullscreenElement || + (document as any).webkitFullscreenElement || + (document as any).mozFullScreenElement || + (document as any).msFullscreenElement; + + setIsFullscreen( + fullscreenElement === element || + (fullscreenElement && element.contains(fullscreenElement)) + ); + }; + + // Add event listeners for fullscreen changes + document.addEventListener('fullscreenchange', handleFullscreenChange); + document.addEventListener('webkitfullscreenchange', handleFullscreenChange); + document.addEventListener('mozfullscreenchange', handleFullscreenChange); + document.addEventListener('MSFullscreenChange', handleFullscreenChange); + + // Clean up event listeners + return () => { + document.removeEventListener('fullscreenchange', handleFullscreenChange); + document.removeEventListener( + 'webkitfullscreenchange', + handleFullscreenChange + ); + document.removeEventListener( + 'mozfullscreenchange', + handleFullscreenChange + ); + document.removeEventListener( + 'MSFullscreenChange', + handleFullscreenChange + ); + }; + }, [ref]); + + // Function to request fullscreen + const enterFullscreen = () => { + if (!ref.current) return; + + const element = ref.current; + + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if ((element as any).webkitRequestFullscreen) { + (element as any).webkitRequestFullscreen(); + } else if ((element as any).mozRequestFullScreen) { + (element as any).mozRequestFullScreen(); + } else if ((element as any).msRequestFullscreen) { + (element as any).msRequestFullscreen(); + } + }; + + // Function to exit fullscreen + const exitFullscreen = () => { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if ((document as any).webkitExitFullscreen) { + (document as any).webkitExitFullscreen(); + } else if ((document as any).mozCancelFullScreen) { + (document as any).mozCancelFullScreen(); + } else if ((document as any).msExitFullscreen) { + (document as any).msExitFullscreen(); + } + }; + + const toggleFullscreen = () => { + if (isFullscreen) { + exitFullscreen(); + } else { + enterFullscreen(); + } + }; + + return { isFullscreen, enterFullscreen, exitFullscreen, toggleFullscreen }; +} diff --git a/sigap-website/prisma/data/jsons/sample-data.json b/sigap-website/prisma/data/jsons/sample-data.json index 240a43c..70720f4 100644 --- a/sigap-website/prisma/data/jsons/sample-data.json +++ b/sigap-website/prisma/data/jsons/sample-data.json @@ -27,14 +27,14 @@ "incidents": [ { "id": "CI-3509-2243-2024", - "timestamp": "2024-01-17T02:43:00.000Z", - "description": "Laporan kenakalan remaja terjadi pada Wed Jan 17 2024 09:43:00 GMT+0700 (Western Indonesia Time) di jalan utama Jombang", + "timestamp": "2024-01-19T09:48:00.000Z", + "description": "Kasus penyelenggaraan pemilu terjadi di Jalan Gajah Mada", "status": "resolved", - "category": "Kenakalan Remaja", + "category": "Penyelenggaraan Pemilu", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Gajah Mada Blok H-7, Jombang, Jember", + "latitude": -8.220942806386573, + "longitude": 113.3642056086129 } ] }, @@ -65,14 +65,14 @@ "incidents": [ { "id": "CI-3509-2244-2024", - "timestamp": "2024-02-26T16:31:00.000Z", - "description": "Kejadian perlindungan anak di perbatasan Jombang", + "timestamp": "2024-02-03T19:37:00.000Z", + "description": "Sistem Peradilan Anak terdeteksi di sekitar Jombang pada 2:37:00 AM", "status": "resolved", - "category": "Perlindungan Anak", + "category": "Sistem Peradilan Anak", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Alun-alun Jombang, Jalan Jawa, Jember", + "latitude": -8.225862928612479, + "longitude": 113.3545810955381 } ] }, @@ -103,47 +103,47 @@ "incidents": [ { "id": "CI-3509-2245-2024", - "timestamp": "2024-03-06T19:08:00.000Z", - "description": "Insiden perlindungan konsumen terjadi di perbatasan Jombang", + "timestamp": "2024-03-16T23:04:00.000Z", + "description": "Kejadian fidusia di sekitar Jombang", "status": "resolved", - "category": "Perlindungan Konsumen", + "category": "Fidusia", "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Diponegoro No. 19, Jombang, Jember", + "latitude": -8.21880601996157, + "longitude": 113.3583240255355 }, { "id": "CI-3509-2246-2024", - "timestamp": "2024-03-21T04:28:00.000Z", - "description": "Laporan terhadap ketertiban umum terjadi pada Thu Mar 21 2024 11:28:00 GMT+0700 (Western Indonesia Time) di jalan utama Jombang", + "timestamp": "2024-03-09T01:45:00.000Z", + "description": "Pelaporan keimigrasian di Jalan Srikandi Blok B-15, Jombang, Jember", "status": "resolved", - "category": "Terhadap Ketertiban Umum", + "category": "Keimigrasian", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Srikandi Blok B-15, Jombang, Jember", + "latitude": -8.218269599612134, + "longitude": 113.3537943056248 }, { "id": "CI-3509-2247-2024", - "timestamp": "2024-03-14T23:13:00.000Z", - "description": "Insiden selundup senpi terjadi di jalan utama Jombang", + "timestamp": "2024-03-08T02:47:00.000Z", + "description": "Membahayakan Kam Umum terjadi di dekat pertigaan Jombang", "status": "resolved", - "category": "Selundup Senpi", - "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Membahayakan Kam Umum", + "type": "Pidana Umum", + "address": "Jalan Kalimantan No. 58, Jombang, Jember", + "latitude": -8.218269599612134, + "longitude": 113.3537943056248 }, { "id": "CI-3509-2248-2024", - "timestamp": "2024-03-03T16:54:00.000Z", - "description": "Insiden ekstradisi terjadi di wilayah Jombang", + "timestamp": "2024-03-20T19:06:00.000Z", + "description": "Kasus penggelapan Jalan Cendrawasih Blok O-1, Jombang, Jember", "status": "resolved", - "category": "Ekstradisi", + "category": "Penggelapan", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Cendrawasih Blok O-1, Jombang, Jember", + "latitude": -8.226009836151528, + "longitude": 113.3552530903767 } ] }, @@ -174,25 +174,25 @@ "incidents": [ { "id": "CI-3509-2249-2024", - "timestamp": "2024-04-03T12:14:00.000Z", - "description": "Insiden agraria terjadi di pasar Jombang", + "timestamp": "2024-04-14T09:23:00.000Z", + "description": "Curat terdeteksi di area Jalan Cendrawasih pada 4:23:00 PM", "status": "resolved", - "category": "Agraria", + "category": "Curat", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Cendrawasih Blok P-9, Jombang, Jember", + "latitude": -8.219532604872622, + "longitude": 113.3658183785155 }, { "id": "CI-3509-2250-2024", - "timestamp": "2024-04-22T10:17:00.000Z", - "description": "Curanmor dilaporkan di daerah Jombang", + "timestamp": "2024-04-03T10:56:00.000Z", + "description": "Pidter Lainnya terdeteksi di perumahan Jombang pada 5:56:00 PM", "status": "resolved", - "category": "Curanmor", - "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Pidter Lainnya", + "type": "Pidana Tertentu", + "address": "Sekolah Jombang, Jalan Cendrawasih, Jember", + "latitude": -8.217365999685471, + "longitude": 113.364745657837 } ] }, @@ -223,25 +223,25 @@ "incidents": [ { "id": "CI-3509-2251-2024", - "timestamp": "2024-05-30T03:01:00.000Z", - "description": "Kasus trans ekonomi crime Jalan Raya Sumberbaru No. 1, Jombang, Jember", + "timestamp": "2024-05-29T19:25:00.000Z", + "description": "Kasus perlindungan saksi – korban terjadi di Jalan Letjen Suprapto", "status": "resolved", - "category": "Trans Ekonomi Crime", - "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Perlindungan Saksi – Korban", + "type": "Pidana Umum", + "address": "Jalan Letjen Suprapto Blok G-13, Jombang, Jember", + "latitude": -8.220640105831363, + "longitude": 113.3593557708753 }, { "id": "CI-3509-2252-2024", - "timestamp": "2024-05-24T01:40:00.000Z", - "description": "Insiden terhadap ketertiban umum terjadi di daerah Jombang", + "timestamp": "2024-05-06T09:30:00.000Z", + "description": "Kejadian penghinaan di kawasan pertokoan Jombang", "status": "resolved", - "category": "Terhadap Ketertiban Umum", + "category": "Penghinaan", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Pertokoan Jombang, Jalan Srikandi, Jember", + "latitude": -8.223170621517482, + "longitude": 113.3596837062433 } ] }, @@ -272,14 +272,14 @@ "incidents": [ { "id": "CI-3509-2253-2024", - "timestamp": "2024-06-04T12:32:00.000Z", - "description": "Insiden pengrusakan terjadi di perbatasan Jombang", + "timestamp": "2024-06-28T15:18:00.000Z", + "description": "Laporan trafficking in person terjadi pada Fri Jun 28 2024 22:18:00 GMT+0700 (Western Indonesia Time) di perbatasan Jombang", "status": "resolved", - "category": "Pengrusakan", - "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Trafficking In Person", + "type": "Pidana Tertentu", + "address": "Komplek Jombang, Jalan Pantai, Jember", + "latitude": -8.225037955873907, + "longitude": 113.356324271068 } ] }, @@ -310,25 +310,25 @@ "incidents": [ { "id": "CI-3509-2254-2024", - "timestamp": "2024-07-01T18:32:00.000Z", - "description": "Penadahan dilaporkan di jalan utama Jombang", + "timestamp": "2024-07-13T09:33:00.000Z", + "description": "Laporan trans ekonomi crime terjadi pada Sat Jul 13 2024 16:33:00 GMT+0700 (Western Indonesia Time) di persimpangan jalan Jalan Raya Sumberbaru", "status": "resolved", - "category": "Penadahan", - "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Trans Ekonomi Crime", + "type": "Pidana Tertentu", + "address": "Jalan Raya Sumberbaru Blok E-3, Jombang, Jember", + "latitude": -8.218091578513922, + "longitude": 113.3615109511363 }, { "id": "CI-3509-2255-2024", - "timestamp": "2024-07-09T17:49:00.000Z", - "description": "Satwa dilaporkan di daerah Jombang", + "timestamp": "2024-07-02T23:46:00.000Z", + "description": "Pelaporan penganiayaan ringan di Jalan Jawa No. 13, Jombang, Jember", "status": "resolved", - "category": "Satwa", - "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Penganiayaan Ringan", + "type": "Pidana Umum", + "address": "Jalan Jawa No. 13, Jombang, Jember", + "latitude": -8.227294841217901, + "longitude": 113.3512174459733 } ] }, @@ -359,36 +359,36 @@ "incidents": [ { "id": "CI-3509-2256-2024", - "timestamp": "2024-08-25T09:25:00.000Z", - "description": "Kasus penyelenggaraan pemilu Jalan Raya Sumberbaru No. 1, Jombang, Jember", + "timestamp": "2024-08-08T20:58:00.000Z", + "description": "Pelaporan fidusia di Toko Jombang, Jalan Cendrawasih, Jember", "status": "resolved", - "category": "Penyelenggaraan Pemilu", - "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Fidusia", + "type": "Pidana Tertentu", + "address": "Toko Jombang, Jalan Cendrawasih, Jember", + "latitude": -8.221020024793528, + "longitude": 113.3600760414435 }, { "id": "CI-3509-2257-2024", - "timestamp": "2024-08-30T03:36:00.000Z", - "description": "Laporan pekerjakan anak terjadi pada Fri Aug 30 2024 10:36:00 GMT+0700 (Western Indonesia Time) di pasar Jombang", + "timestamp": "2024-08-01T05:39:00.000Z", + "description": "Insiden pengrusakan terjadi di pasar Jombang", "status": "resolved", - "category": "Pekerjakan Anak", + "category": "Pengrusakan", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Toko Jombang, Jalan Srikandi, Jember", + "latitude": -8.220330780964309, + "longitude": 113.3575533505413 }, { "id": "CI-3509-2258-2024", - "timestamp": "2024-08-21T13:52:00.000Z", - "description": "Kasus menerima suap Jalan Raya Sumberbaru No. 1, Jombang, Jember", + "timestamp": "2024-08-24T03:35:00.000Z", + "description": "Laporan trafficking in person terjadi pada Sat Aug 24 2024 10:35:00 GMT+0700 (Western Indonesia Time) di wilayah Jombang", "status": "resolved", - "category": "Menerima Suap", - "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Trafficking In Person", + "type": "Pidana Tertentu", + "address": "Alun-alun Jombang, Jalan Jawa, Jember", + "latitude": -8.225862928612479, + "longitude": 113.3545810955381 } ] }, @@ -419,25 +419,25 @@ "incidents": [ { "id": "CI-3509-2259-2024", - "timestamp": "2024-09-12T22:50:00.000Z", - "description": "Kejadian korupsi di pasar Jombang", + "timestamp": "2024-09-01T12:23:00.000Z", + "description": "Kasus premanisme terjadi di Jalan Cendrawasih", "status": "resolved", - "category": "Korupsi", - "type": "Korupsi", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Premanisme", + "type": "Pidana Umum", + "address": "Jalan Cendrawasih No. 57, Jombang, Jember", + "latitude": -8.223364642360638, + "longitude": 113.3587958397817 }, { "id": "CI-3509-2260-2024", - "timestamp": "2024-09-01T17:56:00.000Z", - "description": "Laporan pemalsuan surat terjadi pada Mon Sep 02 2024 00:56:00 GMT+0700 (Western Indonesia Time) di daerah Jombang", + "timestamp": "2024-09-05T14:40:00.000Z", + "description": "Kejadian sistem peradilan anak di jalan utama Jombang", "status": "resolved", - "category": "Pemalsuan Surat", + "category": "Sistem Peradilan Anak", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Mastrip Blok E-7, Jombang, Jember", + "latitude": -8.214910387946766, + "longitude": 113.3617351868134 } ] }, @@ -468,38 +468,63 @@ "incidents": [ { "id": "CI-3509-2261-2024", - "timestamp": "2024-10-26T02:32:00.000Z", - "description": "Laporan menerima suap terjadi pada Sat Oct 26 2024 09:32:00 GMT+0700 (Western Indonesia Time) di pasar Jombang", + "timestamp": "2024-10-11T14:44:00.000Z", + "description": "Insiden lahgun senpi/handak/sajam dilaporkan warga setempat di jalan utama Jombang", "status": "resolved", - "category": "Menerima Suap", + "category": "Lahgun Senpi/Handak/Sajam", "type": "Pidana Umum", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Jalan Pantai No. 92, Jombang, Jember", + "latitude": -8.222114170247359, + "longitude": 113.3585291112248 }, { "id": "CI-3509-2262-2024", - "timestamp": "2024-10-25T14:48:00.000Z", - "description": "Trafficking In Person dilaporkan di wilayah Jombang", + "timestamp": "2024-10-11T20:10:00.000Z", + "description": "Insiden perlindungan konsumen terjadi di belakang sekolah Jombang", "status": "resolved", - "category": "Trafficking In Person", + "category": "Perlindungan Konsumen", "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "address": "Sekolah Jombang, Jalan Jawa, Jember", + "latitude": -8.229801790715758, + "longitude": 113.3609559436243 }, { "id": "CI-3509-2263-2024", - "timestamp": "2024-10-31T12:47:00.000Z", - "description": "Kasus money loudering Jalan Raya Sumberbaru No. 1, Jombang, Jember", + "timestamp": "2024-10-22T21:08:00.000Z", + "description": "Perlindungan Anak terdeteksi di belakang perempatan Jombang pada 4:08:00 AM", "status": "resolved", - "category": "Money Loudering", - "type": "Pidana Tertentu", - "address": "Jalan Raya Sumberbaru No. 1, Jombang, Jember", - "latitude": -8.207667404400098, - "longitude": 113.3497229500003 + "category": "Perlindungan Anak", + "type": "Pidana Umum", + "address": "Jalan Srikandi No. 64, Jombang, Jember", + "latitude": -8.22081341311974, + "longitude": 113.3589319157199 } ] } - ] + ], + "meta": { + "values": { + "0.incidents.0.timestamp": ["Date"], + "1.incidents.0.timestamp": ["Date"], + "2.incidents.0.timestamp": ["Date"], + "2.incidents.1.timestamp": ["Date"], + "2.incidents.2.timestamp": ["Date"], + "2.incidents.3.timestamp": ["Date"], + "3.incidents.0.timestamp": ["Date"], + "3.incidents.1.timestamp": ["Date"], + "4.incidents.0.timestamp": ["Date"], + "4.incidents.1.timestamp": ["Date"], + "5.incidents.0.timestamp": ["Date"], + "6.incidents.0.timestamp": ["Date"], + "6.incidents.1.timestamp": ["Date"], + "7.incidents.0.timestamp": ["Date"], + "7.incidents.1.timestamp": ["Date"], + "7.incidents.2.timestamp": ["Date"], + "8.incidents.0.timestamp": ["Date"], + "8.incidents.1.timestamp": ["Date"], + "9.incidents.0.timestamp": ["Date"], + "9.incidents.1.timestamp": ["Date"], + "9.incidents.2.timestamp": ["Date"] + } + } }