import React, { useState, useEffect, useMemo } from 'react'; import { useParams, Link } from 'react-router-dom'; import { ArrowLeft, Video } from 'lucide-react'; export default function EventTimeline() { const { id } = useParams(); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const API_BASE = '/api'; useEffect(() => { async function fetchData() { try { const query = id ? `?changeover_id=${id}` : ''; const res = await fetch(`${API_BASE}/events${query}`); if (res.ok) { setEvents(await res.json()); } } catch (err) { console.error("Error fetching events", err); } finally { setLoading(false); } } fetchData(); }, [id]); const formatDuration = (seconds) => { if (!seconds) return '0s'; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = seconds % 60; let res = []; if (h > 0) res.push(`${h}h`); if (m > 0) res.push(`${m}m`); if (s > 0) res.push(`${s}s`); return res.join(' ') || '0s'; }; const getStateBadge = (state) => { const s = state?.toLowerCase() || ''; if (s === 'active') return Active; if (s === 'waste') return Waste; if (s === 'idle') return Idle; if (s === 'load waiting') return Load Wait; return {state}; }; const chronologicalBlocks = useMemo(() => { const blocks = {}; events.forEach(ev => { // Group by exact time (HH:MM:SS) for a waterfall layout const timeKey = ev.start_time.substring(0, 8); if (!blocks[timeKey]) { blocks[timeKey] = { timeKey, CO: [], Mill: [], Conveyor: [] }; } if (ev.camera_zone === 'CO') blocks[timeKey].CO.push(ev); if (ev.camera_zone === 'Mill') blocks[timeKey].Mill.push(ev); if (ev.camera_zone === 'Conveyor') blocks[timeKey].Conveyor.push(ev); }); // Sort chronologically return Object.values(blocks).sort((a, b) => a.timeKey.localeCompare(b.timeKey)); }, [events]); const renderEventCard = (ev, i, arrLength) => (
{ev.event_description}
Synchronized Swimlane Timeline
No events recorded.
) : ( chronologicalBlocks.map((block, index) => (