diff --git a/sigap-website/app/_components/map/controls/time-control.tsx b/sigap-website/app/_components/map/controls/bottom-left/time-control.tsx similarity index 97% rename from sigap-website/app/_components/map/controls/time-control.tsx rename to sigap-website/app/_components/map/controls/bottom-left/time-control.tsx index 5dd0d11..69f317f 100644 --- a/sigap-website/app/_components/map/controls/time-control.tsx +++ b/sigap-website/app/_components/map/controls/bottom-left/time-control.tsx @@ -1,7 +1,7 @@ "use client" import { Checkbox } from "@/app/_components/ui/checkbox" import { Label } from "@/app/_components/ui/label" -import { Overlay } from "../overlay" +import { Overlay } from "../../overlay" import { ControlPosition } from "mapbox-gl" diff --git a/sigap-website/app/_components/map/controls/crime-timelapse.tsx b/sigap-website/app/_components/map/controls/bottom/crime-timelapse.tsx similarity index 100% rename from sigap-website/app/_components/map/controls/crime-timelapse.tsx rename to sigap-website/app/_components/map/controls/bottom/crime-timelapse.tsx diff --git a/sigap-website/app/_components/map/controls/time-slider.tsx b/sigap-website/app/_components/map/controls/bottom/time-slider.tsx similarity index 100% rename from sigap-website/app/_components/map/controls/time-slider.tsx rename to sigap-website/app/_components/map/controls/bottom/time-slider.tsx diff --git a/sigap-website/app/_components/map/controls/example.tsx b/sigap-website/app/_components/map/controls/example.tsx deleted file mode 100644 index e8a268f..0000000 --- a/sigap-website/app/_components/map/controls/example.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Map } from "mapbox-gl"; - -/* Idea from Stack Overflow https://stackoverflow.com/a/51683226 */ -export class CustomControl { - private _className: string; - private _title: string; - private _eventHandler: (event: MouseEvent) => void; - private _btn!: HTMLButtonElement; - private _container!: HTMLDivElement; - private _map?: Map; - private _root: any; // React root for rendering our component - - constructor({ - className = "", - title = "", - eventHandler = () => { } - }: { - className?: string; - title?: string; - eventHandler?: (event: MouseEvent) => void; - }) { - this._className = className; - this._title = title; - this._eventHandler = eventHandler; - } - - onAdd(map: Map) { - this._map = map; - this._btn = document.createElement("button"); - this._btn.className = "mapboxgl-ctrl-icon" + " " + this._className; - this._btn.type = "button"; - this._btn.title = this._title; - this._btn.onclick = this._eventHandler; - - // Apply pointer-events: auto; style dynamically - this._btn.style.pointerEvents = "auto"; - - // Dynamically append the style to the auto-generated className - const styleSheet = document.styleSheets[0]; - styleSheet.insertRule( - `.${this._className} { pointer-events: auto; }`, - styleSheet.cssRules.length - ); - - this._container = document.createElement("div"); - this._container.className = "mapboxgl-ctrl-group mapboxgl-ctrl"; - this._container.appendChild(this._btn); - - return this._container; - } - - onRemove() { - if (this._container && this._container.parentNode) { - this._container.parentNode.removeChild(this._container); - } - - // Defer unmounting React component to prevent race conditions - if (this._root) { - setTimeout(() => { - this._root.unmount(); - }); - } - - this._map = undefined; - } -} \ No newline at end of file diff --git a/sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx b/sigap-website/app/_components/map/controls/left/sidebar/components/crime-type-card.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/components/crime-type-card.tsx diff --git a/sigap-website/app/_components/map/sidebar/components/incident-card.tsx b/sigap-website/app/_components/map/controls/left/sidebar/components/incident-card.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/components/incident-card.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/components/incident-card.tsx diff --git a/sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx b/sigap-website/app/_components/map/controls/left/sidebar/components/sidebar-section.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/components/sidebar-section.tsx diff --git a/sigap-website/app/_components/map/sidebar/components/stat-card.tsx b/sigap-website/app/_components/map/controls/left/sidebar/components/stat-card.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/components/stat-card.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/components/stat-card.tsx diff --git a/sigap-website/app/_components/map/sidebar/components/system-status-card.tsx b/sigap-website/app/_components/map/controls/left/sidebar/components/system-status-card.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/components/system-status-card.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/components/system-status-card.tsx diff --git a/sigap-website/app/_components/map/sidebar/map-sidebar.tsx b/sigap-website/app/_components/map/controls/left/sidebar/map-sidebar.tsx similarity index 81% rename from sigap-website/app/_components/map/sidebar/map-sidebar.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/map-sidebar.tsx index 3c99293..d1981cb 100644 --- a/sigap-website/app/_components/map/sidebar/map-sidebar.tsx +++ b/sigap-website/app/_components/map/controls/left/sidebar/map-sidebar.tsx @@ -13,11 +13,11 @@ import { ICrimes } from "@/app/_utils/types/crimes" // Import sidebar components import { SidebarIncidentsTab } from "./tabs/incidents-tab" -import { useCrimeAnalytics } from "../../../(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics" -import { usePagination } from "../../../_hooks/use-pagination" import { getMonthName } from "@/app/_utils/common" import { SidebarInfoTab } from "./tabs/info-tab" import { SidebarStatisticsTab } from "./tabs/statistics-tab" +import { useCrimeAnalytics } from "@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics" +import { usePagination } from "@/app/_hooks/use-pagination" interface CrimeSidebarProps { className?: string @@ -211,35 +211,35 @@ export default function CrimeSidebar({ ) : ( <> - - - + + + - - - + + + - - - + + + )} diff --git a/sigap-website/app/_components/map/sidebar/sidebar-toggle.tsx b/sigap-website/app/_components/map/controls/left/sidebar/sidebar-toggle.tsx similarity index 95% rename from sigap-website/app/_components/map/sidebar/sidebar-toggle.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/sidebar-toggle.tsx index 297124b..e9216bc 100644 --- a/sigap-website/app/_components/map/sidebar/sidebar-toggle.tsx +++ b/sigap-website/app/_components/map/controls/left/sidebar/sidebar-toggle.tsx @@ -4,8 +4,9 @@ import React from "react" import { Button } from "@/app/_components/ui/button" import { cn } from "@/app/_lib/utils" import { ChevronLeft, ChevronRight } from "lucide-react" -import { Overlay } from "../overlay" + import type { ControlPosition } from "mapbox-gl" +import { Overlay } from "../../../overlay" interface SidebarToggleProps { isCollapsed: boolean diff --git a/sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx b/sigap-website/app/_components/map/controls/left/sidebar/tabs/incidents-tab.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/tabs/incidents-tab.tsx diff --git a/sigap-website/app/_components/map/sidebar/tabs/info-tab.tsx b/sigap-website/app/_components/map/controls/left/sidebar/tabs/info-tab.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/tabs/info-tab.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/tabs/info-tab.tsx diff --git a/sigap-website/app/_components/map/sidebar/tabs/statistics-tab.tsx b/sigap-website/app/_components/map/controls/left/sidebar/tabs/statistics-tab.tsx similarity index 100% rename from sigap-website/app/_components/map/sidebar/tabs/statistics-tab.tsx rename to sigap-website/app/_components/map/controls/left/sidebar/tabs/statistics-tab.tsx diff --git a/sigap-website/app/_components/map/controls/map-control.tsx b/sigap-website/app/_components/map/controls/map-control.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/sigap-website/app/_components/map/controls/map-filter-control.tsx b/sigap-website/app/_components/map/controls/map-filter-control.tsx index b52904e..1ab9e49 100644 --- a/sigap-website/app/_components/map/controls/map-filter-control.tsx +++ b/sigap-website/app/_components/map/controls/map-filter-control.tsx @@ -1,107 +1,107 @@ -"use client" +// "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 { useCallback } from "react" +// import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select" +// import { Button } from "@/app/_components/ui/button" +// import { FilterX } from "lucide-react" +// 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 -} +// 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" }, -] +// 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], - ) +// 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 handleMonthChange = useCallback( +// (value: string) => { +// onMonthChange(value === "all" ? "all" : Number(value)) +// }, +// [onMonthChange], +// ) - const isDefaultFilter = selectedYear === 2024 && selectedMonth === "all" +// const isDefaultFilter = selectedYear === 2024 && selectedMonth === "all" - return ( -
-
Map Filters
+// return ( +//
+//
Map Filters
-
- +//
+// - +// -
- - -
-
-
- ) -} +//
+// +// +//
+//
+//
+// ) +// } diff --git a/sigap-website/app/_components/map/controls/map-sidebar.tsx b/sigap-website/app/_components/map/controls/map-sidebar.tsx deleted file mode 100644 index 053e1da..0000000 --- a/sigap-website/app/_components/map/controls/map-sidebar.tsx +++ /dev/null @@ -1,184 +0,0 @@ -"use client" -import { ChevronLeft, ChevronRight, Cloud, Droplets, Wind } from "lucide-react" - -import { Button } from "@/app/_components/ui/button" -import { Card, CardContent, CardHeader, CardTitle } from "@/app/_components/ui/card" -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/_components/ui/tabs" -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/app/_components/ui/collapsible" -import { cn } from "@/app/_lib/utils" - -interface MapSidebarProps { - isOpen: boolean - onToggle: () => void - crimes?: Array<{ - id: string - district_name: string - district_id?: string - number_of_crime?: number - level?: "low" | "medium" | "high" | "critical" - incidents: any[] - }> - selectedYear?: number | string - selectedMonth?: number | string - weatherData?: { - temperature: number - condition: string - humidity: number - windSpeed: number - forecast: Array<{ - time: string - temperature: number - condition: string - }> - } -} - -export default function MapSidebar({ - isOpen, - onToggle, - crimes = [], - selectedYear, - selectedMonth, - weatherData = { - temperature: 78, - condition: "Mostly cloudy", - humidity: 65, - windSpeed: 8, - forecast: [ - { time: "Now", temperature: 78, condition: "Cloudy" }, - { time: "9:00 PM", temperature: 75, condition: "Cloudy" }, - { time: "10:00 PM", temperature: 73, condition: "Cloudy" }, - { time: "11:00 PM", temperature: 72, condition: "Cloudy" }, - { time: "12:00 AM", temperature: 70, condition: "Cloudy" }, - ], - }, -}: MapSidebarProps) { - return ( -
-
-

Weather Information

- -
- -
- - - Current - Forecast - - - - - - - {weatherData.temperature}°F - {weatherData.condition} - - - -
-
- - Humidity: {weatherData.humidity}% -
-
- - Wind: {weatherData.windSpeed} mph -
-
-
-
- -
-

Today's Recommendations

- -
- - -
-
🌂
-
Umbrella
-
No need
-
-
-
- - - -
-
🏞️
-
Outdoors
-
Very poor
-
-
-
-
-
- - - - - - - {crimes.length > 0 ? ( - crimes.map((crime) => ( - - -
- {crime.district_name} - - {crime.number_of_crime} - -
-
-
- )) - ) : ( -
No crime data available
- )} -
-
-
- - -
- {weatherData.forecast.map((item, index) => ( - - -
- - {item.time} -
-
- {item.condition} - {item.temperature}° -
-
-
- ))} -
-
-
-
-
- ) -} diff --git a/sigap-website/app/_components/map/controls/map-toggle.tsx b/sigap-website/app/_components/map/controls/map-toggle.tsx deleted file mode 100644 index f1da82c..0000000 --- a/sigap-website/app/_components/map/controls/map-toggle.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client" -import { ChevronLeft, ChevronRight } from "lucide-react" -import { Button } from "../../ui/button" -import { cn } from "@/app/_lib/utils" -import { Overlay } from "../overlay" - -interface SidebarToggleProps { - isOpen: boolean - onToggle: () => void - position?: "left" | "right" - className?: string -} - -export default function SidebarToggle({ isOpen, onToggle, position = "left", className }: SidebarToggleProps) { - return ( - - - - ) -} diff --git a/sigap-website/app/_components/map/controls/map-tooltips.tsx b/sigap-website/app/_components/map/controls/map-tooltips.tsx deleted file mode 100644 index dabe928..0000000 --- a/sigap-website/app/_components/map/controls/map-tooltips.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client" - -import { ReactNode } from "react" - -// Define the possible control IDs for the crime map -export type ITopTooltipsMapId = - // Crime data views - | "incidents" - | "heatmap" - | "trends" - | "patrol" - | "reports" - | "clusters" - | "timeline" - - // Tools and features - | "refresh" - | "search" - | "alerts" - | "layers" - | "evidence" - | "arrests"; - -// Map tools type definition -export interface IMapTool { - id: ITopTooltipsMapId; - label: string; - icon: ReactNode; - description?: string; -} - -// Default export for future expansion -export default function MapTools() { - return null; -} diff --git a/sigap-website/app/_components/map/controls/severity-indicator.tsx b/sigap-website/app/_components/map/controls/severity-indicator.tsx deleted file mode 100644 index e31253e..0000000 --- a/sigap-website/app/_components/map/controls/severity-indicator.tsx +++ /dev/null @@ -1,15 +0,0 @@ -"use client" - -export default function SeverityIndicator() { - return ( -
-
-
- Low - Medium - High -
-
-
- ) -} diff --git a/sigap-website/app/_components/map/controls/top/additional-tooltips.tsx b/sigap-website/app/_components/map/controls/top/additional-tooltips.tsx new file mode 100644 index 0000000..abdcc9a --- /dev/null +++ b/sigap-website/app/_components/map/controls/top/additional-tooltips.tsx @@ -0,0 +1,151 @@ +"use client" + +import { Button } from "@/app/_components/ui/button" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/app/_components/ui/tooltip" +import { Popover, PopoverContent, PopoverTrigger } from "@/app/_components/ui/popover" +import { ChevronDown, Layers, Siren } from "lucide-react" +import { IconMessage } from "@tabler/icons-react" + +import { useRef, useState } from "react" +import { ITooltips } from "./tooltips" +import MonthSelector from "../month-selector" +import YearSelector from "../year-selector" +import CategorySelector from "../category-selector" + +// Define the additional tools and features +const additionalTooltips = [ + { id: "reports" as ITooltips, icon: , label: "Police Report" }, + { id: "layers" as ITooltips, icon: , label: "Map Layers" }, + { id: "alerts" as ITooltips, icon: , label: "Active Alerts" }, +] + +interface AdditionalTooltipsProps { + activeControl?: string + onControlChange?: (controlId: ITooltips) => void + selectedYear: number + setSelectedYear: (year: number) => void + selectedMonth: number | "all" + setSelectedMonth: (month: number | "all") => void + selectedCategory: string | "all" + setSelectedCategory: (category: string | "all") => void + availableYears?: (number | null)[] + categories?: string[] +} + +export default function AdditionalTooltips({ + activeControl, + onControlChange, + selectedYear, + setSelectedYear, + selectedMonth, + setSelectedMonth, + selectedCategory, + setSelectedCategory, + availableYears = [2022, 2023, 2024], + categories = [], +}: AdditionalTooltipsProps) { + const [showSelectors, setShowSelectors] = useState(false) + const containerRef = useRef(null) + const [isClient, setIsClient] = useState(false) + + const container = isClient ? document.getElementById("root") : null + + return ( + <> +
+ + {additionalTooltips.map((control) => ( + + + + + +

{control.label}

+
+
+ ))} + + + + + + + +
+
+ Year: + +
+
+ Month: + +
+
+ Category: + +
+
+
+
+
+
+
+ + {showSelectors && ( +
+ + + +
+ )} + + ) +} diff --git a/sigap-website/app/_components/map/controls/top/crime-tooltips.tsx b/sigap-website/app/_components/map/controls/top/crime-tooltips.tsx new file mode 100644 index 0000000..6279a37 --- /dev/null +++ b/sigap-website/app/_components/map/controls/top/crime-tooltips.tsx @@ -0,0 +1,52 @@ +"use client" + +import { Button } from "@/app/_components/ui/button" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/app/_components/ui/tooltip" +import { AlertTriangle, BarChart2, Clock, Map, Shield, Users } from "lucide-react" +import { ITooltips } from "./tooltips" + + +// Define the primary crime data controls +const crimeTooltips = [ + { id: "incidents" as ITooltips, icon: , label: "All Incidents" }, + { id: "heatmap" as ITooltips, icon: , label: "Crime Heatmap" }, + { id: "trends" as ITooltips, icon: , label: "Crime Trends" }, + { id: "patrol" as ITooltips, icon: , label: "Patrol Areas" }, + { id: "clusters" as ITooltips, icon: , label: "Clusters" }, + { id: "timeline" as ITooltips, icon: , label: "Time Analysis" }, +] + +interface CrimeTooltipsProps { + activeControl?: string + onControlChange?: (controlId: ITooltips) => void +} + +export default function CrimeTooltips({ activeControl, onControlChange }: CrimeTooltipsProps) { + return ( +
+ + {crimeTooltips.map((control) => ( + + + + + +

{control.label}

+
+
+ ))} +
+
+ ) +} diff --git a/sigap-website/app/_components/map/controls/top-controls.tsx b/sigap-website/app/_components/map/controls/top/search-control.tsx similarity index 69% rename from sigap-website/app/_components/map/controls/top-controls.tsx rename to sigap-website/app/_components/map/controls/top/search-control.tsx index e67af28..b4580b0 100644 --- a/sigap-website/app/_components/map/controls/top-controls.tsx +++ b/sigap-website/app/_components/map/controls/top/search-control.tsx @@ -1,38 +1,15 @@ "use client" -import { useEffect, useRef, useState } from "react" import { Button } from "@/app/_components/ui/button" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/app/_components/ui/tooltip" -import { Popover, PopoverContent, PopoverTrigger } from "@/app/_components/ui/popover" -import { ChevronDown, MessageSquare, MapPin, Calendar, Info, ExternalLink } from "lucide-react" -import YearSelector from "./year-selector" -import MonthSelector from "./month-selector" -import CategorySelector from "./category-selector" -import ActionSearchBar from "@/app/_components/action-search-bar" +import { Search, XCircle, Info, ExternalLink, Calendar, MapPin, MessageSquare, FileText, Map, FolderOpen } from 'lucide-react' + +import { useEffect, useRef, useState } from "react" import { AnimatePresence, motion } from "framer-motion" -import { - AlertTriangle, - Shield, - FileText, - Users, - Map, - BarChart2, - Clock, - Filter, - Search, - RefreshCw, - Layers, - Siren, - BadgeAlert, - FolderOpen, - XCircle, -} from "lucide-react" -import { ITopTooltipsMapId } from "./map-tooltips" -import { IconAnalyze, IconMessage } from "@tabler/icons-react" -import { FloatingActionSearchBar } from "../../floating-action-search-bar" -import { format } from 'date-fns' +import ActionSearchBar from "@/app/_components/action-search-bar" import { Card } from "@/app/_components/ui/card" -import { ICrimes } from "@/app/_utils/types/crimes" +import { format } from 'date-fns' +import { ITooltips } from "./tooltips" // Expanded sample crime data with more entries for testing const SAMPLE_CRIME_DATA = [ @@ -46,8 +23,6 @@ const SAMPLE_CRIME_DATA = [ { id: "CR-34517-2024", description: "Mugging at Central Station" }, { id: "CR-14517-2024", description: "Shoplifting at Mall" }, { id: "CR-24517-2024", description: "Break-in at Office Building" }, - // Add more entries for testing (up to 100) - // ...more sample entries... ]; // Generate additional sample data for testing scrolling @@ -140,52 +115,13 @@ const ACTIONS = [ }, ] -// Define the primary crime data controls -const crimeControls = [ - { id: "incidents" as ITopTooltipsMapId, icon: , label: "All Incidents" }, - { id: "heatmap" as ITopTooltipsMapId, icon: , label: "Crime Heatmap" }, - { id: "trends" as ITopTooltipsMapId, icon: , label: "Crime Trends" }, - { id: "patrol" as ITopTooltipsMapId, icon: , label: "Patrol Areas" }, - { id: "clusters" as ITopTooltipsMapId, icon: , label: "Clusters" }, - { id: "timeline" as ITopTooltipsMapId, icon: , label: "Time Analysis" }, -] - -// Define the additional tools and features -const additionalControls = [ - { id: "reports" as ITopTooltipsMapId, icon: , label: "Police Report" }, - { id: "layers" as ITopTooltipsMapId, icon: , label: "Map Layers" }, - { id: "alerts" as ITopTooltipsMapId, icon: , label: "Active Alerts" }, -] - -interface TopControlProps { - onControlChange?: (controlId: ITopTooltipsMapId) => void +interface SearchTooltipProps { + onControlChange?: (controlId: ITooltips) => void activeControl?: string - selectedYear: number - setSelectedYear: (year: number) => void - selectedMonth: number | "all" - setSelectedMonth: (month: number | "all") => void - selectedCategory: string | "all" - setSelectedCategory: (category: string | "all") => void - availableYears?: (number | null)[] - categories?: string[] } -export default function TopControl({ - onControlChange, - activeControl, - selectedYear, - setSelectedYear, - selectedMonth, - setSelectedMonth, - selectedCategory, - setSelectedCategory, - availableYears = [2022, 2023, 2024], - categories = [], - -}: TopControlProps) { - const [showSelectors, setShowSelectors] = useState(false) +export default function SearchTooltip({ onControlChange, activeControl }: SearchTooltipProps) { const [showSearch, setShowSearch] = useState(false) - const containerRef = useRef(null) const searchInputRef = useRef(null) const [selectedSearchType, setSelectedSearchType] = useState(null) const [searchValue, setSearchValue] = useState("") @@ -203,14 +139,6 @@ export default function TopControl({ } | null>(null) const [showInfoBox, setShowInfoBox] = useState(false) - const [isClient, setIsClient] = useState(false) - - const container = isClient ? document.getElementById("root") : null - - useEffect(() => { - setIsClient(true) - }, []) - useEffect(() => { if (showSearch && searchInputRef.current) { setTimeout(() => { @@ -239,8 +167,6 @@ export default function TopControl({ initialSuggestions = EXPANDED_SAMPLE_DATA; } - console.log("Initial suggestions count:", initialSuggestions.length); - // Force a re-render by setting suggestions in the next tick setTimeout(() => { setSuggestions(initialSuggestions); @@ -383,23 +309,15 @@ export default function TopControl({ // Restore original suggestions for the current search type if (selectedSearchType) { - const currentPrefix = ACTIONS.find(action => action.id === selectedSearchType)?.prefix || ""; - const initialSuggestions = filterSuggestions(selectedSearchType, currentPrefix); - - setTimeout(() => { - setSuggestions(initialSuggestions); - }, 0); + const initialSuggestions = filterSuggestions(selectedSearchType, searchValue); + setSuggestions(initialSuggestions); } }; - const toggleSelectors = () => { - setShowSelectors(!showSelectors) - } - const toggleSearch = () => { setShowSearch(!showSearch) if (!showSearch && onControlChange) { - onControlChange("search" as ITopTooltipsMapId) + onControlChange("search" as ITooltips) setSelectedSearchType(null); setSearchValue(""); setSuggestions([]); @@ -407,153 +325,31 @@ export default function TopControl({ } return ( -
-
-
- - {crimeControls.map((control) => ( - - - - - -

{control.label}

-
-
- ))} -
-
- -
- - {additionalControls.map((control) => ( - - - - - -

{control.label}

-
-
- ))} - - - - - - - -
-
- Year: - -
-
- Month: - -
-
- Category: - -
-
-
-
-
-
-
- -
- - - - - - -

Search Incidents

-
-
-
-
+ <> +
+ + + + + + +

Search Incidents

+
+
+
- {showSelectors && ( -
- - - -
- )} - {showSearch && ( <> @@ -689,7 +485,7 @@ export default function TopControl({ ) : (
-

{selectedSuggestion?.id}

+

{selectedSuggestion?.id}

{selectedSuggestion && ( @@ -754,6 +550,6 @@ export default function TopControl({ )}
-
+ ) } diff --git a/sigap-website/app/_components/map/controls/top/tooltips.tsx b/sigap-website/app/_components/map/controls/top/tooltips.tsx new file mode 100644 index 0000000..cc1332e --- /dev/null +++ b/sigap-website/app/_components/map/controls/top/tooltips.tsx @@ -0,0 +1,90 @@ +"use client" + +import { useRef, useState } from "react" + +import CrimeTooltips from "./crime-tooltips" +import AdditionalTooltips from "./additional-tooltips" +import SearchTooltip from "./search-control" +import { ReactNode } from "react" + +// Define the possible control IDs for the crime map +export type ITooltips = + // Crime data views + | "incidents" + | "heatmap" + | "trends" + | "patrol" + | "reports" + | "clusters" + | "timeline" + + // Tools and features + | "refresh" + | "search" + | "alerts" + | "layers" + | "evidence" + | "arrests"; + +// Map tools type definition +export interface IMapTools { + id: ITooltips; + label: string; + icon: ReactNode; + description?: string; +} + +interface TooltipProps { + onControlChange?: (controlId: ITooltips) => void + activeControl?: string + selectedYear: number + setSelectedYear: (year: number) => void + selectedMonth: number | "all" + setSelectedMonth: (month: number | "all") => void + selectedCategory: string | "all" + setSelectedCategory: (category: string | "all") => void + availableYears?: (number | null)[] + categories?: string[] +} + +export default function Tooltips({ + onControlChange, + activeControl, + selectedYear, + setSelectedYear, + selectedMonth, + setSelectedMonth, + selectedCategory, + setSelectedCategory, + availableYears = [2022, 2023, 2024], + categories = [], +}: TooltipProps) { + const containerRef = useRef(null) + const [isClient, setIsClient] = useState(false) + + return ( +
+
+ {/* Crime Tooltips Component */} + + + {/* Additional Tooltips Component */} + + + {/* Search Control Component */} + +
+
+ ) +} diff --git a/sigap-website/app/_components/map/crime-map.tsx b/sigap-website/app/_components/map/crime-map.tsx index be25c50..2506dee 100644 --- a/sigap-website/app/_components/map/crime-map.tsx +++ b/sigap-website/app/_components/map/crime-map.tsx @@ -10,18 +10,17 @@ import { getMonthName } from "@/app/_utils/common" import { useRef, useState, useCallback, useMemo, useEffect } from "react" import { useFullscreen } from "@/app/_hooks/use-fullscreen" import { Overlay } from "./overlay" -import MapLegend from "./controls/map-legend" +import MapLegend from "./legends/map-legend" import { useGetAvailableYears, useGetCrimeCategories, useGetCrimes } from "@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_queries/queries" -import { ITopTooltipsMapId } from "./controls/map-tooltips" import MapSelectors from "./controls/map-selector" -import CrimeSidebar from "./sidebar/map-sidebar" -import SidebarToggle from "./sidebar/sidebar-toggle" import { cn } from "@/app/_lib/utils" import CrimePopup from "./pop-up/crime-popup" import { $Enums, crime_categories, crime_incidents, crimes, demographics, districts, geographics, locations } from "@prisma/client" -import { CrimeTimelapse } from "./controls/crime-timelapse" -import TopControl from "./controls/top-controls" +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" // Updated CrimeIncident type to match the structure in crime_incidents interface CrimeIncident { @@ -45,7 +44,7 @@ export default function CrimeMap() { const [selectedCategory, setSelectedCategory] = useState("all") const [selectedYear, setSelectedYear] = useState(2024) const [selectedMonth, setSelectedMonth] = useState("all") - const [activeControl, setActiveControl] = useState("incidents") + const [activeControl, setActiveControl] = useState("incidents") const [yearProgress, setYearProgress] = useState(0) const [isTimelapsePlaying, setisTimelapsePlaying] = useState(false) const [isSearchActive, setIsSearchActive] = useState(false) @@ -277,7 +276,7 @@ export default function CrimeMap() { }, [sidebarCollapsed]) // Handle control changes from the top controls component - const handleControlChange = (controlId: ITopTooltipsMapId) => { + const handleControlChange = (controlId: ITooltips) => { setActiveControl(controlId) // Toggle search state when search control is clicked @@ -348,7 +347,7 @@ export default function CrimeMap() { <>
-