← back to Goodquestion
src/components/historical/TrajectoryCharts.tsx
144 lines
'use client'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import {
LineChart,
Line,
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts'
import type { AreaTrajectory } from '@/types/swot'
interface TrajectoryChartsProps {
trajectories: AreaTrajectory[]
}
export function TrajectoryCharts({ trajectories }: TrajectoryChartsProps) {
const getTrendColor = (trend: 'increasing' | 'decreasing' | 'stable') => {
switch (trend) {
case 'increasing':
return '#22c55e' // green
case 'decreasing':
return '#ef4444' // red
case 'stable':
return '#3b82f6' // blue
}
}
const formatMetricName = (metric: string) => {
return metric
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}
const formatValue = (value: number, metric: string) => {
if (metric.includes('population') || metric.includes('employment')) {
return value.toLocaleString()
}
if (metric.includes('rate') || metric.includes('percentage')) {
return `${value}%`
}
if (metric.includes('income') || metric.includes('revenue')) {
return `$${value.toLocaleString()}`
}
return value.toLocaleString()
}
return (
<div className="space-y-6">
{trajectories.map((trajectory, index) => {
const color = getTrendColor(trajectory.trend)
const hasForecast = trajectory.forecast && trajectory.forecast.length > 0
// Combine historical and forecast data
const chartData = [
...trajectory.data.map(d => ({
year: d.year,
value: d.value,
type: 'Historical',
})),
...(hasForecast
? trajectory.forecast!.map(f => ({
year: f.year,
value: f.value,
type: 'Forecast',
confidence: f.confidence,
}))
: []),
]
return (
<Card key={index}>
<CardHeader>
<CardTitle>{formatMetricName(trajectory.metric)}</CardTitle>
<CardDescription>
Trend: <strong className={`text-${trajectory.trend === 'increasing' ? 'green' : trajectory.trend === 'decreasing' ? 'red' : 'blue'}-600`}>
{trajectory.trend.charAt(0).toUpperCase() + trajectory.trend.slice(1)}
</strong>
{hasForecast && ' • Includes forecast data'}
</CardDescription>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
{hasForecast ? (
<AreaChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="year" />
<YAxis
tickFormatter={(value) => formatValue(value, trajectory.metric)}
/>
<Tooltip
formatter={(value: any, name: string) => [
formatValue(value, trajectory.metric),
name,
]}
/>
<Legend />
<Area
type="monotone"
dataKey="value"
stroke={color}
fill={color}
fillOpacity={0.3}
name={formatMetricName(trajectory.metric)}
/>
</AreaChart>
) : (
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="year" />
<YAxis
tickFormatter={(value) => formatValue(value, trajectory.metric)}
/>
<Tooltip
formatter={(value: any) => [
formatValue(value, trajectory.metric),
formatMetricName(trajectory.metric),
]}
/>
<Line
type="monotone"
dataKey="value"
stroke={color}
strokeWidth={2}
dot={{ fill: color, r: 4 }}
activeDot={{ r: 6 }}
/>
</LineChart>
)}
</ResponsiveContainer>
</CardContent>
</Card>
)
})}
</div>
)
}