+
+
7-Day Forecast
+
+ AI-Powered
+
+
+
+ {predictions.map((prediction, index) => (
+
+
+
+
{prediction.area}
+
+ {prediction.riskLevel} Risk
+
+
+
+
+
+ Predicted Crime:
+ {prediction.crimeType}
+
+
+ Confidence:
+ {prediction.confidence}
+
+
+ Trend:
+
+ {prediction.trend}
+ {prediction.trend === "Increasing" && }
+
+
+
+
+ ))}
+
+
+ Predictions based on historical data, weather patterns, and local events
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/analytics-reporting/_components/response-time-metrics.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/analytics-reporting/_components/response-time-metrics.tsx
new file mode 100644
index 0000000..b721601
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/analytics-reporting/_components/response-time-metrics.tsx
@@ -0,0 +1,103 @@
+"use client"
+
+import { Progress } from "@/app/_components/ui/progress"
+import { Clock, TrendingDown } from "lucide-react"
+import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/app/_components/ui/chart"
+import { Bar, BarChart, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"
+
+export default function ResponseTimeMetrics() {
+ // Sample data for the chart
+ const data = [
+ { name: "Violent", time: 4.2 },
+ { name: "Theft", time: 8.5 },
+ { name: "Domestic", time: 5.1 },
+ { name: "Traffic", time: 9.8 },
+ { name: "Noise", time: 12.3 },
+ ]
+
+ return (
+
+
+
+
4.2m
+
Average Response Time
+
+
+
+ -0.3m from last month
+
+
+
+
+
+
+
Priority 1 (Emergency)
+
+
+ 3.2 min avg
+
+
+
+
+
+
+
Priority 2 (Urgent)
+
+
+ 5.8 min avg
+
+
+
+
+
+
+
Priority 3 (Standard)
+
+
+ 12.5 min avg
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
Response time by incident type (minutes)
+
+ )
+}
+
+function CustomTooltip({ active, payload, label }: any) {
+ if (active && payload && payload.length) {
+ return (
+
+
+ We value your input! Share your thoughts, concerns, or suggestions to help us better serve the community.
+
+
+
+
+ Feedback Type
+
+
+
+
+
+ Suggestion
+ Concern
+ Compliment
+ Question
+
+
+
+
+
+ Your Message
+
+
+
+
+
+
+ I would like to be contacted about my feedback
+
+
+
+
+
+ Submit Feedback
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/community-header.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/community-header.tsx
new file mode 100644
index 0000000..ee9cf6b
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/community-header.tsx
@@ -0,0 +1,27 @@
+import { Button } from "@/app/_components/ui/button"
+import { Search, Phone } from "lucide-react"
+
+export default function CommunityHeader() {
+ return (
+
+
+
+
+ Crime Trends
+ By Neighborhood
+
+
+
+
+
+ 3 Months
+ 6 Months
+ 1 Year
+
+
+
+
+
+
+ <>
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+ Data source: City Police Department. Last updated: April 24, 2023
+
+
+ )
+}
+
+function CustomLineTooltip({ active, payload, label }: any) {
+ if (active && payload && payload.length) {
+ return (
+ (null)
+
+ const toggleFAQ = (index: number) => {
+ setOpenIndex(openIndex === index ? null : index)
+ }
+
+ return (
+
+ {faqs.map((faq, index) => (
+
+
toggleFAQ(index)}
+ >
+ {faq.question}
+
+
+ {openIndex === index &&
{faq.answer}
}
+
+ ))}
+
+
View All FAQs
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/neighborhood-watch.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/neighborhood-watch.tsx
new file mode 100644
index 0000000..1e3fe53
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/neighborhood-watch.tsx
@@ -0,0 +1,62 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Users, MapPin, Phone } from "lucide-react"
+
+export default function NeighborhoodWatch() {
+ const groups = [
+ {
+ name: "Downtown Watch Group",
+ coordinator: "Sarah Johnson",
+ members: 24,
+ area: "Downtown District",
+ contact: "555-123-4567",
+ },
+ {
+ name: "Westside Neighbors",
+ coordinator: "Michael Chen",
+ members: 18,
+ area: "West Residential Area",
+ contact: "555-234-5678",
+ },
+ {
+ name: "Parkview Safety",
+ coordinator: "Emily Parker",
+ members: 15,
+ area: "Park District",
+ contact: "555-345-6789",
+ },
+ ]
+
+ return (
+
+ {groups.map((group, index) => (
+
+
+
+
+
+
+
{group.name}
+
Coordinator: {group.coordinator}
+
+
+ {group.members} members
+
+
+
+
+
+ ))}
+
+
Start or Join a Group
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/report-incident.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/report-incident.tsx
new file mode 100644
index 0000000..8f63515
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/report-incident.tsx
@@ -0,0 +1,62 @@
+import { Button } from "@/app/_components/ui/button"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select"
+import { FileText, MapPin, Camera } from "lucide-react"
+
+export default function ReportIncident() {
+ return (
+
+
+ Use this form to report non-emergency incidents. For emergencies, please call 911.
+
+
+
+
+ Incident Type
+
+
+
+
+
+ Theft/Burglary
+ Vandalism
+ Suspicious Activity
+ Noise Complaint
+ Other
+
+
+
+
+
+
+
+ Description
+
+
+
+
+
+
+ Add Photo
+
+
+
+ Submit Report
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/resource-directory.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/resource-directory.tsx
new file mode 100644
index 0000000..b46965c
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/resource-directory.tsx
@@ -0,0 +1,58 @@
+import { Phone, Building, Heart, Shield } from "lucide-react"
+
+export default function ResourceDirectory() {
+ const resources = [
+ {
+ name: "Police Non-Emergency",
+ category: "Law Enforcement",
+ contact: "555-123-4567",
+ icon: Shield,
+ },
+ {
+ name: "Victim Services",
+ category: "Support",
+ contact: "555-234-5678",
+ icon: Heart,
+ },
+ {
+ name: "City Hall",
+ category: "Government",
+ contact: "555-345-6789",
+ icon: Building,
+ },
+ {
+ name: "Mental Health Hotline",
+ category: "Support",
+ contact: "555-456-7890",
+ icon: Heart,
+ },
+ ]
+
+ return (
+
+ {resources.map((resource, index) => (
+
+
+
+
+
+
+
{resource.name}
+
{resource.category}
+
+
+
+
+
+ ))}
+
+
View Full Directory
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/safety-tips.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/safety-tips.tsx
new file mode 100644
index 0000000..25f464c
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/_components/safety-tips.tsx
@@ -0,0 +1,46 @@
+import { Home, Car, Smartphone } from "lucide-react"
+
+export default function SafetyTips() {
+ const categories = [
+ {
+ name: "Home Security",
+ icon: Home,
+ tips: ["Lock all doors and windows when away", "Install motion-sensor lighting", "Consider a security system"],
+ },
+ {
+ name: "Vehicle Protection",
+ icon: Car,
+ tips: ["Always lock your vehicle", "Don't leave valuables visible", "Park in well-lit areas"],
+ },
+ {
+ name: "Digital Safety",
+ icon: Smartphone,
+ tips: ["Use strong, unique passwords", "Be cautious of suspicious emails", "Keep software updated"],
+ },
+ ]
+
+ return (
+
+ {categories.map((category) => (
+
+
+
+
+
+
{category.name}
+
+
+
+ {category.tips.map((tip, index) => (
+
+ {tip}
+
+ ))}
+
+
+ ))}
+
+
View All Safety Tips
+
+ )
+}
diff --git a/supabase/migrations/20250227092633_user_management_starter.sql b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/action.ts
similarity index 100%
rename from supabase/migrations/20250227092633_user_management_starter.sql
rename to sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/action.ts
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/page.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/page.tsx
new file mode 100644
index 0000000..6774279
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/community-engagement/page.tsx
@@ -0,0 +1,110 @@
+"use client"
+
+import { BentoGrid, BentoGridItem } from "@/app/_components/ui/bento-grid"
+import { Bell, Calendar, FileText, MapPin, MessageSquare, Phone, Users, HelpCircle, BarChart3 } from "lucide-react"
+import CommunityHeader from "./_components/community-header"
+import CrimeAlerts from "./_components/crime-alerts"
+import SafetyTips from "./_components/safety-tips"
+import CommunityEvents from "./_components/community-events"
+import NeighborhoodWatch from "./_components/neighborhood-watch"
+import ReportIncident from "./_components/report-incident"
+import CrimeStatistics from "./_components/crime-statistics"
+import ResourceDirectory from "./_components/resource-directory"
+import FAQSection from "./_components/faq-section"
+import CommunityFeedback from "./_components/community-feedback"
+import CommunityMap from "./_components/community-map"
+
+export default function CommunityEngagementPage() {
+ return (
+
+
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ rowSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-assignees.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-assignees.tsx
new file mode 100644
index 0000000..858e30d
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-assignees.tsx
@@ -0,0 +1,69 @@
+import { User } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+
+export default function CaseAssignees() {
+ const assignees = [
+ {
+ id: "OFF-1234",
+ name: "Michael Chen",
+ role: "Lead Detective",
+ badge: "ID-5678",
+ contact: "555-123-4567",
+ status: "Active",
+ },
+ {
+ id: "OFF-2345",
+ name: "Sarah Johnson",
+ role: "Forensic Specialist",
+ badge: "ID-6789",
+ contact: "555-234-5678",
+ status: "Active",
+ },
+ {
+ id: "OFF-3456",
+ name: "Robert Wilson",
+ role: "Evidence Technician",
+ badge: "ID-7890",
+ contact: "555-345-6789",
+ status: "Active",
+ },
+ {
+ id: "OFF-4567",
+ name: "James Rodriguez",
+ role: "Patrol Officer",
+ badge: "ID-8901",
+ contact: "555-456-7890",
+ status: "Standby",
+ },
+ ]
+
+ return (
+
+ {assignees.map((officer) => (
+
+
+
+
+
+
+
+
{officer.name}
+
+ {officer.status}
+
+
+
+
+ {officer.role} • Badge #{officer.badge}
+
+
+
+ ))}
+
+
+ Assign Personnel
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-documents.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-documents.tsx
new file mode 100644
index 0000000..9ccb419
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-documents.tsx
@@ -0,0 +1,92 @@
+import { Download, FileText, FilePlus, FileImage, FileSpreadsheet } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+
+export default function CaseDocuments() {
+ const documents = [
+ {
+ id: "DOC-3421",
+ name: "Initial Police Report",
+ type: "PDF",
+ size: "1.2 MB",
+ uploadedBy: "James Rodriguez",
+ uploadDate: "Apr 15, 2023",
+ icon: FileText,
+ },
+ {
+ id: "DOC-3422",
+ name: "Autopsy Report",
+ type: "PDF",
+ size: "3.5 MB",
+ uploadedBy: "Dr. Lisa Wong",
+ uploadDate: "Apr 17, 2023",
+ icon: FileText,
+ },
+ {
+ id: "DOC-3423",
+ name: "Crime Scene Photos",
+ type: "ZIP",
+ size: "24.7 MB",
+ uploadedBy: "Robert Wilson",
+ uploadDate: "Apr 15, 2023",
+ icon: FileImage,
+ },
+ {
+ id: "DOC-3424",
+ name: "Witness Statement - Emily Parker",
+ type: "DOCX",
+ size: "285 KB",
+ uploadedBy: "Michael Chen",
+ uploadDate: "Apr 20, 2023",
+ icon: FilePlus,
+ },
+ {
+ id: "DOC-3425",
+ name: "Evidence Log",
+ type: "XLSX",
+ size: "420 KB",
+ uploadedBy: "Sarah Johnson",
+ uploadDate: "Apr 22, 2023",
+ icon: FileSpreadsheet,
+ },
+ ]
+
+ return (
+
+
+
Case Documents ({documents.length})
+ Upload Document
+
+
+
+ {documents.map((doc) => (
+
+
+
+
+
+
+
+
{doc.name}
+ {doc.type}
+
+
+
+ {doc.size}
+ Uploaded: {doc.uploadDate}
+ By: {doc.uploadedBy}
+
+
+
+
+
+ Download
+
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-evidence.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-evidence.tsx
new file mode 100644
index 0000000..280e859
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-evidence.tsx
@@ -0,0 +1,93 @@
+import { FileText, ImageIcon, Package } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+
+export default function CaseEvidence() {
+ const evidenceItems = [
+ {
+ id: "EV-4523",
+ type: "Weapon",
+ description: "Kitchen knife, 8-inch blade with wooden handle",
+ dateCollected: "Apr 18, 2023",
+ status: "Processing",
+ location: "Evidence Locker B-12",
+ icon: Package,
+ },
+ {
+ id: "EV-4524",
+ type: "Photograph",
+ description: "Crime scene photos - living room area",
+ dateCollected: "Apr 15, 2023",
+ status: "Analyzed",
+ location: "Digital Storage",
+ icon: ImageIcon,
+ },
+ {
+ id: "EV-4525",
+ type: "Document",
+ description: "Victim's personal diary",
+ dateCollected: "Apr 15, 2023",
+ status: "Analyzed",
+ location: "Evidence Locker A-7",
+ icon: FileText,
+ },
+ {
+ id: "EV-4526",
+ type: "DNA Sample",
+ description: "Blood sample from kitchen floor",
+ dateCollected: "Apr 15, 2023",
+ status: "Lab Analysis",
+ location: "Forensic Lab",
+ icon: Package,
+ },
+ ]
+
+ return (
+
+
+
Evidence Items ({evidenceItems.length})
+ Add Evidence
+
+
+
+ {evidenceItems.map((item) => (
+
+
+
+
+
+
+
+
{item.id}
+ {item.type}
+
+ {item.status}
+
+
+
+
{item.description}
+
+
+ Collected: {item.dateCollected}
+ Location: {item.location}
+
+
+
+
View Details
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-header.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-header.tsx
new file mode 100644
index 0000000..2b25031
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-header.tsx
@@ -0,0 +1,47 @@
+import { AlertTriangle, Calendar, Clock } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+
+interface CaseHeaderProps {
+ caseId: string
+ title: string
+ status: string
+ priority: string
+ dateOpened: string
+ lastUpdated: string
+}
+
+export default function CaseHeader({ caseId, title, status, priority, dateOpened, lastUpdated }: CaseHeaderProps) {
+ return (
+
+
+
+
Case #{caseId}
+
+ {status}
+
+
+
+ {priority}
+
+
+
{title}
+
+
+
+ Opened: {dateOpened}
+
+
+
+ Last updated: {lastUpdated}
+
+
+
+
+
+ Update Status
+ Export Case
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-notes.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-notes.tsx
new file mode 100644
index 0000000..5a4f3e1
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-notes.tsx
@@ -0,0 +1,72 @@
+import { User } from "lucide-react"
+import { Button } from "@/app/_components/ui/button"
+import { Textarea } from "@/app/_components/ui/textarea"
+
+export default function CaseNotes() {
+ const notes = [
+ {
+ id: "N-5621",
+ content:
+ "Forensic analysis suggests the murder weapon was wiped clean before being discarded. No fingerprints were recovered, but DNA traces were found on the handle.",
+ author: "Sarah Johnson",
+ role: "Forensic Specialist",
+ timestamp: "Apr 22, 2023 • 15:10",
+ },
+ {
+ id: "N-5620",
+ content:
+ "Witness Emily Parker's statement corroborates the timeline established by the medical examiner. Victim was likely killed between 9:00 PM and 11:00 PM on April 14.",
+ author: "Michael Chen",
+ role: "Detective",
+ timestamp: "Apr 20, 2023 • 14:35",
+ },
+ {
+ id: "N-5619",
+ content:
+ "Background check on suspect John Doe shows prior assault charges that were dropped in 2021. Need to follow up with previous complainant.",
+ author: "Michael Chen",
+ role: "Detective",
+ timestamp: "Apr 19, 2023 • 10:22",
+ },
+ {
+ id: "N-5618",
+ content:
+ "Initial canvas of the neighborhood complete. Three potential witnesses identified and scheduled for interviews.",
+ author: "James Rodriguez",
+ role: "Patrol Officer",
+ timestamp: "Apr 16, 2023 • 08:45",
+ },
+ ]
+
+ return (
+
+
+
Case Notes
+
+
+
+
+ {notes.map((note) => (
+
+
+
+
+
+
{note.author}
+
({note.role})
+
{note.timestamp}
+
+
+
{note.content}
+
+ ))}
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-related.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-related.tsx
new file mode 100644
index 0000000..1104aea
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-related.tsx
@@ -0,0 +1,59 @@
+import { ArrowRight } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+
+export default function CaseRelated() {
+ const relatedCases = [
+ {
+ id: "CR-7456",
+ title: "Assault - Downtown District",
+ date: "Mar 12, 2023",
+ status: "Closed",
+ relationship: "Same suspect",
+ },
+ {
+ id: "CR-7689",
+ title: "Breaking & Entering - Westside",
+ date: "Apr 02, 2023",
+ status: "Active",
+ relationship: "Similar MO",
+ },
+ {
+ id: "CR-7712",
+ title: "Theft - Downtown District",
+ date: "Apr 10, 2023",
+ status: "Active",
+ relationship: "Same location",
+ },
+ ]
+
+ return (
+
+ {relatedCases.map((case_) => (
+
+
+
+
Case #{case_.id}
+
+ {case_.status}
+
+
+
+
{case_.title}
+
+
+ {case_.date}
+ {case_.relationship}
+
+
+
+
+
+ ))}
+
+
+ Link Related Case
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-timeline.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-timeline.tsx
new file mode 100644
index 0000000..f75ab6c
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-timeline.tsx
@@ -0,0 +1,78 @@
+import { Circle } from "lucide-react"
+
+export default function CaseTimeline() {
+ const timelineEvents = [
+ {
+ date: "Apr 22, 2023",
+ time: "14:30",
+ title: "Forensic Report Received",
+ description: "DNA analysis results from the lab confirm suspect's presence at the crime scene.",
+ user: "Sarah Johnson",
+ role: "Forensic Specialist",
+ },
+ {
+ date: "Apr 20, 2023",
+ time: "09:15",
+ title: "Witness Interview Conducted",
+ description: "Key witness provided detailed description of events and potential suspect.",
+ user: "Michael Chen",
+ role: "Detective",
+ },
+ {
+ date: "Apr 18, 2023",
+ time: "16:45",
+ title: "Evidence Collected",
+ description: "Weapon recovered from dumpster 2 blocks from crime scene. Sent for fingerprint analysis.",
+ user: "Robert Wilson",
+ role: "Evidence Technician",
+ },
+ {
+ date: "Apr 15, 2023",
+ time: "23:10",
+ title: "Case Opened",
+ description: "Officers responded to 911 call. Victim found deceased at the scene.",
+ user: "James Rodriguez",
+ role: "Patrol Officer",
+ },
+ ]
+
+ return (
+
+
+
Case Timeline
+ Add Event
+
+
+
+ {timelineEvents.map((event, index) => (
+
+ {/* Timeline connector */}
+ {index < timelineEvents.length - 1 && (
+
+ )}
+
+ {/* Timeline dot */}
+
+
+
+
+
+
+
{event.date}
+
{event.time}
+
+
+
+
{event.title}
+
{event.description}
+
+ Added by {event.user} ({event.role})
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-witnesses.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-witnesses.tsx
new file mode 100644
index 0000000..3c2c4ed
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/_components/case-witnesses.tsx
@@ -0,0 +1,87 @@
+import { User } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+
+export default function CaseWitnesses() {
+ const witnesses = [
+ {
+ id: "W-1201",
+ name: "Emily Parker",
+ type: "Eyewitness",
+ contactInfo: "555-123-4567",
+ interviewDate: "Apr 20, 2023",
+ status: "Interviewed",
+ reliability: "High",
+ notes: "Neighbor who heard argument and saw suspect leaving the scene.",
+ },
+ {
+ id: "W-1202",
+ name: "Thomas Grant",
+ type: "Character Witness",
+ contactInfo: "555-987-6543",
+ interviewDate: "Apr 19, 2023",
+ status: "Interviewed",
+ reliability: "Medium",
+ notes: "Victim's coworker who provided information about recent conflicts.",
+ },
+ {
+ id: "W-1203",
+ name: "Maria Sanchez",
+ type: "Eyewitness",
+ contactInfo: "555-456-7890",
+ interviewDate: "Pending",
+ status: "Scheduled",
+ reliability: "Unknown",
+ notes: "Passerby who may have seen suspect in the area before the incident.",
+ },
+ ]
+
+ return (
+
+
+
Witnesses & Interviews ({witnesses.length})
+ Add Witness
+
+
+
+ {witnesses.map((witness) => (
+
+
+
+
+
+
+
+
{witness.name}
+ {witness.type}
+
+ {witness.status}
+
+
+
+
+ ID: {witness.id}
+ Contact: {witness.contactInfo}
+ Reliability: {witness.reliability}
+ {witness.interviewDate !== "Pending" && Interviewed: {witness.interviewDate} }
+
+
+
{witness.notes}
+
+
+
+ {witness.status === "Interviewed" ? "View Statement" : "Schedule Interview"}
+
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/action.ts b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/action.ts
new file mode 100644
index 0000000..e69de29
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/page.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/page.tsx
new file mode 100644
index 0000000..b2775d8
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-incident/page.tsx
@@ -0,0 +1,101 @@
+"use client"
+
+import { ArrowLeft, Calendar, FileText, MessageSquare, Paperclip, Shield, User } from "lucide-react"
+import { Button } from "@/app/_components/ui/button"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/_components/ui/tabs"
+import CaseHeader from "./_components/case-header"
+import CaseTimeline from "./_components/case-timeline"
+import CaseEvidence from "./_components/case-evidence"
+import CaseWitnesses from "./_components/case-witnesses"
+import CaseDocuments from "./_components/case-documents"
+import CaseNotes from "./_components/case-notes"
+import CaseAssignees from "./_components/case-assignees"
+import CaseRelated from "./_components/case-related"
+
+export default function CrimeIncidentPage() {
+ return (
+
+
+
+
+
+ Back to Cases
+
+
+
+
+
+
+
+
+
+
+
+ Timeline
+
+
+
+ Evidence
+
+
+
+ Witnesses
+
+
+
+ Documents
+
+
+
+ Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Assigned Personnel
+
+
+
+
+
+
Related Cases
+
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/chain-of-custody.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/chain-of-custody.tsx
new file mode 100644
index 0000000..df1fb68
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/chain-of-custody.tsx
@@ -0,0 +1,84 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { ArrowRight, Clock, User } from "lucide-react"
+
+export default function ChainOfCustody() {
+ const custodyEvents = [
+ {
+ evidenceId: "EV-4523",
+ events: [
+ {
+ action: "Collected",
+ by: "Officer Wilson",
+ date: "Apr 18, 2023",
+ time: "16:45",
+ },
+ {
+ action: "Transferred",
+ by: "Officer Wilson",
+ to: "Evidence Room",
+ date: "Apr 18, 2023",
+ time: "18:30",
+ },
+ {
+ action: "Checked Out",
+ by: "Sarah Johnson",
+ date: "Apr 20, 2023",
+ time: "09:15",
+ },
+ {
+ action: "Returned",
+ by: "Sarah Johnson",
+ date: "Apr 22, 2023",
+ time: "14:30",
+ },
+ ],
+ },
+ ]
+
+ return (
+
+
+
Recent Activity
+ View All
+
+
+ {custodyEvents.map((item) => (
+
+
+
Evidence #{item.evidenceId}
+
+ {item.events.length} transfers
+
+
+
+
+ {item.events.map((event, index) => (
+
+
+ {event.action === "Collected" || event.action === "Checked Out" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {event.action}
+ by {event.by}
+ {event.to && to {event.to} }
+
+
+
+
+
+ {event.date}, {event.time}
+
+
+ ))}
+
+
+ ))}
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/digital-evidence.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/digital-evidence.tsx
new file mode 100644
index 0000000..e1114b3
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/digital-evidence.tsx
@@ -0,0 +1,66 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Download, FileText, ImageIcon, Video } from "lucide-react"
+
+export default function DigitalEvidence() {
+ const digitalItems = [
+ {
+ id: "EV-4524",
+ type: "Images",
+ description: "Crime scene photos (24 files)",
+ case: "CR-7823",
+ size: "156 MB",
+ format: "JPG",
+ icon: ImageIcon,
+ },
+ {
+ id: "EV-4530",
+ type: "Video",
+ description: "Security camera footage",
+ case: "CR-7825",
+ size: "1.2 GB",
+ format: "MP4",
+ icon: Video,
+ },
+ {
+ id: "EV-4532",
+ type: "Document",
+ description: "Forensic report",
+ case: "CR-7823",
+ size: "2.4 MB",
+ format: "PDF",
+ icon: FileText,
+ },
+ ]
+
+ return (
+
+ {digitalItems.map((item) => (
+
+
+
+
+
+
+
+
{item.id}
+ {item.format}
+
+
+
{item.description}
+
+
+ Case: {item.case}
+ {item.size}
+
+
+
+
+
+
+
+ ))}
+
+
Upload Digital Evidence
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-by-case.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-by-case.tsx
new file mode 100644
index 0000000..b1b25d4
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-by-case.tsx
@@ -0,0 +1,74 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { FileText } from "lucide-react"
+
+export default function EvidenceByCase() {
+ const cases = [
+ {
+ id: "CR-7823",
+ title: "Homicide Investigation",
+ evidenceCount: 12,
+ recentItems: [
+ { id: "EV-4523", type: "Weapon" },
+ { id: "EV-4524", type: "Photograph" },
+ { id: "EV-4525", type: "Document" },
+ ],
+ },
+ {
+ id: "CR-7825",
+ title: "Armed Robbery",
+ evidenceCount: 8,
+ recentItems: [
+ { id: "EV-4527", type: "Fingerprint" },
+ { id: "EV-4528", type: "Clothing" },
+ ],
+ },
+ {
+ id: "CR-7830",
+ title: "Kidnapping",
+ evidenceCount: 15,
+ recentItems: [
+ { id: "EV-4535", type: "Vehicle" },
+ { id: "EV-4536", type: "DNA Sample" },
+ ],
+ },
+ ]
+
+ return (
+
+ {cases.map((case_) => (
+
+
+
+
+
+
+
+
Case #{case_.id}
+
{case_.title}
+
+
+
+ {case_.evidenceCount} items
+
+
+
+
+ {case_.recentItems.map((item) => (
+
+ {item.id} ({item.type})
+
+ ))}
+
+ {case_.evidenceCount > case_.recentItems.length && (
+
+ +{case_.evidenceCount - case_.recentItems.length} more
+
+ )}
+
+
+ ))}
+
+
View All Cases
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-catalog.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-catalog.tsx
new file mode 100644
index 0000000..51e8a47
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-catalog.tsx
@@ -0,0 +1,129 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { FileText, ImageIcon, Package, Search } from "lucide-react"
+
+export default function EvidenceCatalog() {
+ const evidenceItems = [
+ {
+ id: "EV-4523",
+ type: "Weapon",
+ description: "Kitchen knife, 8-inch blade with wooden handle",
+ case: "CR-7823",
+ dateCollected: "Apr 18, 2023",
+ status: "Processing",
+ location: "Evidence Locker B-12",
+ icon: Package,
+ },
+ {
+ id: "EV-4524",
+ type: "Photograph",
+ description: "Crime scene photos - living room area",
+ case: "CR-7823",
+ dateCollected: "Apr 15, 2023",
+ status: "Analyzed",
+ location: "Digital Storage",
+ icon: ImageIcon,
+ },
+ {
+ id: "EV-4525",
+ type: "Document",
+ description: "Victim's personal diary",
+ case: "CR-7823",
+ dateCollected: "Apr 15, 2023",
+ status: "Analyzed",
+ location: "Evidence Locker A-7",
+ icon: FileText,
+ },
+ {
+ id: "EV-4526",
+ type: "DNA Sample",
+ description: "Blood sample from kitchen floor",
+ case: "CR-7823",
+ dateCollected: "Apr 15, 2023",
+ status: "Lab Analysis",
+ location: "Forensic Lab",
+ icon: Package,
+ },
+ {
+ id: "EV-4527",
+ type: "Fingerprint",
+ description: "Prints lifted from doorknob",
+ case: "CR-7825",
+ dateCollected: "Apr 20, 2023",
+ status: "Processing",
+ location: "Forensic Lab",
+ icon: FileText,
+ },
+ {
+ id: "EV-4528",
+ type: "Clothing",
+ description: "Victim's jacket with possible blood stains",
+ case: "CR-7825",
+ dateCollected: "Apr 20, 2023",
+ status: "Lab Analysis",
+ location: "Evidence Locker C-5",
+ icon: Package,
+ },
+ ]
+
+ return (
+
+
+
+ All
+ Physical
+ Digital
+
+
+
+
+
+
+
+
+ {evidenceItems.map((item) => (
+
+
+
+
+
+
+
+
{item.id}
+ {item.type}
+
+ {item.status}
+
+
+
+
{item.description}
+
+
+ Case: {item.case}
+ Collected: {item.dateCollected}
+ Location: {item.location}
+
+
+
+
View Details
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-disposal.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-disposal.tsx
new file mode 100644
index 0000000..ef54aa4
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-disposal.tsx
@@ -0,0 +1,74 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { AlertTriangle, Calendar } from "lucide-react"
+
+export default function EvidenceDisposal() {
+ const disposalItems = [
+ {
+ id: "EV-3245",
+ type: "Clothing",
+ case: "CR-6578",
+ scheduledDate: "May 15, 2023",
+ disposalType: "Return to Owner",
+ status: "Scheduled",
+ },
+ {
+ id: "EV-3246",
+ type: "Drug Sample",
+ case: "CR-6580",
+ scheduledDate: "May 20, 2023",
+ disposalType: "Destruction",
+ status: "Pending Approval",
+ },
+ {
+ id: "EV-3250",
+ type: "Electronics",
+ case: "CR-6590",
+ scheduledDate: "May 25, 2023",
+ disposalType: "Return to Owner",
+ status: "Scheduled",
+ },
+ ]
+
+ return (
+
+ {disposalItems.map((item) => (
+
+
+
+
+
+
{item.id}
+ {item.type}
+
+
+
+ Case: {item.case}
+ Method: {item.disposalType}
+
+
+
+
+ Scheduled: {item.scheduledDate}
+
+ {item.status}
+
+
+
+
+
Review
+
+ ))}
+
+
Schedule Disposal
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-header.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-header.tsx
new file mode 100644
index 0000000..e7a18a0
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-header.tsx
@@ -0,0 +1,24 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+
+export default function EvidenceHeader() {
+ return (
+
+
+
Evidence Management
+
Track, analyze, and manage physical and digital evidence
+
+
+
+
+ Items: 1,245
+
+
+ Processing: 32
+
+
+
Log New Evidence
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-search.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-search.tsx
new file mode 100644
index 0000000..9695caa
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/evidence-search.tsx
@@ -0,0 +1,57 @@
+import { Search } from "lucide-react"
+import { Button } from "@/app/_components/ui/button"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select"
+
+export default function EvidenceSearch() {
+ return (
+
+
+
+
+
+
+
+
+ Evidence Type
+
+
+
+
+
+ All Types
+ Weapon
+ DNA Sample
+ Document
+ Photograph
+ Clothing
+
+
+
+
+
+ Status
+
+
+
+
+
+ Any Status
+ Processing
+ Analyzed
+ Stored
+ Scheduled for Disposal
+
+
+
+
+
+
Search Evidence
+
+
Recent searches: EV-4523, CR-7823, "knife", "blood sample"
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/lab-analysis.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/lab-analysis.tsx
new file mode 100644
index 0000000..1bf1f85
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/lab-analysis.tsx
@@ -0,0 +1,98 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Progress } from "@/app/_components/ui/progress"
+import { FlaskRoundIcon as Flask } from "lucide-react"
+
+export default function LabAnalysis() {
+ const labItems = [
+ {
+ id: "EV-4526",
+ type: "DNA Sample",
+ description: "Blood sample from kitchen floor",
+ case: "CR-7823",
+ submittedDate: "Apr 15, 2023",
+ status: "Processing",
+ progress: 65,
+ estimatedCompletion: "Apr 25, 2023",
+ priority: "High",
+ },
+ {
+ id: "EV-4528",
+ type: "Clothing",
+ description: "Victim's jacket with possible blood stains",
+ case: "CR-7825",
+ submittedDate: "Apr 20, 2023",
+ status: "Queue",
+ progress: 10,
+ estimatedCompletion: "Apr 30, 2023",
+ priority: "Medium",
+ },
+ {
+ id: "EV-4527",
+ type: "Fingerprint",
+ description: "Prints lifted from doorknob",
+ case: "CR-7825",
+ submittedDate: "Apr 20, 2023",
+ status: "Processing",
+ progress: 40,
+ estimatedCompletion: "Apr 27, 2023",
+ priority: "High",
+ },
+ ]
+
+ return (
+
+
+
+ All
+ Processing
+ Completed
+
+
Submit to Lab
+
+
+
+ {labItems.map((item) => (
+
+
+
+
+
+
+
+
{item.id}
+ {item.type}
+
+ {item.priority}
+
+
+
+
{item.description}
+
+
+
+ Analysis Progress
+ {item.progress}%
+
+
+
+
+
+ Case: {item.case}
+ Submitted: {item.submittedDate}
+ Est. Completion: {item.estimatedCompletion}
+
+
+
+
View Results
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/storage-locations.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/storage-locations.tsx
new file mode 100644
index 0000000..d7670ac
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/_components/storage-locations.tsx
@@ -0,0 +1,82 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Progress } from "@/app/_components/ui/progress"
+import { Package } from "lucide-react"
+
+export default function StorageLocations() {
+ const locations = [
+ {
+ id: "A",
+ name: "Evidence Locker A",
+ capacity: 85,
+ items: 42,
+ securityLevel: "High",
+ },
+ {
+ id: "B",
+ name: "Evidence Locker B",
+ capacity: 90,
+ items: 81,
+ securityLevel: "High",
+ },
+ {
+ id: "C",
+ name: "Evidence Locker C",
+ capacity: 75,
+ items: 45,
+ securityLevel: "Medium",
+ },
+ {
+ id: "D",
+ name: "Digital Storage",
+ capacity: 40,
+ items: 32,
+ securityLevel: "High",
+ },
+ ]
+
+ return (
+
+ {locations.map((location) => (
+
+
+
+
+
+
{location.name}
+
Security: {location.securityLevel}
+
+
+
0.9
+ ? "bg-red-100 text-red-800 ml-auto"
+ : location.items / location.capacity > 0.7
+ ? "bg-yellow-100 text-yellow-800 ml-auto"
+ : "bg-green-100 text-green-800 ml-auto"
+ }
+ >
+ {location.items}/{location.capacity} items
+
+
+
+
0.9
+ ? "bg-red-500"
+ : location.items / location.capacity > 0.7
+ ? "bg-yellow-500"
+ : "bg-green-500"
+ }
+ />
+
+ ))}
+
+
Manage Locations
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/action.ts b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/action.ts
new file mode 100644
index 0000000..e69de29
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/page.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/page.tsx
new file mode 100644
index 0000000..57b7030
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/evidence/page.tsx
@@ -0,0 +1,92 @@
+"use client"
+
+import { BentoGrid, BentoGridItem } from "@/app/_components/ui/bento-grid"
+import { Briefcase, FileText, Package, ImageIcon, Database, Clock, AlertTriangle, Search } from "lucide-react"
+
+import EvidenceHeader from "./_components/evidence-header"
+import EvidenceCatalog from "./_components/evidence-catalog"
+import ChainOfCustody from "./_components/chain-of-custody"
+import LabAnalysis from "./_components/lab-analysis"
+import StorageLocations from "./_components/storage-locations"
+import EvidenceByCase from "./_components/evidence-by-case"
+import DigitalEvidence from "./_components/digital-evidence"
+import EvidenceDisposal from "./_components/evidence-disposal"
+import EvidenceSearch from "./_components/evidence-search"
+
+export default function EvidenceManagementPage() {
+ return (
+
+
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/department-header.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/department-header.tsx
new file mode 100644
index 0000000..c0bf8bf
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/department-header.tsx
@@ -0,0 +1,24 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+
+export default function DepartmentHeader() {
+ return (
+
+
+
Officer Management
+
Personnel, scheduling, and department resources
+
+
+
+
+ On Duty: 18
+
+
+ Off Duty: 12
+
+
+
Add Officer
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/equipment-assignments.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/equipment-assignments.tsx
new file mode 100644
index 0000000..053ede7
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/equipment-assignments.tsx
@@ -0,0 +1,65 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Car, Radio, Shield } from "lucide-react"
+
+export default function EquipmentAssignments() {
+ const equipment = [
+ {
+ type: "Vehicle",
+ id: "VEH-1234",
+ assignedTo: "Emily Parker",
+ status: "In Service",
+ icon: Car,
+ },
+ {
+ type: "Radio",
+ id: "RAD-5678",
+ assignedTo: "Michael Chen",
+ status: "In Service",
+ icon: Radio,
+ },
+ {
+ type: "Body Camera",
+ id: "CAM-9012",
+ assignedTo: "Robert Wilson",
+ status: "Maintenance",
+ icon: Shield,
+ },
+ {
+ type: "Vehicle",
+ id: "VEH-3456",
+ assignedTo: "David Thompson",
+ status: "In Service",
+ icon: Car,
+ },
+ ]
+
+ return (
+
+ {equipment.map((item, index) => (
+
+
+
+
+
+
+
+
{item.type}
+ #{item.id}
+
+
+
Assigned to: {item.assignedTo}
+
+
+
+ {item.status}
+
+
+ ))}
+
+
Manage Equipment
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/incident-reports.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/incident-reports.tsx
new file mode 100644
index 0000000..cf85d34
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/incident-reports.tsx
@@ -0,0 +1,92 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { FileText } from "lucide-react"
+
+export default function IncidentReports() {
+ const reports = [
+ {
+ id: "IR-7823",
+ title: "Traffic Stop - Speeding",
+ officer: "David Thompson",
+ date: "Apr 22, 2023",
+ status: "Submitted",
+ location: "Highway 101, Mile 23",
+ },
+ {
+ id: "IR-7824",
+ title: "Domestic Disturbance",
+ officer: "Emily Parker",
+ date: "Apr 22, 2023",
+ status: "Pending Review",
+ location: "123 Main St, Apt 4B",
+ },
+ {
+ id: "IR-7825",
+ title: "Shoplifting",
+ officer: "James Rodriguez",
+ date: "Apr 21, 2023",
+ status: "Approved",
+ location: "Downtown Mall, Store #12",
+ },
+ {
+ id: "IR-7826",
+ title: "Noise Complaint",
+ officer: "Emily Parker",
+ date: "Apr 21, 2023",
+ status: "Approved",
+ location: "456 Oak Ave",
+ },
+ ]
+
+ return (
+
+
+
+ All
+ Pending
+ Approved
+
+
New Report
+
+
+
+ {reports.map((report) => (
+
+
+
+
+
+
+
+
{report.title}
+ #{report.id}
+
+ {report.status}
+
+
+
+
+ Officer: {report.officer}
+ Date: {report.date}
+ Location: {report.location}
+
+
+
+
View
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/leave-management.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/leave-management.tsx
new file mode 100644
index 0000000..b9b9ea7
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/leave-management.tsx
@@ -0,0 +1,62 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Calendar } from "lucide-react"
+
+export default function LeaveManagement() {
+ const leaveRequests = [
+ {
+ officer: "Sarah Johnson",
+ type: "Vacation",
+ dates: "May 10-15, 2023",
+ status: "Approved",
+ requestDate: "Apr 15, 2023",
+ },
+ {
+ officer: "James Rodriguez",
+ type: "Sick Leave",
+ dates: "Apr 25, 2023",
+ status: "Pending",
+ requestDate: "Apr 24, 2023",
+ },
+ {
+ officer: "Michael Chen",
+ type: "Personal",
+ dates: "May 5, 2023",
+ status: "Pending",
+ requestDate: "Apr 20, 2023",
+ },
+ ]
+
+ return (
+
+ {leaveRequests.map((request, index) => (
+
+
+
+
+
+
+
+
{request.officer}
+
+ {request.status}
+
+
+
+
+ {request.type} • {request.dates}
+
+
+
Requested: {request.requestDate}
+
+
+ ))}
+
+
Request Leave
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-performance.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-performance.tsx
new file mode 100644
index 0000000..caf6b7a
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-performance.tsx
@@ -0,0 +1,55 @@
+import { Progress } from "@/app/_components/ui/progress"
+import { Award, TrendingUp } from "lucide-react"
+
+export default function OfficerPerformance() {
+ const topPerformers = [
+ { name: "Emily Parker", metric: "Case Clearance", value: 92 },
+ { name: "Michael Chen", metric: "Response Time", value: 88 },
+ { name: "Sarah Johnson", metric: "Evidence Processing", value: 95 },
+ ]
+
+ return (
+
+
+
+
Avg. Case Clearance
+
68%
+
+
+ +5% from last month
+
+
+
+
+
Avg. Response Time
+
4.2m
+
+
+ -0.3m from last month
+
+
+
+
+
+
+
+ Top Performers
+
+
+
+ {topPerformers.map((performer, index) => (
+
+
+ {performer.name}
+
+ {performer.metric}: {performer.value}%
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-roster.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-roster.tsx
new file mode 100644
index 0000000..b4c9636
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/officer-roster.tsx
@@ -0,0 +1,132 @@
+import { User, Shield, Phone } from "lucide-react"
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+
+export default function OfficerRoster() {
+ const officers = [
+ {
+ id: "OFF-1234",
+ name: "Michael Chen",
+ badge: "ID-5678",
+ rank: "Detective",
+ unit: "Homicide",
+ status: "On Duty",
+ contact: "555-123-4567",
+ shift: "Day",
+ },
+ {
+ id: "OFF-2345",
+ name: "Sarah Johnson",
+ badge: "ID-6789",
+ rank: "Specialist",
+ unit: "Forensics",
+ status: "On Duty",
+ contact: "555-234-5678",
+ shift: "Day",
+ },
+ {
+ id: "OFF-3456",
+ name: "Robert Wilson",
+ badge: "ID-7890",
+ rank: "Technician",
+ unit: "Evidence",
+ status: "On Duty",
+ contact: "555-345-6789",
+ shift: "Day",
+ },
+ {
+ id: "OFF-4567",
+ name: "James Rodriguez",
+ badge: "ID-8901",
+ rank: "Officer",
+ unit: "Patrol",
+ status: "Off Duty",
+ contact: "555-456-7890",
+ shift: "Night",
+ },
+ {
+ id: "OFF-5678",
+ name: "Emily Parker",
+ badge: "ID-9012",
+ rank: "Sergeant",
+ unit: "Patrol",
+ status: "On Duty",
+ contact: "555-567-8901",
+ shift: "Day",
+ },
+ {
+ id: "OFF-6789",
+ name: "David Thompson",
+ badge: "ID-0123",
+ rank: "Officer",
+ unit: "Traffic",
+ status: "On Duty",
+ contact: "555-678-9012",
+ shift: "Day",
+ },
+ ]
+
+ return (
+
+
+
+
+ All
+
+
+ On Duty
+
+
+ Off Duty
+
+
+
+
+
+
+
+
+
+ {officers.map((officer) => (
+
+
+
+
+
+
+
+
{officer.name}
+
+ {officer.status}
+
+
+
+
+
+
+ {officer.rank} • {officer.unit}
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/shift-schedule.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/shift-schedule.tsx
new file mode 100644
index 0000000..5f4e1e3
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/shift-schedule.tsx
@@ -0,0 +1,85 @@
+import { Calendar, Clock } from "lucide-react"
+
+export default function ShiftSchedule() {
+ const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ const shifts = [
+ { time: "Day (7AM-3PM)", color: "bg-blue-100" },
+ { time: "Evening (3PM-11PM)", color: "bg-purple-100" },
+ { time: "Night (11PM-7AM)", color: "bg-indigo-100" },
+ ]
+
+ // Sample schedule data
+ const schedule = {
+ "OFF-1234": [0, 0, 0, 0, 0, null, null], // Day shift Mon-Fri
+ "OFF-2345": [0, 0, 0, 0, 0, null, null], // Day shift Mon-Fri
+ "OFF-3456": [0, 0, 0, 0, 0, null, null], // Day shift Mon-Fri
+ "OFF-4567": [null, null, 2, 2, 2, 2, 2], // Night shift Wed-Sun
+ "OFF-5678": [0, 0, 0, 0, 0, null, null], // Day shift Mon-Fri
+ "OFF-6789": [1, 1, 1, 1, 1, null, null], // Evening shift Mon-Fri
+ }
+
+ const officers = [
+ { id: "OFF-1234", name: "Michael Chen" },
+ { id: "OFF-2345", name: "Sarah Johnson" },
+ { id: "OFF-3456", name: "Robert Wilson" },
+ { id: "OFF-4567", name: "James Rodriguez" },
+ { id: "OFF-5678", name: "Emily Parker" },
+ { id: "OFF-6789", name: "David Thompson" },
+ ]
+
+ return (
+
+
+
+
+ Week of April 24-30, 2023
+
+
+
+
+
+
+
+
+
+
+
Officer
+ {days.map((day, i) => (
+
+ {day}
+
+ ))}
+
+
+ {officers.map((officer) => (
+
+
{officer.name}
+ {days.map((day, dayIndex) => {
+ const shiftIndex = schedule[officer.id]?.[dayIndex]
+ const shift = shifts[shiftIndex]
+
+ return (
+
+ {shift ? (
+
{shift.time.split(" ")[0]}
+ ) : (
+
Off
+ )}
+
+ )
+ })}
+
+ ))}
+
+
+
+ {shifts.map((shift) => (
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/specialized-units.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/specialized-units.tsx
new file mode 100644
index 0000000..0626016
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/specialized-units.tsx
@@ -0,0 +1,67 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Shield, Users } from "lucide-react"
+
+export default function SpecializedUnits() {
+ const units = [
+ {
+ name: "SWAT",
+ members: 8,
+ status: "Available",
+ lead: "Capt. Anderson",
+ },
+ {
+ name: "K-9 Unit",
+ members: 5,
+ status: "On Call",
+ lead: "Lt. Martinez",
+ },
+ {
+ name: "Narcotics",
+ members: 6,
+ status: "Deployed",
+ lead: "Sgt. Williams",
+ },
+ ]
+
+ return (
+
+ {units.map((unit, index) => (
+
+
+
+
+
+
+
+
{unit.name}
+
+ {unit.status}
+
+
+
+
+
+
+ {unit.members} members
+
+
Lead: {unit.lead}
+
+
+
+
Details
+
+ ))}
+
+
View All Units
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/training-status.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/training-status.tsx
new file mode 100644
index 0000000..17ee40e
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/_components/training-status.tsx
@@ -0,0 +1,61 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Progress } from "@/app/_components/ui/progress"
+
+export default function TrainingStatus() {
+ const trainings = [
+ {
+ name: "Firearms Qualification",
+ dueDate: "May 15, 2023",
+ status: "Completed",
+ completion: 100,
+ },
+ {
+ name: "De-escalation Techniques",
+ dueDate: "June 10, 2023",
+ status: "In Progress",
+ completion: 60,
+ },
+ {
+ name: "Emergency Response",
+ dueDate: "July 22, 2023",
+ status: "Not Started",
+ completion: 0,
+ },
+ {
+ name: "Evidence Handling",
+ dueDate: "May 30, 2023",
+ status: "In Progress",
+ completion: 75,
+ },
+ ]
+
+ return (
+
+ {trainings.map((training, index) => (
+
+
+
{training.name}
+
+ {training.status}
+
+
+
+
+
+
Due: {training.dueDate}
+
+ ))}
+
+
View All Trainings
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/action.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/action.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/page.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/page.tsx
new file mode 100644
index 0000000..b85ac13
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/officer/page.tsx
@@ -0,0 +1,92 @@
+"use client"
+
+import { BentoGrid, BentoGridItem } from "@/app/_components/ui/bento-grid"
+import { Shield, Users, Calendar, Award, Clock, FileText, Briefcase } from "lucide-react"
+import DepartmentHeader from "./_components/department-header"
+import OfficerRoster from "./_components/officer-roster"
+import ShiftSchedule from "./_components/shift-schedule"
+import OfficerPerformance from "./_components/officer-performance"
+import TrainingStatus from "./_components/training-status"
+import EquipmentAssignments from "./_components/equipment-assignments"
+import IncidentReports from "./_components/incident-reports"
+import LeaveManagement from "./_components/leave-management"
+import SpecializedUnits from "./_components/specialized-units"
+
+export default function OfficerManagementPage() {
+ return (
+
+
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ rowSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/active-incidents.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/active-incidents.tsx
new file mode 100644
index 0000000..5ecde88
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/active-incidents.tsx
@@ -0,0 +1,143 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+import { AlertTriangle, MapPin, Clock, Users } from "lucide-react"
+
+export default function ActiveIncidents() {
+ const incidents = [
+ {
+ id: "INC-4523",
+ type: "Domestic Disturbance",
+ location: "123 Main St, Apt 4B",
+ priority: "High",
+ status: "Units Responding",
+ timeReceived: "10:23 AM",
+ responseTime: "2m 15s",
+ assignedUnits: ["Unit 12", "Unit 15"],
+ },
+ {
+ id: "INC-4524",
+ type: "Traffic Accident",
+ location: "Interstate 95, Mile 42",
+ priority: "Medium",
+ status: "On Scene",
+ timeReceived: "10:15 AM",
+ responseTime: "5m 30s",
+ assignedUnits: ["Unit 8", "Unit 22"],
+ },
+ {
+ id: "INC-4525",
+ type: "Burglary",
+ location: "456 Oak Ave",
+ priority: "Medium",
+ status: "Units Responding",
+ timeReceived: "10:05 AM",
+ responseTime: "4m 45s",
+ assignedUnits: ["Unit 17"],
+ },
+ {
+ id: "INC-4526",
+ type: "Medical Emergency",
+ location: "789 Pine St",
+ priority: "Critical",
+ status: "On Scene",
+ timeReceived: "9:58 AM",
+ responseTime: "3m 10s",
+ assignedUnits: ["Unit 5", "Unit 9", "Ambulance 3"],
+ },
+ ]
+
+ return (
+
+
+
+
+ All
+
+
+ Critical
+
+
+ High
+
+
+ Medium
+
+
+
Showing {incidents.length} active incidents
+
+
+
+ {incidents.map((incident) => (
+
+
+
+
+
+
{incident.type}
+ #{incident.id}
+
+ {incident.priority}
+
+
+ {incident.status}
+
+
+
+
+
+
+ {incident.location}
+
+
+
+ Received: {incident.timeReceived} (Response: {incident.responseTime})
+
+
+
+ Units: {incident.assignedUnits.join(", ")}
+
+
+
+
+
+ Update
+
+ Details
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-communications.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-communications.tsx
new file mode 100644
index 0000000..78a33e8
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-communications.tsx
@@ -0,0 +1,87 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Radio, MessageSquare, User } from "lucide-react"
+
+export default function DispatchCommunications() {
+ const communications = [
+ {
+ id: 1,
+ type: "Radio",
+ from: "Unit 12",
+ message: "Arriving on scene at 123 Main St.",
+ time: "10:32 AM",
+ priority: "Normal",
+ },
+ {
+ id: 2,
+ type: "Radio",
+ from: "Unit 8",
+ message: "Requesting additional unit for traffic control at accident scene.",
+ time: "10:28 AM",
+ priority: "High",
+ },
+ {
+ id: 3,
+ type: "Message",
+ from: "Dispatch",
+ message: "Be advised, suspect description updated for INC-4525.",
+ time: "10:25 AM",
+ priority: "Normal",
+ },
+ {
+ id: 4,
+ type: "Radio",
+ from: "Unit 5",
+ message: "Medical assistance required at 789 Pine St.",
+ time: "10:20 AM",
+ priority: "High",
+ },
+ ]
+
+ return (
+
+
+
Recent Communications
+
+ Channel: Main Dispatch
+
+
+
+ {communications.map((comm) => (
+
+
+
+ {comm.type === "Radio" ? : }
+
+
+
+ {comm.from}
+ • {comm.time}
+
+
+
+ {comm.priority}
+
+
+
+
{comm.message}
+
+ ))}
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-header.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-header.tsx
new file mode 100644
index 0000000..226a4a8
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-header.tsx
@@ -0,0 +1,29 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+import { AlertTriangle, Phone } from "lucide-react"
+
+export default function DispatchHeader() {
+ return (
+
+
+
Resource Dispatch Center
+
Emergency response coordination and unit management
+
+
+
+
+ Active Units: 18/24
+
+
+
+ High Call Volume
+
+
+
+
+ New Dispatch
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-map.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-map.tsx
new file mode 100644
index 0000000..1420622
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/dispatch-map.tsx
@@ -0,0 +1,110 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select"
+
+export default function DispatchMap() {
+ return (
+
+
+
+
+ Available Units
+
+
+ On Scene
+
+
+ Responding
+
+
+ Incidents
+
+
+
+
+
+
+
+
+ All Units
+ Patrol Units
+ Traffic Units
+ K-9 Units
+ Medical Units
+
+
+
+
+
+
+
+
+
+ Available Units: 12
+
+
+ Responding: 6
+
+
+ On Scene: 6
+
+
+ Active Incidents: 4
+
+
+
+
+
Real-Time Dispatch Map
+
Last updated: Just now
+
+
+
+
+
+
Coverage Analysis
+
+
+ Downtown District
+
+ Good
+
+
+
+ West Side
+
+ Limited
+
+
+
+ North Area
+
+ Understaffed
+
+
+
+
+
+
+
Response Zones
+
+
+ Zone 1 (Downtown)
+ 4 units
+
+
+ Zone 2 (West)
+ 2 units
+
+
+ Zone 3 (North)
+ 1 unit
+
+
+ Zone 4 (East)
+ 3 units
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/incident-history.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/incident-history.tsx
new file mode 100644
index 0000000..fc6da5a
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/incident-history.tsx
@@ -0,0 +1,73 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Clock, FileText } from "lucide-react"
+
+export default function IncidentHistory() {
+ const closedIncidents = [
+ {
+ id: "INC-4520",
+ type: "Traffic Stop",
+ location: "Highway 101, Mile 35",
+ resolution: "Citation Issued",
+ timeReceived: "9:15 AM",
+ timeClosed: "9:45 AM",
+ units: ["Unit 22"],
+ },
+ {
+ id: "INC-4521",
+ type: "Alarm Activation",
+ location: "First National Bank",
+ resolution: "False Alarm",
+ timeReceived: "9:22 AM",
+ timeClosed: "9:40 AM",
+ units: ["Unit 7", "Unit 10"],
+ },
+ {
+ id: "INC-4522",
+ type: "Welfare Check",
+ location: "234 Cedar Lane",
+ resolution: "Assistance Provided",
+ timeReceived: "9:30 AM",
+ timeClosed: "9:55 AM",
+ units: ["Unit 15"],
+ },
+ ]
+
+ return (
+
+
+
Recently Closed
+
View All
+
+
+ {closedIncidents.map((incident) => (
+
+
+
+
+
+
+
+
+ {incident.type}
+ #{incident.id}
+
+
{incident.location}
+
+
+
+ {incident.resolution}
+
+
+
+
+
+
+ {incident.timeReceived} - {incident.timeClosed}
+
+
Units: {incident.units.join(", ")}
+
+
+ ))}
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/priority-queue.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/priority-queue.tsx
new file mode 100644
index 0000000..4a70072
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/priority-queue.tsx
@@ -0,0 +1,86 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Button } from "@/app/_components/ui/button"
+import { MapPin, Clock, ArrowRight } from "lucide-react"
+
+export default function PriorityQueue() {
+ const queuedCalls = [
+ {
+ id: "INC-4527",
+ type: "Noise Complaint",
+ location: "567 Elm St, Apt 12",
+ priority: "Low",
+ timeReceived: "10:28 AM",
+ waitTime: "5m 12s",
+ },
+ {
+ id: "INC-4528",
+ type: "Suspicious Person",
+ location: "Main Street Park",
+ priority: "Medium",
+ timeReceived: "10:25 AM",
+ waitTime: "8m 35s",
+ },
+ {
+ id: "INC-4529",
+ type: "Shoplifting",
+ location: "Downtown Mall, Store #5",
+ priority: "Medium",
+ timeReceived: "10:20 AM",
+ waitTime: "13m 40s",
+ },
+ ]
+
+ return (
+
+
+
Pending Calls: {queuedCalls.length}
+
+ Avg Wait: 9m 15s
+
+
+
+ {queuedCalls.map((call) => (
+
+
+
+
{call.type}
+
+ {call.priority}
+
+
+
+
+
+
+ {call.location}
+
+
+
+ Waiting: {call.waitTime}
+
+
+
+
+
+ ))}
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/resource-availability.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/resource-availability.tsx
new file mode 100644
index 0000000..43a1f47
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/resource-availability.tsx
@@ -0,0 +1,75 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Progress } from "@/app/_components/ui/progress"
+import { Car, Truck, Ambulance, Shield } from "lucide-react"
+
+export default function ResourceAvailability() {
+ const resources = [
+ {
+ type: "Patrol Cars",
+ total: 24,
+ available: 12,
+ icon: Car,
+ },
+ {
+ type: "K-9 Units",
+ total: 4,
+ available: 2,
+ icon: Shield,
+ },
+ {
+ type: "Ambulances",
+ total: 8,
+ available: 5,
+ icon: Ambulance,
+ },
+ {
+ type: "SWAT Team",
+ total: 1,
+ available: 1,
+ icon: Truck,
+ },
+ ]
+
+ return (
+
+ {resources.map((resource) => (
+
+
+
+
+
+
+
+
{resource.type}
+
+
+
0.5
+ ? "bg-green-100 text-green-800 ml-auto"
+ : resource.available / resource.total > 0.25
+ ? "bg-yellow-100 text-yellow-800 ml-auto"
+ : "bg-red-100 text-red-800 ml-auto"
+ }
+ >
+ {resource.available}/{resource.total} Available
+
+
+
+
0.5
+ ? "bg-green-500"
+ : resource.available / resource.total > 0.25
+ ? "bg-yellow-500"
+ : "bg-red-500"
+ }
+ />
+
+ ))}
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/response-times.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/response-times.tsx
new file mode 100644
index 0000000..0984fcf
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/response-times.tsx
@@ -0,0 +1,97 @@
+"use client"
+
+import { Progress } from "@/app/_components/ui/progress"
+import { Clock } from "lucide-react"
+import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/app/_components/ui/chart"
+import { Bar, BarChart, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"
+
+export default function ResponseTimes() {
+ // Sample data for the chart
+ const data = [
+ { name: "Critical", time: 3.2 },
+ { name: "High", time: 5.8 },
+ { name: "Medium", time: 8.5 },
+ { name: "Low", time: 12.3 },
+ ]
+
+ return (
+
+
+
+
4.2m
+
Average Response Time
+
+
+
+ Target: 5.0m
+
+
+
+
+
+
+
Priority 1 (Critical)
+
+
+ 3.2 min avg
+
+
+
+
+
+
+
Priority 2 (High)
+
+
+ 5.8 min avg
+
+
+
+
+
+
+
Priority 3 (Medium)
+
+
+ 8.5 min avg
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+ )
+}
+
+function CustomTooltip({ active, payload, label }: any) {
+ if (active && payload && payload.length) {
+ return (
+
+ {label} Priority
+
+ Avg. Response Time:
+ {payload[0].value} minutes
+
+
+ }>
+
+ )
+ }
+
+ return null
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/shift-schedule.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/shift-schedule.tsx
new file mode 100644
index 0000000..a36983c
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/shift-schedule.tsx
@@ -0,0 +1,80 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Calendar, Clock } from "lucide-react"
+
+export default function ShiftSchedule() {
+ const currentShift = {
+ name: "Day Shift",
+ hours: "7:00 AM - 3:00 PM",
+ supervisor: "Sgt. Parker",
+ officers: 18,
+ status: "Active",
+ }
+
+ const upcomingShift = {
+ name: "Evening Shift",
+ hours: "3:00 PM - 11:00 PM",
+ supervisor: "Sgt. Rodriguez",
+ officers: 16,
+ status: "Upcoming",
+ }
+
+ return (
+
+
+
+
+ Current Schedule
+
+
April 24, 2023
+
+
+
+
+
{currentShift.name}
+
+ {currentShift.status}
+
+
+
+
+
+
+ {currentShift.hours}
+
+
+ Supervisor:
+ {currentShift.supervisor}
+
+
+ Officers on duty:
+ {currentShift.officers}
+
+
+
+
+
+
+
{upcomingShift.name}
+
+ {upcomingShift.status}
+
+
+
+
+
+
+ {upcomingShift.hours}
+
+
+ Supervisor:
+ {upcomingShift.supervisor}
+
+
+ Officers scheduled:
+ {upcomingShift.officers}
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/unit-status.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/unit-status.tsx
new file mode 100644
index 0000000..6b4f7dc
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/_components/unit-status.tsx
@@ -0,0 +1,65 @@
+import { Badge } from "@/app/_components/ui/badge"
+import { Car, User } from "lucide-react"
+
+export default function UnitStatus() {
+ const units = [
+ { id: "Unit 5", officer: "Parker", status: "On Scene", location: "789 Pine St" },
+ { id: "Unit 8", officer: "Rodriguez", status: "On Scene", location: "I-95, Mile 42" },
+ { id: "Unit 9", officer: "Johnson", status: "On Scene", location: "789 Pine St" },
+ { id: "Unit 12", officer: "Chen", status: "Responding", location: "123 Main St" },
+ { id: "Unit 15", officer: "Wilson", status: "Responding", location: "123 Main St" },
+ { id: "Unit 17", officer: "Thompson", status: "Responding", location: "456 Oak Ave" },
+ ]
+
+ return (
+
+
+
+
+ {units.map((unit) => (
+
+
+
+
+
+
+
+
{unit.id}
+
+
+ {unit.officer}
+
+
+
+
{unit.location}
+
+
+
+ {unit.status}
+
+
+ ))}
+
+
+
View All Units
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/actions.ts b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/actions.ts
new file mode 100644
index 0000000..e69de29
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/page.tsx b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/page.tsx
new file mode 100644
index 0000000..bc6bb28
--- /dev/null
+++ b/sigap-website/app/(pages)/(admin)/dashboard/crime-management/resource-dispatch/page.tsx
@@ -0,0 +1,101 @@
+"use client"
+
+import { BentoGrid, BentoGridItem } from "@/app/_components/ui/bento-grid"
+import { MapPin, Phone, Users, Car, Clock, AlertTriangle, Radio, Calendar, MessageSquare } from "lucide-react"
+import DispatchHeader from "./_components/dispatch-header"
+import ActiveIncidents from "./_components/active-incidents"
+import DispatchMap from "./_components/dispatch-map"
+import UnitStatus from "./_components/unit-status"
+import ResponseTimes from "./_components/response-times"
+import PriorityQueue from "./_components/priority-queue"
+import ResourceAvailability from "./_components/resource-availability"
+import ShiftSchedule from "./_components/shift-schedule"
+import DispatchCommunications from "./_components/dispatch-communications"
+import IncidentHistory from "./_components/incident-history"
+
+export default function ResourceDispatchPage() {
+ return (
+
+
+
+
+
+ }
+ colSpan="2"
+ >
+
+
+
+ }
+ rowSpan="2"
+ colSpan="2"
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+ )
+}
diff --git a/sigap-website/app/(pages)/(auth)/action.ts b/sigap-website/app/(pages)/(auth)/action.ts
index fbc23b1..889ae3f 100644
--- a/sigap-website/app/(pages)/(auth)/action.ts
+++ b/sigap-website/app/(pages)/(auth)/action.ts
@@ -10,89 +10,102 @@ import { createClient } from "@/app/_utils/supabase/server"
import db from "@/prisma/db";
export async function signInPasswordless(formData: FormData) {
- const instrumentationService = getInjection("IInstrumentationService")
- return await instrumentationService.instrumentServerAction("signIn", {
- recordResponse: true
+ const instrumentationService = getInjection('IInstrumentationService');
+ return await instrumentationService.instrumentServerAction(
+ 'signIn',
+ {
+ recordResponse: true,
},
- async () => {
+ async () => {
+ try {
+ const email = formData.get('email')?.toString();
- try {
- const email = formData.get("email")?.toString()
+ const signInPasswordlessController = getInjection(
+ 'ISignInPasswordlessController'
+ );
+ return await signInPasswordlessController({ email });
- const signInPasswordlessController = getInjection("ISignInPasswordlessController")
- return await signInPasswordlessController({ email })
+ // if (email) {
+ // redirect(`/verify-otp?email=${encodeURIComponent(email)}`)
+ // }
+ } catch (err) {
+ if (err instanceof InputParseError) {
+ return { error: err.message };
+ }
- // if (email) {
- // redirect(`/verify-otp?email=${encodeURIComponent(email)}`)
- // }
+ if (err instanceof AuthenticationError) {
+ return { error: 'Invalid credential. Please try again.' };
+ }
- } catch (err) {
- if (err instanceof InputParseError) {
- return { error: err.message }
- }
+ if (
+ err instanceof UnauthenticatedError ||
+ err instanceof NotFoundError
+ ) {
+ return {
+ error: err.message,
+ };
+ }
- if (err instanceof AuthenticationError) {
- return { error: "Invalid credential. Please try again." }
- }
+ const crashReporterService = getInjection('ICrashReporterService');
+ crashReporterService.report(err);
- if (err instanceof UnauthenticatedError || err instanceof NotFoundError) {
- return {
- error: 'User not found. Please tell your admin to create an account for you.',
- };
- }
-
- const crashReporterService = getInjection('ICrashReporterService');
- crashReporterService.report(err);
-
- return {
- error:
- 'An error happened. The developers have been notified. Please try again later.',
- };
- }
-
- })
+ return {
+ error:
+ 'An error happened. The developers have been notified. Please try again later.',
+ };
+ }
+ }
+ );
}
-
export async function signInWithPassword(formData: FormData) {
- const instrumentationService = getInjection("IInstrumentationService")
- return await instrumentationService.instrumentServerAction("signInWithPassword", {
- recordResponse: true
- }, async () => {
- try {
- const email = formData.get("email")?.toString()
- const password = formData.get("password")?.toString()
+ const instrumentationService = getInjection('IInstrumentationService');
+ return await instrumentationService.instrumentServerAction(
+ 'signInWithPassword',
+ {
+ recordResponse: true,
+ },
+ async () => {
+ try {
+ const email = formData.get('email')?.toString();
+ const password = formData.get('password')?.toString();
- console.log("woi:", email + " " + password)
+ // console.log("woi:", email + " " + password)
- const signInWithPasswordController = getInjection("ISignInWithPasswordController")
- await signInWithPasswordController({ email, password })
+ const signInWithPasswordController = getInjection(
+ 'ISignInWithPasswordController'
+ );
+ await signInWithPasswordController({ email, password });
- return { success: true }
- } catch (err) {
- if (err instanceof InputParseError) {
- return { error: err.message }
- }
-
- if (err instanceof AuthenticationError) {
- return { error: "Invalid credential. Please try again." }
- }
-
- if (err instanceof UnauthenticatedError || err instanceof NotFoundError) {
- return {
- error: 'User not found. Please tell your admin to create an account for you.',
- };
- }
-
- const crashReporterService = getInjection('ICrashReporterService');
- crashReporterService.report(err);
-
- return {
- error:
- 'An error happened. The developers have been notified. Please try again later.',
- };
+ return { success: true };
+ } catch (err) {
+ if (err instanceof InputParseError) {
+ return { error: err.message };
}
- })
+
+ if (err instanceof AuthenticationError) {
+ return { error: 'Invalid credential. Please try again.' };
+ }
+
+ if (
+ err instanceof UnauthenticatedError ||
+ err instanceof NotFoundError
+ ) {
+ return {
+ error: err.message,
+ };
+ }
+
+ const crashReporterService = getInjection('ICrashReporterService');
+ crashReporterService.report(err);
+
+ return {
+ error:
+ 'An error happened. The developers have been notified. Please try again later.',
+ };
+ }
+ }
+ );
}
// export async function signUp(formData: FormData) {
// const instrumentationService = getInjection("IInstrumentationService")
diff --git a/sigap-website/app/_components/ui/chart.tsx b/sigap-website/app/_components/ui/chart.tsx
new file mode 100644
index 0000000..f0a571b
--- /dev/null
+++ b/sigap-website/app/_components/ui/chart.tsx
@@ -0,0 +1,364 @@
+"use client"
+
+import { cn } from "@/app/_lib/utils"
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config?: ChartConfig // Jadikan opsional
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config = {}, ...props }, ref) => { // Nilai default adalah objek kosong
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+