localgreenchain/pages/analytics/transport.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

232 lines
7.3 KiB
TypeScript

/**
* Transport Analytics Page
* Carbon footprint and food miles analysis
*/
import { useState, useEffect } from 'react';
import Link from 'next/link';
import {
KPICard,
DateRangePicker,
LineChart,
BarChart,
PieChart,
AreaChart,
Gauge,
DataTable,
} from '../../components/analytics';
import { TimeRange, TransportAnalytics } from '../../lib/analytics/types';
export default function TransportAnalyticsPage() {
const [timeRange, setTimeRange] = useState<TimeRange>('30d');
const [loading, setLoading] = useState(true);
const [data, setData] = useState<TransportAnalytics | null>(null);
useEffect(() => {
fetchData();
}, [timeRange]);
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(`/api/analytics/transport?timeRange=${timeRange}`);
const result = await response.json();
setData(result.data);
} catch (error) {
console.error('Failed to fetch transport analytics:', error);
} finally {
setLoading(false);
}
};
const methodColumns = [
{ key: 'method', header: 'Method' },
{ key: 'count', header: 'Events', align: 'right' as const },
{ key: 'distanceKm', header: 'Distance (km)', align: 'right' as const, render: (v: number) => v.toLocaleString() },
{ key: 'carbonKg', header: 'Carbon (kg)', align: 'right' as const, render: (v: number) => v.toFixed(2) },
{
key: 'efficiency',
header: 'Efficiency',
align: 'right' as const,
render: (v: number) => (
<span className={`font-medium ${v >= 80 ? 'text-green-600' : v >= 50 ? 'text-yellow-600' : 'text-red-600'}`}>
{v}%
</span>
),
},
];
const routeColumns = [
{ key: 'from', header: 'From' },
{ key: 'to', header: 'To' },
{ key: 'method', header: 'Method' },
{ key: 'distanceKm', header: 'Distance', align: 'right' as const, render: (v: number) => `${v} km` },
{ key: 'frequency', header: 'Frequency', align: 'right' as const },
];
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">
{/* Header */}
<div className="bg-gradient-to-r from-teal-600 to-cyan-600 text-white">
<div className="max-w-7xl mx-auto px-4 py-8">
<h1 className="text-3xl font-bold">Transport Analytics</h1>
<p className="text-teal-200 mt-1">Carbon footprint and food miles analysis</p>
</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-white hover:bg-gray-50 text-gray-700 rounded-lg font-medium shadow">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-teal-500 text-white rounded-lg font-medium">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-4 gap-6 mb-8">
<KPICard
title="Total Events"
value={data?.totalEvents || 0}
trend="up"
color="blue"
loading={loading}
/>
<KPICard
title="Total Distance"
value={data?.totalDistanceKm?.toLocaleString() || '0'}
unit="km"
trend="stable"
color="purple"
loading={loading}
/>
<KPICard
title="Carbon Emitted"
value={data?.totalCarbonKg?.toFixed(1) || '0'}
unit="kg CO2"
trend="down"
color="orange"
loading={loading}
/>
<KPICard
title="Carbon Saved"
value={data?.carbonSavedKg?.toFixed(1) || '0'}
unit="kg CO2"
trend="up"
color="green"
loading={loading}
/>
</div>
{/* Gauges */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mb-8">
<Gauge
value={data?.totalCarbonKg ? (data.carbonSavedKg / (data.totalCarbonKg + data.carbonSavedKg)) * 100 : 0}
title="Carbon Reduction"
unit="%"
/>
<Gauge
value={100 - ((data?.averageDistancePerEvent || 0) / 50) * 100}
title="Distance Efficiency"
unit="%"
/>
<Gauge
value={data?.eventsByMethod?.filter(m => m.efficiency >= 80).length
? (data.eventsByMethod.filter(m => m.efficiency >= 80).reduce((s, m) => s + m.count, 0) / data.totalEvents) * 100
: 0}
title="Green Transport"
unit="%"
/>
<Gauge
value={75}
title="Local Sourcing"
unit="%"
/>
</div>
{/* Charts */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
{data?.carbonTrend && (
<AreaChart
data={data.carbonTrend}
xKey="label"
yKey="value"
title="Carbon Emissions Trend"
colors={['#f59e0b']}
height={300}
/>
)}
{data?.eventsByMethod && (
<BarChart
data={data.eventsByMethod}
xKey="method"
yKey="carbonKg"
title="Carbon by Transport Method"
height={300}
/>
)}
</div>
{/* Event Types Pie Chart */}
{data?.eventsByType && (
<div className="mb-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<PieChart
data={data.eventsByType}
dataKey="count"
nameKey="eventType"
title="Events by Type"
height={300}
/>
<PieChart
data={data.eventsByMethod}
dataKey="distanceKm"
nameKey="method"
title="Distance by Method"
height={300}
/>
</div>
</div>
)}
{/* Tables */}
<div className="grid grid-cols-1 gap-6">
{data?.eventsByMethod && (
<DataTable
data={data.eventsByMethod}
columns={methodColumns}
title="Transport Method Breakdown"
pageSize={8}
/>
)}
{data?.mostEfficientRoutes && (
<DataTable
data={data.mostEfficientRoutes}
columns={routeColumns}
title="Most Efficient Routes"
pageSize={5}
/>
)}
</div>
</div>
</div>
);
}