localgreenchain/components/analytics/TrendIndicator.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

105 lines
2.5 KiB
TypeScript

/**
* Trend Indicator Component
* Shows trend direction with visual indicators
*/
import { TrendDirection } from '../../lib/analytics/types';
interface TrendIndicatorProps {
direction: TrendDirection;
value?: number;
showLabel?: boolean;
size?: 'sm' | 'md' | 'lg';
}
const sizeClasses = {
sm: 'w-3 h-3',
md: 'w-4 h-4',
lg: 'w-5 h-5',
};
const textSizeClasses = {
sm: 'text-xs',
md: 'text-sm',
lg: 'text-base',
};
export default function TrendIndicator({
direction,
value,
showLabel = false,
size = 'md',
}: TrendIndicatorProps) {
const iconSize = sizeClasses[size];
const textSize = textSizeClasses[size];
const getColor = () => {
switch (direction) {
case 'up':
return 'text-green-500';
case 'down':
return 'text-red-500';
default:
return 'text-gray-400';
}
};
const getBgColor = () => {
switch (direction) {
case 'up':
return 'bg-green-100';
case 'down':
return 'bg-red-100';
default:
return 'bg-gray-100';
}
};
const getIcon = () => {
switch (direction) {
case 'up':
return (
<svg className={iconSize} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 11l5-5m0 0l5 5m-5-5v12" />
</svg>
);
case 'down':
return (
<svg className={iconSize} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 13l-5 5m0 0l-5-5m5 5V6" />
</svg>
);
default:
return (
<svg className={iconSize} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14" />
</svg>
);
}
};
const getLabel = () => {
switch (direction) {
case 'up':
return 'Increasing';
case 'down':
return 'Decreasing';
default:
return 'Stable';
}
};
return (
<div className={`inline-flex items-center space-x-1.5 px-2 py-1 rounded-full ${getBgColor()}`}>
<span className={getColor()}>{getIcon()}</span>
{value !== undefined && (
<span className={`font-medium ${getColor()} ${textSize}`}>
{value > 0 ? '+' : ''}{value.toFixed(1)}%
</span>
)}
{showLabel && (
<span className={`${getColor()} ${textSize}`}>{getLabel()}</span>
)}
</div>
);
}