From 8a9416b7cc7640feeef0c8ee1a11232bc9a001d4 Mon Sep 17 00:00:00 2001 From: Brad Groux Date: Wed, 28 Jan 2026 08:14:55 -0600 Subject: [PATCH] feat(US-1305): Add status history to activity sidebar - Added daily summary card showing active/idle time and utilization % - Added Status History tab with today's status transitions - Display previous/new status with duration of each state - Mini progress bar for visual time breakdown - Backend was already complete (status-history-service logs all status changes) --- .../telemetry/events-2026-01-28.ndjson | 1 + server/.veritas-kanban/activity.json | 29 +++ web/src/components/layout/ActivitySidebar.tsx | 228 +++++++++++++++--- 3 files changed, 224 insertions(+), 34 deletions(-) diff --git a/.veritas-kanban/telemetry/events-2026-01-28.ndjson b/.veritas-kanban/telemetry/events-2026-01-28.ndjson index 261ed48c..0dd485a9 100644 --- a/.veritas-kanban/telemetry/events-2026-01-28.ndjson +++ b/.veritas-kanban/telemetry/events-2026-01-28.ndjson @@ -1107,3 +1107,4 @@ {"type":"task.created","taskId":"task_20260128_b2CW4g","project":"project-a","status":"todo","id":"evt_-g7kMhjBnCYG","timestamp":"2026-01-28T14:13:49.936Z"} {"type":"task.created","taskId":"task_20260128_NEm1K_","project":"project-b","status":"todo","id":"evt_aCXwbrsfHVkW","timestamp":"2026-01-28T14:13:49.937Z"} {"type":"task.created","taskId":"task_20260128_eCyXeF","status":"todo","id":"evt_qx8JflCKJ_1V","timestamp":"2026-01-28T14:13:49.944Z"} +{"type":"task.status_changed","taskId":"task_20260128_d47DtQ","project":"platform","status":"blocked","previousStatus":"todo","id":"evt_dcyHNA6aIlp5","timestamp":"2026-01-28T14:13:59.205Z"} diff --git a/server/.veritas-kanban/activity.json b/server/.veritas-kanban/activity.json index e662aa3f..ccdc99ba 100644 --- a/server/.veritas-kanban/activity.json +++ b/server/.veritas-kanban/activity.json @@ -1,4 +1,33 @@ [ + { + "id": "activity_1769609642656_9lea2p5ac", + "type": "comment_added", + "taskId": "task_20260128_V0MVwS", + "taskTitle": "US-1405: Dashboard metrics cards refresh", + "details": { + "author": "Veritas", + "preview": "Verified dashboard metrics cards are fully functio..." + }, + "timestamp": "2026-01-28T14:14:02.656Z" + }, + { + "id": "activity_1769609642614_cy9x653r2", + "type": "task_updated", + "taskId": "task_20260128_V0MVwS", + "taskTitle": "US-1405: Dashboard metrics cards refresh", + "timestamp": "2026-01-28T14:14:02.614Z" + }, + { + "id": "activity_1769609639205_sn6jggief", + "type": "status_changed", + "taskId": "task_20260128_d47DtQ", + "taskTitle": "FEATURE REQUEST: Host Kimi K2.5 Model In Azure", + "details": { + "from": "todo", + "status": "blocked" + }, + "timestamp": "2026-01-28T14:13:59.205Z" + }, { "id": "activity_1769609585007_vp6ouz8mc", "type": "comment_added", diff --git a/web/src/components/layout/ActivitySidebar.tsx b/web/src/components/layout/ActivitySidebar.tsx index 24b6fd8c..d7582a6e 100644 --- a/web/src/components/layout/ActivitySidebar.tsx +++ b/web/src/components/layout/ActivitySidebar.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { Activity, Trash2, RefreshCw, Clock, Coffee, ArrowRight, Zap } from 'lucide-react'; +import { Activity, Trash2, RefreshCw, Coffee, ArrowRight, Zap } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { ScrollArea } from '@/components/ui/scroll-area'; import { @@ -102,8 +102,147 @@ function ActivityRow({ activity }: { activity: ActivityItem }) { ); } +// Status Badge component +function StatusBadge({ status }: { status: string }) { + const colorClass = getStatusColor(status); + + return ( + + {status} + + ); +} + +// Daily Summary Card +function DailySummaryCard() { + const { data: summary, isLoading } = useDailySummary(); + + if (isLoading || !summary) { + return ( +
+
+
+
+
+
+
+ ); + } + + const total = summary.activeMs + summary.idleMs + summary.errorMs; + const activePercent = total > 0 ? Math.round((summary.activeMs / total) * 100) : 0; + + return ( +
+
Today's Summary
+
+
+ +
+
{formatDurationMs(summary.activeMs)}
+
Active
+
+
+
+ +
+
{formatDurationMs(summary.idleMs)}
+
Idle
+
+
+
+
+
{activePercent}%
+
Utilization
+
+
+
+ {/* Mini progress bar */} + {total > 0 && ( +
+
+
+ {summary.errorMs > 0 && ( +
+ )} +
+ )} +
+ ); +} + +// Status Transition Row +function StatusTransitionRow({ entry }: { entry: StatusHistoryEntry }) { + return ( +
+ + {new Date(entry.timestamp).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit' + })} + + + + + {entry.durationMs && ( + + {formatDurationMs(entry.durationMs)} + + )} +
+ ); +} + +// Status History List +function StatusHistoryList() { + const { data: history, isLoading } = useStatusHistory(50); + + // Filter to today's entries + const today = new Date().toISOString().split('T')[0]; + const todayEntries = history?.filter(entry => + entry.timestamp.startsWith(today) + ) || []; + + if (isLoading) { + return ( +
+ Loading status history... +
+ ); + } + + if (todayEntries.length === 0) { + return ( +
+ No status changes today +
+ ); + } + + return ( +
+ {todayEntries.map(entry => ( + + ))} +
+ ); +} + export function ActivitySidebar({ open, onOpenChange }: ActivitySidebarProps) { const [filter, setFilter] = useState('all'); + const [tab, setTab] = useState('tasks'); const { data: activities, isLoading, refetch, isRefetching } = useActivities(100); const clearActivities = useClearActivities(); @@ -146,43 +285,64 @@ export function ActivitySidebar({ open, onOpenChange }: ActivitySidebarProps) {
- - {activityTypes.length > 1 && ( - - )} - -
- {isLoading ? ( -
- Loading activities... -
- ) : filteredActivities.length === 0 ? ( -
- No activities yet -
- ) : ( -
- {filteredActivities.map(activity => ( - - ))} + {/* Daily Summary always visible at top */} + + + + + Task Activity + Status History + + + + {activityTypes.length > 1 && ( +
+
)} -
- + +
+ {isLoading ? ( +
+ Loading activities... +
+ ) : filteredActivities.length === 0 ? ( +
+ No activities yet +
+ ) : ( +
+ {filteredActivities.map(activity => ( + + ))} +
+ )} +
+
+ + + + +
+ +
+
+
+ );