diff --git a/web/src/app/(main)/function/schedules/page.tsx b/web/src/app/(main)/function/schedules/page.tsx index 2d8c12e..6b9a3ce 100644 --- a/web/src/app/(main)/function/schedules/page.tsx +++ b/web/src/app/(main)/function/schedules/page.tsx @@ -162,14 +162,23 @@ function historyActionText(action: string) { ); } -function timelinePosition(event: ScheduleHistoryEntry, index: number, events: ScheduleHistoryEntry[]) { - if (events.length <= 1) return 50; - const timestamps = events.map((item) => Date.parse(item.created_at)).filter(Number.isFinite); - const time = Date.parse(event.created_at); - const min = Math.min(...timestamps); - const max = Math.max(...timestamps); - if (!Number.isFinite(time) || !Number.isFinite(min) || max <= min) return (index / (events.length - 1)) * 100; - return ((time - min) / (max - min)) * 100; +function historyEventClass(event: ScheduleHistoryEntry) { + if (!event.success) + return "border-red-200 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300"; + if (event.action === "running" || event.action === "resume") { + return "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-300"; + } + if (event.action === "outside" || event.action === "pause" || event.action === "paused") { + return "border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-300"; + } + return "border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-900/60 dark:text-slate-300"; +} + +function historyDotClass(event: ScheduleHistoryEntry) { + if (!event.success) return "bg-red-500"; + if (event.action === "running" || event.action === "resume") return "bg-emerald-500"; + if (event.action === "outside" || event.action === "pause" || event.action === "paused") return "bg-amber-500"; + return "bg-slate-400"; } function ScheduleTimeline({ schedule, loading }: { schedule?: TaskSchedule; loading: boolean }) { @@ -181,38 +190,42 @@ function ScheduleTimeline({ schedule, loading }: { schedule?: TaskSchedule; load ); } - const events = [...(schedule?.history ?? [])].reverse(); + // The API returns newest first. Keep that order so the latest transition is + // visible immediately when a row is expanded. + const events = schedule?.history ?? []; if (!events.length) { return