Untitled
unknown
plain_text
a month ago
25 kB
3
Indexable
"use client" import { useState } from "react" import { Bar, BarChart, Line, LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from "recharts" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { AppSidebar } from "@/components/app-sidebar" import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" import { useTranslation, type SupportedLanguages } from "@/utils/translations" import { useSettings } from "@/hooks/useSettings" import { useBinConfig } from "@/hooks/use-bin-config" import { useMetrics } from "@/hooks/useMetrics" import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb" import { Separator } from "@/components/ui/separator" import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart" import { format, parseISO } from "date-fns" export default function Page() { const { settings } = useSettings() const { t } = useTranslation((settings?.language as SupportedLanguages) || "EN") const { metrics, loading, error } = useMetrics() const { bins } = useBinConfig() const [activeTab, setActiveTab] = useState("distribution") if (loading || !metrics) { return ( <SidebarProvider> <div className="flex h-screen w-full"> <AppSidebar /> <div className="flex-1 overflow-auto"> <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6"> <SidebarTrigger /> <Separator orientation="vertical" className="mx-2 h-4" /> <Breadcrumb> <BreadcrumbList> <BreadcrumbItem className="hidden sm:block"> <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator className="hidden sm:block" /> <BreadcrumbItem> <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> </header> <main className="container mx-auto p-4"> <div className="flex items-center justify-center h-64"> <p className="text-lg text-muted-foreground">Loading metrics...</p> </div> </main> </div> </div> </SidebarProvider> ) } if (error) { return ( <SidebarProvider> <div className="flex h-screen w-full"> <AppSidebar /> <div className="flex-1 overflow-auto"> <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6"> <SidebarTrigger /> <Separator orientation="vertical" className="mx-2 h-4" /> <Breadcrumb> <BreadcrumbList> <BreadcrumbItem className="hidden sm:block"> <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator className="hidden sm:block" /> <BreadcrumbItem> <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> </header> <main className="container mx-auto p-4"> <div className="flex items-center justify-center h-64"> <p className="text-lg text-destructive">Error loading metrics: {error}</p> </div> </main> </div> </div> </SidebarProvider> ) } // Prepare data for different charts based on the specified requirements // 1. Sorting Distribution: Items per bin type (bar chart) const getItemsPerBinData = () => { const data = [] Object.entries(metrics.basic_metrics.items_sorted_per_bin).forEach(([binId, count]) => { const bin = bins.find(b => b.id === binId) data.push({ bin: bin?.name || binId, count, fill: bin ? (settings?.theme === "dark" ? bin.color_dark : bin.color) : "#888888" }) }) return data.sort((a, b) => b.count - a.count) } // Config for items per bin chart const itemsPerBinConfig = { count: { label: "Items", color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" } } // 2. Classification methods (API vs. Local) const getClassificationMethodsData = () => { const { api, local } = metrics.basic_metrics.api_vs_local_usage return [ { name: "API", value: api, fill: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" }, { name: "Local", value: local, fill: settings?.theme === "dark" ? "#10b981" : "#059669" } ] } // Config for classification methods chart const classificationMethodsConfig = { api: { label: "API", color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" }, local: { label: "Local", color: settings?.theme === "dark" ? "#10b981" : "#059669" } } // 3. Daily/Weekly/Monthly sorting trends (line chart) const getSortingTrendsData = () => { // Daily data const dailyData = Object.entries(metrics.time_metrics.daily_usage_counts) .map(([date, count]) => ({ date, count, formattedDate: format(parseISO(date), 'MMM dd') })) .sort((a, b) => parseISO(a.date).getTime() - parseISO(b.date).getTime()) // Weekly data const weeklyData = Object.entries(metrics.time_metrics.weekly_usage_counts) .map(([week, count]) => ({ week, count })) .sort((a, b) => a.week.localeCompare(b.week)) // Monthly data const monthlyData = Object.entries(metrics.time_metrics.monthly_usage_counts) .map(([month, count]) => ({ month, count, formattedMonth: month.substring(5) })) .sort((a, b) => a.month.localeCompare(b.month)) return { daily: dailyData, weekly: weeklyData, monthly: monthlyData } } // Config for sorting trends chart const sortingTrendsConfig = { count: { label: "Items Sorted", color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" } } // 4. Most common waste types (table or graph) const getWasteTypesData = () => { const wasteTypes = {} // Collect all waste types from bin specialization Object.values(metrics.bin_specialization).forEach(bin => { Object.entries(bin.items_by_type).forEach(([type, count]) => { wasteTypes[type] = (wasteTypes[type] || 0) + count }) }) // Convert to array and sort by count return Object.entries(wasteTypes) .map(([type, count]) => ({ type, count })) .sort((a, b) => b.count - a.count) } // Config for waste types chart const wasteTypesConfig = { count: { label: "Items", color: settings?.theme === "dark" ? "#3b82f6" : "#2563eb" } } return ( <SidebarProvider> <div className="flex h-screen w-full"> <AppSidebar /> <div className="flex-1 overflow-auto"> <header className="flex shrink-0 items-center gap-2 border-b px-4 py-6"> <SidebarTrigger /> <Separator orientation="vertical" className="mx-2 h-4" /> <Breadcrumb> <BreadcrumbList> <BreadcrumbItem className="hidden sm:block"> <BreadcrumbLink href="/">{t('navigation.home')}</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator className="hidden sm:block" /> <BreadcrumbItem> <BreadcrumbPage>{t('navigation.patterns')}</BreadcrumbPage> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> </header> <main className="container mx-auto py-6 px-4"> <div className="flex flex-col gap-6"> <div> <h1 className="text-3xl font-bold tracking-tight">Sorting Analytics</h1> <p className="text-muted-foreground">Analyze sorting trends and patterns in detail</p> </div> <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full"> <TabsList className="mb-4"> <TabsTrigger value="distribution">Sorting Distribution</TabsTrigger> <TabsTrigger value="trends">Trends</TabsTrigger> <TabsTrigger value="classifications">Top Classifications</TabsTrigger> </TabsList> <TabsContent value="distribution" className="space-y-6"> <div className="grid grid-cols-1 gap-6"> {/* Items per bin type (bar chart) */} <Card> <CardHeader> <CardTitle>Items per Bin Type</CardTitle> <CardDescription>Distribution of sorted items across different bins</CardDescription> </CardHeader> <CardContent> <ChartContainer config={itemsPerBinConfig} className="min-h-[300px]"> <BarChart accessibilityLayer data={getItemsPerBinData()}> <CartesianGrid vertical={false} /> <XAxis dataKey="bin" axisLine={false} tickLine={false} tickMargin={8} /> <YAxis axisLine={false} tickLine={false} tickMargin={8} /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="count" nameKey="bin" radius={[4, 4, 0, 0]} fill="fill" /> </BarChart> </ChartContainer> </CardContent> </Card> {/* Classification methods (API vs. Local) */} <Card> <CardHeader> <CardTitle>Classification Methods</CardTitle> <CardDescription>Distribution between API vs. Local classification</CardDescription> </CardHeader> <CardContent> <ChartContainer config={classificationMethodsConfig} className="min-h-[300px]"> <PieChart> <Pie data={getClassificationMethodsData()} cx="50%" cy="50%" labelLine={false} outerRadius={100} dataKey="value" nameKey="name" label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`} > {getClassificationMethodsData().map((entry, index) => ( <Cell key={`cell-${index}`} fill={entry.fill} /> ))} </Pie> <Tooltip formatter={(value, name) => [`${value} items`, name]} contentStyle={{ backgroundColor: 'var(--background)', borderColor: 'var(--border)', borderRadius: '0.5rem' }} /> <Legend /> </PieChart> </ChartContainer> </CardContent> </Card> </div> </TabsContent> <TabsContent value="trends" className="space-y-6"> <div className="grid grid-cols-1 gap-6"> {/* Daily/Weekly/Monthly sorting trends (line chart) */} <Card> <CardHeader> <CardTitle>Daily Sorting Trends</CardTitle> <CardDescription>Number of items sorted each day</CardDescription> </CardHeader> <CardContent> <ChartContainer config={sortingTrendsConfig} className="min-h-[300px]"> <LineChart accessibilityLayer data={getSortingTrendsData().daily}> <CartesianGrid vertical={false} /> <XAxis dataKey="formattedDate" axisLine={false} tickLine={false} tickMargin={8} /> <YAxis axisLine={false} tickLine={false} tickMargin={8} /> <ChartTooltip content={<ChartTooltipContent />} /> <Line type="monotone" dataKey="count" name="Items Sorted" stroke={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"} strokeWidth={2} dot={{ r: 3 }} activeDot={{ r: 6 }} /> </LineChart> </ChartContainer> </CardContent> </Card> <Card> <CardHeader> <CardTitle>Monthly Sorting Trends</CardTitle> <CardDescription>Number of items sorted each month</CardDescription> </CardHeader> <CardContent> <ChartContainer config={sortingTrendsConfig} className="min-h-[300px]"> <BarChart accessibilityLayer data={getSortingTrendsData().monthly}> <CartesianGrid vertical={false} /> <XAxis dataKey="formattedMonth" axisLine={false} tickLine={false} tickMargin={8} /> <YAxis axisLine={false} tickLine={false} tickMargin={8} /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="count" name="Items Sorted" fill={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"} radius={[4, 4, 0, 0]} /> </BarChart> </ChartContainer> </CardContent> </Card> {/* Time of Day Heatmap */} <Card> <CardHeader> <CardTitle>Time of Day Heatmap</CardTitle> <CardDescription>When items are most frequently sorted</CardDescription> </CardHeader> <CardContent> <ChartContainer config={sortingTrendsConfig} className="min-h-[300px]"> <BarChart accessibilityLayer data={ metrics.time_metrics.time_of_day_patterns.map((count, hour) => ({ hour: `${hour}:00`, count })) }> <CartesianGrid vertical={false} /> <XAxis dataKey="hour" axisLine={false} tickLine={false} tickMargin={8} interval={2} /> <YAxis axisLine={false} tickLine={false} tickMargin={8} /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="count" name="Items Sorted" fill={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"} radius={[4, 4, 0, 0]} /> </BarChart> </ChartContainer> </CardContent> </Card> </div> </TabsContent> <TabsContent value="classifications" className="space-y-6"> <div className="grid grid-cols-1 gap-6"> {/* Most common waste types (table or graph) */} <Card> <CardHeader> <CardTitle>Most Common Waste Types</CardTitle> <CardDescription>Distribution of waste classifications</CardDescription> </CardHeader> <CardContent> <ChartContainer config={wasteTypesConfig} className="min-h-[300px]"> <BarChart accessibilityLayer data={getWasteTypesData()} layout="vertical" margin={{ left: 100 }} > <CartesianGrid horizontal={false} /> <XAxis type="number" axisLine={false} tickLine={false} tickMargin={8} /> <YAxis type="category" dataKey="type" axisLine={false} tickLine={false} tickMargin={8} width={100} /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="count" nameKey="type" fill={settings?.theme === "dark" ? "#3b82f6" : "#2563eb"} radius={[0, 4, 4, 0]} /> </BarChart> </ChartContainer> </CardContent> </Card> {/* Most common waste types as a table */} <Card> <CardHeader> <CardTitle>Most Common Waste Types</CardTitle> <CardDescription>Tabular view of waste classifications</CardDescription> </CardHeader> <CardContent> <div className="overflow-x-auto"> <table className="w-full border-collapse"> <thead> <tr> <th className="text-left py-3 px-4 font-medium text-muted-foreground">Waste Type</th> <th className="text-left py-3 px-4 font-medium text-muted-foreground">Count</th> <th className="text-left py-3 px-4 font-medium text-muted-foreground">Percentage</th> </tr> </thead> <tbody className="divide-y"> {getWasteTypesData().map((item, index) => { const totalItems = metrics.basic_metrics.total_items_sorted; const percentage = (item.count / totalItems) * 100; return ( <tr key={index} className="hover:bg-muted/50"> <td className="py-3 px-4">{item.type}</td> <td className="py-3 px-4">{item.count}</td> <td className="py-3 px-4">{percentage.toFixed(1)}%</td> </tr> ); })} </tbody> </table> </div> </CardContent> </Card> </div> </TabsContent> </Tabs> {/* Interactive Features Section */} <div className="space-y-4 mt-6"> <h2 className="text-2xl font-bold tracking-tight">Interactive Features</h2> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> {/* Date range comparisons */} <Card> <CardHeader> <CardTitle>Date Range Comparisons</CardTitle> <CardDescription>Compare sorting metrics between time periods</CardDescription> </CardHeader> <CardContent> <div className="flex flex-col p-6 items-center justify-center text-center border rounded-lg bg-muted/10"> <p className="text-muted-foreground"> Select date ranges to compare sorting patterns and identify trends over time. </p> <button className="mt-4 px-4 py-2 border rounded-md hover:bg-muted"> Coming soon </button> </div> </CardContent> </Card> {/* Bin fill level predictions */} <Card> <CardHeader> <CardTitle>Bin Fill Level Predictions</CardTitle> <CardDescription>Projected time until bins need emptying</CardDescription> </CardHeader> <CardContent> <div className="flex flex-col p-6 items-center justify-center text-center border rounded-lg bg-muted/10"> <p className="text-muted-foreground"> View predictive analytics on bin fill rates and optimal emptying times. </p> <button className="mt-4 px-4 py-2 border rounded-md hover:bg-muted"> Coming soon </button> </div> </CardContent> </Card> </div> </div> </div> </main> </div> </div> </SidebarProvider> ) }
Editor is loading...
Leave a Comment