diff --git a/ui/src/components/EventTimeline.jsx b/ui/src/components/EventTimeline.jsx index 928a041..135d054 100644 --- a/ui/src/components/EventTimeline.jsx +++ b/ui/src/components/EventTimeline.jsx @@ -47,77 +47,61 @@ export default function EventTimeline() { return {state}; }; - const groupedEvents = useMemo(() => { - return { - CO: events.filter(e => e.camera_zone === 'CO'), - Mill: events.filter(e => e.camera_zone === 'Mill'), - Conveyor: events.filter(e => e.camera_zone === 'Conveyor') - }; + const chronologicalBlocks = useMemo(() => { + const blocks = {}; + events.forEach(ev => { + // Group by Minute (HH:MM) + const timeKey = ev.start_time.substring(0, 5); + 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 renderTimeline = (zone, zoneEvents, index) => ( -
( +
-

- {zone} Timeline -

-
- {zoneEvents.length === 0 ? ( -

No events recorded.

- ) : ( - zoneEvents.map((ev, i) => ( -
-
- - {ev.start_time.substring(0, 5)} - {ev.end_time.substring(0, 5)} - - {getStateBadge(ev.state)} -
-

{ev.event_description}

-
- Duration: {formatDuration(ev.state_duration_seconds)} - -
-
-
- Std Cycle: - N/A -
-
- Manpower: - Nil -
-
-
- )) - )} +
+ + {ev.start_time.substring(0, 5)} - {ev.end_time.substring(0, 5)} + + {getStateBadge(ev.state)} +
+

{ev.event_description}

+
+ Duration: {formatDuration(ev.state_duration_seconds)} + +
+
+
+ Std Cycle: + N/A +
+
+ Manpower: + Nil +
); @@ -137,13 +121,75 @@ export default function EventTimeline() { Back to Changeover Analytics

Event Timeline: {id}

-

Location-wise sequential breakdown of events

+

Synchronized Swimlane Timeline

-
- {renderTimeline('CO', groupedEvents.CO, 0)} - {renderTimeline('Mill', groupedEvents.Mill, 1)} - {renderTimeline('Conveyor', groupedEvents.Conveyor, 2)} +
+ + {/* Sticky Header Grid */} +
+

Time

+

CO Timeline

+

Mill Timeline

+

Conveyor Timeline

+
+ + {/* Scrollable Timeline Tracks Container */} +
+ {chronologicalBlocks.length === 0 ? ( +

No events recorded.

+ ) : ( + chronologicalBlocks.map((block, index) => ( +
+ + {/* Time Anchor Left Column */} +
+
+ {block.timeKey} +
+
+ + {/* Event Columns */} +
+ {block.CO.map((ev, i) => renderEventCard(ev, i, block.CO.length))} +
+
+ {block.Mill.map((ev, i) => renderEventCard(ev, i, block.Mill.length))} +
+
+ {block.Conveyor.map((ev, i) => renderEventCard(ev, i, block.Conveyor.length))} +
+ +
+ )) + )} +
+
);