Untitled
unknown
plain_text
9 months ago
25 kB
7
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