diff --git a/backend/app/api/endpoints/sidebar.py b/backend/app/api/endpoints/sidebar.py index f3d5fdff..a473433b 100644 --- a/backend/app/api/endpoints/sidebar.py +++ b/backend/app/api/endpoints/sidebar.py @@ -65,16 +65,17 @@ async def get_sidebar_stats( ) active_count = active_result.scalar() or 0 - # --- Total session minutes today --- + # --- Completed session minutes today (active session time computed client-side) --- duration_expr = func.extract( "epoch", - func.coalesce(Session.completed_at, now_utc) - Session.started_at, + Session.completed_at - Session.started_at, ) / 60.0 duration_result = await db.execute( select(func.coalesce(func.sum(duration_expr), 0)).where( and_( user_filter, Session.started_at.isnot(None), + Session.completed_at.isnot(None), Session.started_at >= today_start_utc, ) ) diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 2baaf3d0..d32120f4 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -104,7 +104,8 @@ export function Sidebar() { s.started_at) ?? []} /> {/* Activity Feed */} diff --git a/frontend/src/components/sidebar/SidebarStatsBar.tsx b/frontend/src/components/sidebar/SidebarStatsBar.tsx index 411e2703..3fd0645b 100644 --- a/frontend/src/components/sidebar/SidebarStatsBar.tsx +++ b/frontend/src/components/sidebar/SidebarStatsBar.tsx @@ -1,7 +1,12 @@ +import { useEffect, useState } from 'react' + interface SidebarStatsBarProps { resolved: number active: number - sessionMinutes: number + /** Minutes from completed sessions today (server-computed) */ + completedMinutes: number + /** Start times of currently active sessions (ISO strings) */ + activeSessionStartTimes: string[] } function formatDuration(minutes: number): string { @@ -11,7 +16,28 @@ function formatDuration(minutes: number): string { return m > 0 ? `${h}h ${m}m` : `${h}h` } -export function SidebarStatsBar({ resolved, active, sessionMinutes }: SidebarStatsBarProps) { +function calcActiveMinutes(startTimes: string[]): number { + const now = Date.now() + return startTimes.reduce((sum, st) => { + const elapsed = Math.floor((now - new Date(st).getTime()) / 60000) + return sum + Math.max(0, elapsed) + }, 0) +} + +export function SidebarStatsBar({ resolved, active, completedMinutes, activeSessionStartTimes }: SidebarStatsBarProps) { + const [liveMinutes, setLiveMinutes] = useState(() => calcActiveMinutes(activeSessionStartTimes)) + + // Tick every 60s to keep the timer live + useEffect(() => { + setLiveMinutes(calcActiveMinutes(activeSessionStartTimes)) + const interval = setInterval(() => { + setLiveMinutes(calcActiveMinutes(activeSessionStartTimes)) + }, 60000) + return () => clearInterval(interval) + }, [activeSessionStartTimes]) + + const totalMinutes = completedMinutes + liveMinutes + return (
- {formatDuration(sessionMinutes)} + {formatDuration(totalMinutes)}
In Session