smart-ai-traffic-intel / frontend /src /components /SignalRecommendationTable.jsx
MR.HABITH
Fix main.py indentation error
bd29ae3
import React from 'react';
import { motion } from 'framer-motion';
import { Brain, Trophy, Clock, AlertTriangle } from 'lucide-react';
const ROAD_LABELS = ['Road A', 'Road B', 'Road C', 'Road D'];
const MAX_SIG = 90, MIN_SIG = 15;
const calcSig = (count, max) => Math.max(MIN_SIG, Math.round((count / (max || 1)) * MAX_SIG));
const RANK_STYLES = [
{ badge: '#ef4444', badgeBg: 'rgba(239,68,68,0.15)', label: '1st Priority', barColor: '#dc2626' },
{ badge: '#f97316', badgeBg: 'rgba(249,115,22,0.15)', label: '2nd Priority', barColor: '#ea580c' },
{ badge: '#f59e0b', badgeBg: 'rgba(245,158,11,0.15)', label: '3rd Priority', barColor: '#d97706' },
{ badge: '#64748b', badgeBg: 'rgba(100,116,139,0.10)', label: '4th Priority', barColor: '#475569' },
];
const SignalRecommendationTable = ({ vehicleData = [] }) => {
const maxCount = Math.max(...vehicleData.map(d => d?.count || 0), 1);
const sorted = [...vehicleData]
.map((d, i) => ({
index: i, label: ROAD_LABELS[i] || `Road ${i+1}`,
count: d?.count || 0, signalTime: calcSig(d?.count || 0, maxCount),
}))
.sort((a, b) => b.count - a.count);
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, #d97706, #f59e0b)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<Brain size={16} color="#fff" />
</div>
<span style={{ fontSize: 11, fontWeight: 700, color: '#fcd34d', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
Step 4 — AI Signal Plan
</span>
</div>
<h2 className="font-display" style={{ fontSize: 26, fontWeight: 800, color: '#f0f6ff', letterSpacing: '-0.02em', marginBottom: 6 }}>
AI Signal Recommendation
</h2>
<p style={{ color: '#8da4c8', fontSize: 14 }}>
Proportional green-light allocation — busiest road gets up to {MAX_SIG}s, minimum {MIN_SIG}s guaranteed
</p>
</motion.div>
{/* Info Banner */}
<div style={{
display: 'flex', alignItems: 'flex-start', gap: 12,
padding: '12px 16px', borderRadius: 12, marginBottom: 24,
background: 'rgba(245,158,11,0.08)', border: '1px solid rgba(245,158,11,0.25)',
color: '#fcd34d', fontSize: 13,
}}>
<AlertTriangle size={16} style={{ flexShrink: 0, marginTop: 1 }} />
<span>
Signal time formula: <strong style={{ fontFamily: 'JetBrains Mono, monospace' }}>green_time = max({MIN_SIG}, round(count / max_count × {MAX_SIG}))</strong>.
Higher vehicle count = longer green light = cleared faster.
</span>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr', gap: 20 }} className="responsive-grid-2">
{/* Table Wrapper for horizontal scroll on mobile */}
<div style={{ overflowX: 'auto', margin: '0 -24px', padding: '0 24px' }}>
<div style={{ background: '#111d35', border: '1px solid #1e3057', borderRadius: 16, overflow: 'hidden', minWidth: 600 }}>
{/* Table Header */}
<div style={{
display: 'grid', gridTemplateColumns: '1.2fr 0.8fr 1fr 1fr 1fr',
padding: '12px 20px', background: '#0b1426',
borderBottom: '1px solid #1e3057',
fontSize: 10, fontWeight: 700, color: '#4a6080',
textTransform: 'uppercase', letterSpacing: '0.1em',
}}>
<span>Road</span>
<span style={{ textAlign: 'center' }}>Vehicles</span>
<span style={{ textAlign: 'center' }}>Priority</span>
<span style={{ textAlign: 'center' }}>Signal</span>
<span style={{ textAlign: 'center' }}>Allocation</span>
</div>
{sorted.map((road, rank) => {
const rs = RANK_STYLES[rank] || RANK_STYLES[3];
const pct = (road.signalTime / MAX_SIG) * 100;
return (
<motion.div
key={road.index}
initial={{ opacity: 0, x: -12 }} animate={{ opacity: 1, x: 0 }}
transition={{ delay: rank * 0.08 }}
style={{
display: 'grid', gridTemplateColumns: '1.2fr 0.8fr 1fr 1fr 1fr',
padding: '14px 20px',
background: rank === 0 ? 'rgba(239,68,68,0.04)' : 'transparent',
borderBottom: '1px solid #1e3057',
alignItems: 'center',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{rank === 0 && <Trophy size={14} color="#fbbf24" />}
<span style={{ fontSize: 13, fontWeight: 700, color: '#f0f6ff' }}>{road.label}</span>
</div>
<div style={{ textAlign: 'center', fontFamily: 'JetBrains Mono, monospace', fontSize: 14, fontWeight: 700, color: '#cbd5e1' }}>
{road.count}
</div>
<div style={{ textAlign: 'center' }}>
<span style={{
padding: '4px 10px', borderRadius: 8, fontSize: 10, fontWeight: 700,
background: rs.badgeBg, color: rs.badge, border: `1px solid ${rs.badge}40`,
}}>
{rs.label}
</span>
</div>
<div style={{ textAlign: 'center' }}>
<div style={{
display: 'inline-flex', alignItems: 'center', gap: 5,
padding: '6px 12px', borderRadius: 8,
background: '#0b1426', border: '1px solid #1e3057',
}}>
<Clock size={12} color="#22d3ee" />
<span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 14, fontWeight: 800, color: '#22d3ee' }}>
{road.signalTime}s
</span>
</div>
</div>
<div style={{ paddingLeft: 8 }}>
<div style={{ height: 8, borderRadius: 4, background: '#0b1426', overflow: 'hidden' }}>
<motion.div
initial={{ width: 0 }} animate={{ width: `${pct}%` }}
transition={{ duration: 1.2, ease: 'easeOut', delay: rank * 0.12 }}
style={{ height: '100%', borderRadius: 4, background: rs.barColor }}
/>
</div>
<div style={{ fontSize: 9, color: '#4a6080', marginTop: 3, fontFamily: 'JetBrains Mono, monospace', textAlign: 'right' }}>
{pct.toFixed(0)}%
</div>
</div>
</motion.div>
);
})}
</div>
</div>
{/* Signal Visual Cards */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{sorted.map((road, rank) => {
const rs = RANK_STYLES[rank] || RANK_STYLES[3];
return (
<motion.div
key={road.index}
initial={{ opacity: 0, scale: 0.92 }} animate={{ opacity: 1, scale: 1 }}
transition={{ delay: rank * 0.1 }}
style={{
background: rank === 0 ? 'rgba(239,68,68,0.06)' : '#111d35',
border: `1px solid ${rank === 0 ? 'rgba(239,68,68,0.25)' : '#1e3057'}`,
borderRadius: 14, padding: '14px 18px',
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{
width: 32, height: 32, borderRadius: 8, flexShrink: 0,
background: rs.badgeBg, border: `1px solid ${rs.badge}40`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 11, fontWeight: 900, color: rs.badge,
fontFamily: 'JetBrains Mono, monospace',
}}>#{rank+1}</div>
<div>
<div style={{ fontSize: 13, fontWeight: 700, color: '#f0f6ff' }}>{road.label}</div>
<div style={{ fontSize: 10, color: '#4a6080' }}>{road.count} vehicles</div>
</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{
fontSize: 28, fontWeight: 900, fontFamily: 'JetBrains Mono, monospace',
color: rs.badge, lineHeight: 1,
}}>{road.signalTime}s</div>
<div style={{ fontSize: 10, color: '#4a6080' }}>green time</div>
</div>
</motion.div>
);
})}
</div>
</div>
</section>
);
};
export default SignalRecommendationTable;