localgreenchain/pages/analytics/index.tsx
Claude 816c3b3f2e
Implement Agent 7: Advanced Analytics Dashboard
Add comprehensive analytics system with:
- Analytics data layer (aggregator, metrics, trends, cache)
- 6 API endpoints (overview, plants, transport, farms, sustainability, export)
- 6 chart components (LineChart, BarChart, PieChart, AreaChart, Gauge, Heatmap)
- 5 dashboard widgets (KPICard, TrendIndicator, DataTable, DateRangePicker, FilterPanel)
- 5 dashboard pages (overview, plants, transport, farms, sustainability)
- Export functionality (CSV, JSON)

Dependencies added: recharts, d3, date-fns

Also includes minor fixes:
- Fix EnvironmentalForm spread type error
- Fix AgentOrchestrator Map iteration issues
- Fix next.config.js image domains undefined error
- Add downlevelIteration to tsconfig
2025-11-23 04:02:07 +00:00

225 lines
8 KiB
TypeScript

/**
* Analytics Dashboard - Main Page
* Comprehensive overview of all analytics data
*/
import { useState, useEffect } from 'react';
import Link from 'next/link';
import {
KPICard,
DateRangePicker,
LineChart,
BarChart,
PieChart,
} from '../../components/analytics';
import { TimeRange, AnalyticsOverview, PlantAnalytics, TransportAnalytics } from '../../lib/analytics/types';
export default function AnalyticsDashboard() {
const [timeRange, setTimeRange] = useState<TimeRange>('30d');
const [loading, setLoading] = useState(true);
const [overview, setOverview] = useState<AnalyticsOverview | null>(null);
const [plantData, setPlantData] = useState<PlantAnalytics | null>(null);
const [transportData, setTransportData] = useState<TransportAnalytics | null>(null);
useEffect(() => {
fetchData();
}, [timeRange]);
const fetchData = async () => {
setLoading(true);
try {
const [overviewRes, plantRes, transportRes] = await Promise.all([
fetch(`/api/analytics/overview?timeRange=${timeRange}`),
fetch(`/api/analytics/plants?timeRange=${timeRange}`),
fetch(`/api/analytics/transport?timeRange=${timeRange}`),
]);
const overviewData = await overviewRes.json();
const plantDataRes = await plantRes.json();
const transportDataRes = await transportRes.json();
setOverview(overviewData.data);
setPlantData(plantDataRes.data);
setTransportData(transportDataRes.data);
} catch (error) {
console.error('Failed to fetch analytics data:', error);
} finally {
setLoading(false);
}
};
const handleExport = (format: 'csv' | 'json') => {
window.open(`/api/analytics/export?format=${format}&timeRange=${timeRange}`, '_blank');
};
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">
{/* Header */}
<div className="bg-gradient-to-r from-green-600 to-emerald-600 text-white">
<div className="max-w-7xl mx-auto px-4 py-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Analytics Dashboard</h1>
<p className="text-green-200 mt-1">
Comprehensive insights into your LocalGreenChain network
</p>
</div>
<div className="flex items-center space-x-4">
<button
onClick={() => handleExport('csv')}
className="px-4 py-2 bg-white/20 hover:bg-white/30 rounded-lg text-sm font-medium transition-colors"
>
Export CSV
</button>
<button
onClick={() => handleExport('json')}
className="px-4 py-2 bg-white/20 hover:bg-white/30 rounded-lg text-sm font-medium transition-colors"
>
Export JSON
</button>
</div>
</div>
</div>
</div>
<div className="max-w-7xl mx-auto px-4 py-8">
{/* Time Range Selector */}
<div className="mb-8">
<DateRangePicker value={timeRange} onChange={setTimeRange} />
</div>
{/* Navigation Tabs */}
<div className="flex space-x-4 mb-8">
<Link href="/analytics">
<a className="px-4 py-2 bg-green-500 text-white rounded-lg font-medium">Overview</a>
</Link>
<Link href="/analytics/plants">
<a className="px-4 py-2 bg-white hover:bg-gray-50 text-gray-700 rounded-lg font-medium shadow">Plants</a>
</Link>
<Link href="/analytics/transport">
<a className="px-4 py-2 bg-white hover:bg-gray-50 text-gray-700 rounded-lg font-medium shadow">Transport</a>
</Link>
<Link href="/analytics/farms">
<a className="px-4 py-2 bg-white hover:bg-gray-50 text-gray-700 rounded-lg font-medium shadow">Farms</a>
</Link>
<Link href="/analytics/sustainability">
<a className="px-4 py-2 bg-white hover:bg-gray-50 text-gray-700 rounded-lg font-medium shadow">Sustainability</a>
</Link>
</div>
{/* KPI Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<KPICard
title="Total Plants"
value={overview?.totalPlants || 0}
trend={overview?.trendsData?.[0]?.direction || 'stable'}
changePercent={overview?.trendsData?.[0]?.changePercent}
color="green"
loading={loading}
/>
<KPICard
title="Carbon Saved"
value={transportData?.carbonSavedKg?.toFixed(1) || '0'}
unit="kg CO2"
trend="up"
changePercent={12.5}
color="teal"
loading={loading}
/>
<KPICard
title="Food Miles"
value={transportData?.totalDistanceKm?.toFixed(0) || '0'}
unit="km"
trend="down"
changePercent={-8.7}
color="blue"
loading={loading}
/>
<KPICard
title="Active Users"
value={overview?.activeUsers || 0}
trend="up"
changePercent={8.3}
color="purple"
loading={loading}
/>
</div>
{/* Charts Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
{/* Plant Registrations Trend */}
{plantData?.registrationsTrend && (
<LineChart
data={plantData.registrationsTrend}
xKey="label"
yKey="value"
title="Plant Registrations Over Time"
height={300}
/>
)}
{/* Species Distribution */}
{plantData?.plantsBySpecies && (
<PieChart
data={plantData.plantsBySpecies}
dataKey="count"
nameKey="species"
title="Plants by Species"
height={300}
/>
)}
</div>
{/* Transport Charts */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
{/* Transport Methods */}
{transportData?.eventsByMethod && (
<BarChart
data={transportData.eventsByMethod}
xKey="method"
yKey="count"
title="Transport Events by Method"
height={300}
horizontal
/>
)}
{/* Carbon Trend */}
{transportData?.carbonTrend && (
<LineChart
data={transportData.carbonTrend}
xKey="label"
yKey="value"
title="Carbon Emissions Trend"
colors={['#ef4444']}
height={300}
/>
)}
</div>
{/* Summary Stats */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-bold text-gray-900 mb-4">Network Summary</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="text-center p-4 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-gray-900">{overview?.plantsRegisteredToday || 0}</p>
<p className="text-sm text-gray-500">Registered Today</p>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-gray-900">{overview?.plantsRegisteredThisWeek || 0}</p>
<p className="text-sm text-gray-500">This Week</p>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-gray-900">{overview?.plantsRegisteredThisMonth || 0}</p>
<p className="text-sm text-gray-500">This Month</p>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-green-600">{overview?.growthRate?.toFixed(1) || 0}%</p>
<p className="text-sm text-gray-500">Growth Rate</p>
</div>
</div>
</div>
</div>
</div>
);
}