'use client' import { useEffect, useState } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { ArrowRight, ArrowLeft, Accessibility, Train } from 'lucide-react' interface Arrival { route: string minutes: number park: string wheelchair: boolean } interface Direction { stopId: string title: string subtitle: string icon: 'right' | 'left' accent: string glow: string } const DIRECTIONS: Direction[] = [ { stopId: '16226', title: 'В центр', subtitle: 'к м. Новочеркасская', icon: 'right', accent: 'linear-gradient(135deg, rgba(16,185,129,0.14), rgba(6,182,212,0.06))', glow: 'rgba(16,185,129,0.35)', }, { stopId: '16354', title: 'Из центра', subtitle: 'к пр. Большевиков', icon: 'left', accent: 'linear-gradient(135deg, rgba(236,72,153,0.14), rgba(245,158,11,0.06))', glow: 'rgba(236,72,153,0.35)', }, ] const ALLOWED_ROUTES = new Set(['23', '27', '39']) const ROUTE_STYLE: Record = { '23': { color: '#60a5fa', bg: 'linear-gradient(135deg, #3b82f6, #2563eb)' }, '27': { color: '#fbbf24', bg: 'linear-gradient(135deg, #f59e0b, #d97706)' }, '39': { color: '#c084fc', bg: 'linear-gradient(135deg, #a855f7, #7c3aed)' }, } function formatMinutes(m: number): { big: string; unit: string } { if (m <= 0) return { big: 'сейчас', unit: '' } if (m === 1) return { big: '1', unit: 'мин' } return { big: String(m), unit: 'мин' } } function DirectionCard({ dir, arrivals, loading }: { dir: Direction; arrivals: Arrival[]; loading: boolean }) { const sorted = [...arrivals].sort((a, b) => a.minutes - b.minutes).slice(0, 3) const ArrowIcon = dir.icon === 'right' ? ArrowRight : ArrowLeft return (
{/* Glow */}
{/* Header */}
{dir.title}
{dir.subtitle}
{/* Arrivals */}
{loading && arrivals.length === 0 ? (
Загрузка...
) : sorted.length === 0 ? (
Пока нет данных
) : ( {sorted.map((arr, idx) => { const style = ROUTE_STYLE[arr.route] || { color: '#9ca3af', bg: 'linear-gradient(135deg, #6b7280, #4b5563)' } const time = formatMinutes(arr.minutes) const isImminent = arr.minutes <= 2 return ( {/* Route badge */}
{arr.route}
{/* Label */}
Трамвай
№{arr.park}
{/* Time */}
{time.big}
{time.unit && (
{time.unit}
)}
{/* Wheelchair */} {arr.wheelchair && ( )}
) })}
)}
) } export default function TransportWidget() { const [data, setData] = useState>({}) const [loading, setLoading] = useState(true) useEffect(() => { let cancelled = false const load = async () => { try { const results = await Promise.all( DIRECTIONS.map(d => fetch(`/api/transport?stopId=${d.stopId}`) .then(r => r.json()) .then(j => ({ stopId: d.stopId, arrivals: (j.arrivals as Arrival[]) || [] })) .catch(() => ({ stopId: d.stopId, arrivals: [] as Arrival[] })) ) ) if (cancelled) return const map: Record = {} for (const r of results) { map[r.stopId] = r.arrivals.filter(a => ALLOWED_ROUTES.has(a.route)) } setData(map) } finally { if (!cancelled) setLoading(false) } } load() const t = setInterval(load, 30_000) return () => { cancelled = true; clearInterval(t) } }, []) return (
{DIRECTIONS.map(d => ( ))}
) }