185 lines
7.1 KiB
TypeScript
185 lines
7.1 KiB
TypeScript
"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 (
|
||
<div
|
||
className={cn(
|
||
"h-full bg-background border-r transition-all duration-300 flex flex-col",
|
||
isOpen ? "w-80" : "w-0 overflow-hidden",
|
||
)}
|
||
>
|
||
<div className="flex items-center justify-between p-4 border-b">
|
||
<h2 className="text-lg font-semibold">Weather Information</h2>
|
||
<Button variant="ghost" size="icon" onClick={onToggle} className="h-8 w-8">
|
||
<ChevronLeft className="h-4 w-4" />
|
||
<span className="sr-only">Close sidebar</span>
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="flex-1 overflow-auto p-4">
|
||
<Tabs defaultValue="current">
|
||
<TabsList className="grid w-full grid-cols-2">
|
||
<TabsTrigger value="current">Current</TabsTrigger>
|
||
<TabsTrigger value="forecast">Forecast</TabsTrigger>
|
||
</TabsList>
|
||
|
||
<TabsContent value="current" className="space-y-4 mt-4">
|
||
<Card>
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-2xl font-bold flex items-center justify-between">
|
||
<span>{weatherData.temperature}°F</span>
|
||
<span className="text-sm font-normal">{weatherData.condition}</span>
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="grid grid-cols-2 gap-2">
|
||
<div className="flex items-center gap-2">
|
||
<Droplets className="h-4 w-4 text-blue-500" />
|
||
<span>Humidity: {weatherData.humidity}%</span>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<Wind className="h-4 w-4 text-gray-500" />
|
||
<span>Wind: {weatherData.windSpeed} mph</span>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<div className="space-y-2">
|
||
<h3 className="text-sm font-medium">Today's Recommendations</h3>
|
||
|
||
<div className="grid grid-cols-2 gap-2">
|
||
<Card className="bg-muted/50">
|
||
<CardContent className="p-3">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="mb-1">🌂</div>
|
||
<div className="text-xs font-medium">Umbrella</div>
|
||
<div className="text-xs">No need</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card className="bg-muted/50">
|
||
<CardContent className="p-3">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="mb-1">🏞️</div>
|
||
<div className="text-xs font-medium">Outdoors</div>
|
||
<div className="text-xs text-red-500">Very poor</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
|
||
<Collapsible className="w-full">
|
||
<CollapsibleTrigger asChild>
|
||
<Button variant="ghost" size="sm" className="flex w-full justify-between p-0 h-8">
|
||
<span>Crime Statistics</span>
|
||
<ChevronRight className="h-4 w-4 transition-transform ui-open:rotate-90" />
|
||
</Button>
|
||
</CollapsibleTrigger>
|
||
<CollapsibleContent className="space-y-2 mt-2">
|
||
{crimes.length > 0 ? (
|
||
crimes.map((crime) => (
|
||
<Card key={crime.id} className="bg-muted/50">
|
||
<CardContent className="p-3">
|
||
<div className="flex justify-between items-center">
|
||
<span className="text-sm">{crime.district_name}</span>
|
||
<span
|
||
className={cn(
|
||
"text-xs px-2 py-0.5 rounded-full",
|
||
crime.level === "low" && "bg-green-100 text-green-800",
|
||
crime.level === "medium" && "bg-yellow-100 text-yellow-800",
|
||
crime.level === "high" && "bg-orange-100 text-orange-800",
|
||
crime.level === "critical" && "bg-red-100 text-red-800",
|
||
)}
|
||
>
|
||
{crime.number_of_crime}
|
||
</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))
|
||
) : (
|
||
<div className="text-sm text-muted-foreground">No crime data available</div>
|
||
)}
|
||
</CollapsibleContent>
|
||
</Collapsible>
|
||
</TabsContent>
|
||
|
||
<TabsContent value="forecast" className="mt-4">
|
||
<div className="space-y-3">
|
||
{weatherData.forecast.map((item, index) => (
|
||
<Card key={index}>
|
||
<CardContent className="p-3 flex justify-between items-center">
|
||
<div className="flex items-center gap-2">
|
||
<Cloud className="h-5 w-5 text-blue-500" />
|
||
<span>{item.time}</span>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<span>{item.condition}</span>
|
||
<span className="font-medium">{item.temperature}°</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
</TabsContent>
|
||
</Tabs>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|