smart-ai-traffic-intel / frontend /src /components /TrafficAnalysisPanel.jsx
MR.HABITH
Fix main.py indentation error
bd29ae3
import React from 'react';
import { motion } from 'framer-motion';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Cell } from 'recharts';
import { BarChart2, TrendingUp, TrendingDown, Activity, AlertTriangle } from 'lucide-react';
const ROAD_LABELS = ['Road A', 'Road B', 'Road C', 'Road D'];
const BAR_COLORS = ['#3b82f6', '#06b6d4', '#8b5cf6', '#f59e0b'];
const getCongestion = (pct) => {
if (pct <= 20) return { label: 'Low', barClass: 'bar-green', color: '#4ade80' };
if (pct <= 45) return { label: 'Medium', barClass: 'bar-amber', color: '#fbbf24' };
if (pct <= 70) return { label: 'High', barClass: 'bar-orange', color: '#fb923c' };
return { label: 'Critical', barClass: 'bar-red', color: '#f87171' };
};
const CustomTooltip = ({ active, payload }) => {
if (!active || !payload?.length) return null;
return (
<div style={{
background: '#0b1426', border: '1px solid #2a4a8a',
borderRadius: 10, padding: '10px 16px',
boxShadow: '0 8px 32px rgba(0,0,0,0.6)'
}}>
<p style={{ color: '#f0f6ff', fontWeight: 700, fontSize: 13, marginBottom: 3 }}>{payload[0].payload.name}</p>
<p style={{ color: '#60a5fa', fontFamily: 'JetBrains Mono, monospace', fontSize: 12 }}>
{payload[0].value} vehicles
</p>
</div>
);
};
const StatCard = ({ title, value, Icon, color, sub }) => (
<motion.div
initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }}
style={{
background: '#111d35', border: '1px solid #1e3057', borderRadius: 14,
padding: '16px 20px', display: 'flex', alignItems: 'center', gap: 14,
}}
>
<div style={{
width: 44, height: 44, borderRadius: 12, flexShrink: 0,
background: `${color}22`, border: `1px solid ${color}40`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<Icon size={20} color={color} />
</div>
<div>
<div style={{ fontSize: 10, color: '#4a6080', textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 600 }}>{title}</div>
<div style={{ fontSize: 28, fontWeight: 900, fontFamily: 'JetBrains Mono, monospace', color: '#f0f6ff', lineHeight: 1.1 }}>{value}</div>
{sub && <div style={{ fontSize: 10, color: '#4a6080', marginTop: 2 }}>{sub}</div>}
</div>
</motion.div>
);
const TrafficAnalysisPanel = ({ vehicleData = [] }) => {
const counts = vehicleData.map(d => d?.count || 0);
const total = counts.reduce((a, b) => a + b, 0);
const avg = vehicleData.length > 0 ? (total / vehicleData.length).toFixed(1) : 0;
const peak = Math.max(...counts, 0);
const low = vehicleData.length > 0 ? Math.min(...counts) : 0;
const maxC = peak || 1;
const ranked = [...vehicleData]
.map((d, i) => ({ label: ROAD_LABELS[i] || `Road ${i+1}`, count: d?.count || 0, index: i }))
.sort((a, b) => b.count - a.count);
const chartData = vehicleData.map((d, i) => ({ name: ROAD_LABELS[i] || `Road ${i+1}`, count: d?.count || 0 }));
return (
<section style={{ padding: '0 24px 40px', maxWidth: 1400, margin: '0 auto' }}>
{/* Header */}
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} style={{ marginBottom: 28 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
<div style={{
width: 32, height: 32, borderRadius: 10,
background: 'linear-gradient(135deg, #7c3aed, #6d28d9)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<BarChart2 size={16} color="#fff" />
</div>
<span style={{ fontSize: 11, fontWeight: 700, color: '#a78bfa', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
Step 3 — Analysis
</span>
</div>
<h2 className="font-display" style={{ fontSize: 26, fontWeight: 800, color: '#f0f6ff', letterSpacing: '-0.02em', marginBottom: 6 }}>
Traffic Analysis
</h2>
<p style={{ color: '#8da4c8', fontSize: 14 }}>
Vehicle density scoring, congestion levels, and road priority ranking
</p>
</motion.div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1.2fr', gap: 20 }} className="responsive-grid-2">
{/* LEFT COLUMN */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{/* Stat Cards */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<StatCard title="Total Vehicles" value={total} Icon={Activity} color="#3b82f6" sub="all roads combined" />
<StatCard title="Avg per Road" value={avg} Icon={TrendingUp} color="#06b6d4" sub="vehicles / road" />
<StatCard title="Peak Traffic" value={peak} Icon={AlertTriangle} color="#ef4444" sub="busiest road" />
<StatCard title="Lowest" value={low} Icon={TrendingDown} color="#22c55e" sub="clearest road" />
</div>
{/* Congestion Bars */}
<div style={{ background: '#111d35', border: '1px solid #1e3057', borderRadius: 16, padding: 20 }}>
<h3 style={{ fontSize: 13, fontWeight: 700, color: '#f0f6ff', marginBottom: 16 }}>Road Congestion Density</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{vehicleData.map((d, i) => {
const count = d?.count || 0;
const pct = (count / maxC) * 100;
const cong = getCongestion(pct);
return (
<div key={i}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
<span style={{ fontSize: 12, fontWeight: 600, color: '#8da4c8' }}>{ROAD_LABELS[i]}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ color: cong.color, fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 6, background: `${cong.color}18` }}>{cong.label}</span>
<span style={{ color: '#4a6080', fontSize: 11, fontFamily: 'JetBrains Mono, monospace' }}>{count}</span>
</div>
</div>
<div style={{ height: 10, borderRadius: 6, background: '#0b1426', overflow: 'hidden' }}>
<motion.div
initial={{ width: 0 }} animate={{ width: `${pct}%` }}
transition={{ duration: 1.2, ease: 'easeOut', delay: i * 0.12 }}
className={cong.barClass}
style={{ height: '100%', borderRadius: 6 }}
/>
</div>
</div>
);
})}
</div>
</div>
{/* Priority Ranking */}
<div style={{ background: '#111d35', border: '1px solid #1e3057', borderRadius: 16, padding: 20 }}>
<h3 style={{ fontSize: 13, fontWeight: 700, color: '#f0f6ff', marginBottom: 14 }}>Priority Ranking</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{ranked.map((road, rank) => {
const rankColors = ['#ef4444', '#f97316', '#f59e0b', '#64748b'];
return (
<div key={road.index} style={{
display: 'flex', alignItems: 'center', gap: 12,
padding: '10px 14px', borderRadius: 10,
background: rank === 0 ? 'rgba(239,68,68,0.07)' : '#0b1426',
border: `1px solid ${rank === 0 ? 'rgba(239,68,68,0.2)' : '#1e3057'}`,
}}>
<div style={{
width: 28, height: 28, borderRadius: 8, flexShrink: 0,
background: `${rankColors[rank]}22`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 11, fontWeight: 900, color: rankColors[rank],
fontFamily: 'JetBrains Mono, monospace',
}}>#{rank + 1}</div>
<span style={{ flex: 1, fontSize: 13, fontWeight: 600, color: '#f0f6ff' }}>{road.label}</span>
<span style={{ fontSize: 13, fontWeight: 700, fontFamily: 'JetBrains Mono, monospace', color: rankColors[rank] }}>{road.count}</span>
</div>
);
})}
</div>
</div>
</div>
{/* RIGHT — Bar Chart */}
<div style={{ background: '#111d35', border: '1px solid #1e3057', borderRadius: 16, padding: 24 }}>
<h3 style={{ fontSize: 13, fontWeight: 700, color: '#f0f6ff', marginBottom: 20 }}>Vehicle Count by Road</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={chartData} margin={{ top: 8, right: 8, left: -10, bottom: 8 }}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(30,48,87,0.8)" />
<XAxis dataKey="name" tick={{ fill: '#4a6080', fontSize: 12 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: '#4a6080', fontSize: 12 }} axisLine={false} tickLine={false} />
<Tooltip content={<CustomTooltip />} cursor={{ fill: 'rgba(59,130,246,0.06)' }} />
<Bar dataKey="count" radius={[8, 8, 0, 0]} maxBarSize={64}>
{chartData.map((_, i) => <Cell key={i} fill={BAR_COLORS[i % BAR_COLORS.length]} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
{/* Legend */}
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 16, paddingTop: 16, borderTop: '1px solid #1e3057' }}>
{chartData.map((d, i) => (
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<div style={{ width: 10, height: 10, borderRadius: 3, background: BAR_COLORS[i] }} />
<span style={{ fontSize: 11, color: '#8da4c8' }}>
{d.name}: <span style={{ color: '#f0f6ff', fontWeight: 700, fontFamily: 'JetBrains Mono, monospace' }}>{d.count}</span>
</span>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default TrafficAnalysisPanel;