diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics.ts b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics.ts index b2cfb19..01cd61c 100644 --- a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics.ts +++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics.ts @@ -1,6 +1,19 @@ import { useMemo } from 'react'; import { ICrimes } from '@/app/_utils/types/crimes'; +export interface ICrimeAnalytics { + todaysIncidents: number; + totalIncidents: number; + recentIncidents: any[]; + filteredIncidents: any[]; + categoryCounts: Record; + districts: Record; + incidentsByMonth: number[]; + clearanceRate: number; + incidentsByMonthDetail: Record; + availableMonths: string[]; +} + export function useCrimeAnalytics(crimes: ICrimes[]) { return useMemo(() => { if (!crimes || !Array.isArray(crimes) || crimes.length === 0) diff --git a/sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx b/sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx index f4920af..77d4cc7 100644 --- a/sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx +++ b/sigap-website/app/_components/map/sidebar/components/crime-type-card.tsx @@ -1,13 +1,14 @@ import React from 'react' import { Card, CardContent } from "@/app/_components/ui/card" +import { crime_categories } from '@prisma/client' -interface CrimeTypeCardProps { +export interface ICrimeTypeCardProps { type: string count: number percentage: number } -export function CrimeTypeCard({ type, count, percentage }: CrimeTypeCardProps) { +export function CrimeTypeCard({ type, count, percentage }: ICrimeTypeCardProps) { return ( diff --git a/sigap-website/app/_components/map/sidebar/components/incident-card.tsx b/sigap-website/app/_components/map/sidebar/components/incident-card.tsx index 8e89343..146a2e3 100644 --- a/sigap-website/app/_components/map/sidebar/components/incident-card.tsx +++ b/sigap-website/app/_components/map/sidebar/components/incident-card.tsx @@ -3,7 +3,7 @@ import { AlertTriangle, MapPin, Calendar } from 'lucide-react' import { Badge } from "@/app/_components/ui/badge" import { Card, CardContent } from "@/app/_components/ui/card" -interface EnhancedIncidentCardProps { +interface IIncidentCardProps { title: string time: string location: string @@ -19,7 +19,7 @@ export function IncidentCard({ severity, onClick, showTimeAgo = true -}: EnhancedIncidentCardProps) { +}: IIncidentCardProps) { const getBadgeColor = () => { switch (severity) { case "Low": return "bg-green-500/20 text-green-300"; diff --git a/sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx b/sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx index 1a53291..93a42b8 100644 --- a/sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx +++ b/sigap-website/app/_components/map/sidebar/components/sidebar-section.tsx @@ -1,12 +1,12 @@ import React from 'react' -interface SidebarSectionProps { +interface ISidebarSectionProps { title: string children: React.ReactNode icon?: React.ReactNode } -export function SidebarSection({ title, children, icon }: SidebarSectionProps) { +export function SidebarSection({ title, children, icon }: ISidebarSectionProps) { return (

diff --git a/sigap-website/app/_components/map/sidebar/components/stat-card.tsx b/sigap-website/app/_components/map/sidebar/components/stat-card.tsx index 8dd2096..eee06af 100644 --- a/sigap-website/app/_components/map/sidebar/components/stat-card.tsx +++ b/sigap-website/app/_components/map/sidebar/components/stat-card.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Card, CardContent } from "@/app/_components/ui/card" -interface StatCardProps { +interface IStatCardProps { title: string value: string change: string @@ -17,7 +17,7 @@ export function StatCard({ isPositive = false, icon, bgColor = "bg-white/10" -}: StatCardProps) { +}: IStatCardProps) { return ( diff --git a/sigap-website/app/_components/map/sidebar/components/system-status-card.tsx b/sigap-website/app/_components/map/sidebar/components/system-status-card.tsx index ac6d376..1862fd6 100644 --- a/sigap-website/app/_components/map/sidebar/components/system-status-card.tsx +++ b/sigap-website/app/_components/map/sidebar/components/system-status-card.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Card, CardContent } from "@/app/_components/ui/card" -interface SystemStatusCardProps { +interface ISystemStatusCardProps { title: string status: string statusIcon: React.ReactNode @@ -19,7 +19,7 @@ export function SystemStatusCard({ updatedTime, bgColor = "bg-sidebar-accent/20", borderColor = "border-sidebar-border" -}: SystemStatusCardProps) { +}: ISystemStatusCardProps) { return ( diff --git a/sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx b/sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx index 385fca0..c7fca91 100644 --- a/sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx +++ b/sigap-website/app/_components/map/sidebar/tabs/incidents-tab.tsx @@ -7,21 +7,43 @@ import { Button } from "@/app/_components/ui/button" import { formatMonthKey, getIncidentSeverity, getMonthName, getTimeAgo } from "@/app/_utils/common" import { SystemStatusCard } from "../components/system-status-card" import { IncidentCard } from "../components/incident-card" +import { ICrimeAnalytics } from '@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_hooks/use-crime-analytics' + +interface Incident { + id: string; + category: string; + address: string; + timestamp: string; + district?: string; + severity?: number; + status?: string; + description?: string; + location?: { + lat: number; + lng: number; + }; +} interface SidebarIncidentsTabProps { - crimeStats: any - formattedDate: string - formattedTime: string - location: string - selectedMonth?: number | "all" - selectedYear: number - selectedCategory: string | "all" - getTimePeriodDisplay: () => string - paginationState: Record - handlePageChange: (monthKey: string, direction: 'next' | 'prev') => void - handleIncidentClick: (incident: any) => void - activeIncidentTab: string - setActiveIncidentTab: (tab: string) => void + crimeStats: ICrimeAnalytics; + formattedDate: string; + formattedTime: string; + location: string; + selectedMonth?: number | "all"; + selectedYear: number; + selectedCategory: string | "all"; + getTimePeriodDisplay: () => string; + paginationState: Record; + handlePageChange: (monthKey: string, direction: 'next' | 'prev') => void; + handleIncidentClick: (incident: Incident) => void; + activeIncidentTab: string; + setActiveIncidentTab: (tab: string) => void; +} + +interface CrimeCategory { + type: string; + count: number; + percentage: number; } export function SidebarIncidentsTab({ @@ -41,12 +63,11 @@ export function SidebarIncidentsTab({ }: SidebarIncidentsTabProps) { const topCategories = crimeStats.categoryCounts ? Object.entries(crimeStats.categoryCounts) - .sort((a: any, b: any) => (b[1] as number) - (a[1] as number)) + .sort((a, b) => b[1] - a[1]) .slice(0, 4) - .map(([type, count]: [string, unknown]) => { - const countAsNumber = count as number; - const percentage = Math.round(((countAsNumber) / crimeStats.totalIncidents) * 100) || 0 - return { type, count: countAsNumber, percentage } + .map(([type, count]) => { + const percentage = Math.round((count / crimeStats.totalIncidents) * 100) || 0 + return { type, count, percentage } }) : [] return ( @@ -166,7 +187,7 @@ export function SidebarIncidentsTab({ ) : (
- {crimeStats.recentIncidents.slice(0, 6).map((incident: any) => ( + {crimeStats.recentIncidents.slice(0, 6).map((incident: Incident) => (
- {paginatedIncidents.map((incident: any) => ( + {paginatedIncidents.map((incident: Incident) => ( (b[1] as number) - (a[1] as number)) + .sort((a, b) => b[1] - a[1]) .slice(0, 4) - .map(([type, count]: [string, unknown]) => { - const countAsNumber = count as number; - const percentage = Math.round(((countAsNumber) / crimeStats.totalIncidents) * 100) || 0 - return { type, count: countAsNumber, percentage } + .map(([type, count]) => { + const percentage = Math.round(((count) / crimeStats.totalIncidents) * 100) || 0 + return { type, count, percentage } }) : [] return ( @@ -62,18 +64,11 @@ export function SidebarStatisticsTab({ })}
- Jan - Feb - Mar - Apr - May - Jun - Jul - Aug - Sep - Oct - Nov - Dec + {MONTHS.map((month, i) => ( + + {month.substring(0, 3)} + + ))}
@@ -119,7 +114,7 @@ export function SidebarStatisticsTab({ }>
{topCategories.length > 0 ? ( - topCategories.map((category: any) => ( + topCategories.map((category: ICrimeTypeCardProps) => (