From c9bd0abf18c08cad86848579739b081843901cac Mon Sep 17 00:00:00 2001 From: Simon Franken Date: Mon, 9 Mar 2026 11:14:21 +0100 Subject: [PATCH] feat: include ongoing timer in today's tracked time on Dashboard The 'Today' stat card now adds the running timer's elapsed seconds to the total, so the displayed duration ticks up live alongside the timer widget. The timer is only counted when it started today (timers carried over from the previous day are excluded). A pulsing green indicator dot is shown on the stat card value while the timer is active, consistent with the balance widget treatment. The dot is implemented via a new optional 'indicator' prop on StatCard so it can be reused elsewhere without changing existing call sites. --- frontend/src/components/StatCard.tsx | 18 ++++++++++++++---- frontend/src/pages/DashboardPage.tsx | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/StatCard.tsx b/frontend/src/components/StatCard.tsx index b719705..3627e7b 100644 --- a/frontend/src/components/StatCard.tsx +++ b/frontend/src/components/StatCard.tsx @@ -3,25 +3,35 @@ interface StatCardProps { label: string; value: string; color: 'blue' | 'green' | 'purple' | 'orange'; + /** When true, renders a pulsing green dot to signal a live/active state. */ + indicator?: boolean; } -const colorClasses: Record = { +const colorClasses: Record, string> = { blue: 'bg-blue-50 text-blue-600', green: 'bg-green-50 text-green-600', purple: 'bg-purple-50 text-purple-600', orange: 'bg-orange-50 text-orange-600', }; -export function StatCard({ icon: Icon, label, value, color }: StatCardProps) { +export function StatCard({ icon: Icon, label, value, color, indicator }: StatCardProps) { return (
-
+

{label}

-

{value}

+
+

{value}

+ {indicator && ( + + )} +
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index 96d2c83..94de5e3 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -3,6 +3,7 @@ import { Link } from "react-router-dom"; import { Clock, Calendar, Briefcase, TrendingUp, Target, Edit2, Trash2 } from "lucide-react"; import { useTimeEntries } from "@/hooks/useTimeEntries"; import { useClientTargets } from "@/hooks/useClientTargets"; +import { useTimer } from "@/contexts/TimerContext"; import { ProjectColorDot } from "@/components/ProjectColorDot"; import { StatCard } from "@/components/StatCard"; import { TimeEntryFormModal } from "@/components/TimeEntryFormModal"; @@ -30,6 +31,7 @@ export function DashboardPage() { }); const { targets } = useClientTargets(); + const { ongoingTimer, elapsedSeconds } = useTimer(); const [isModalOpen, setIsModalOpen] = useState(false); const [editingEntry, setEditingEntry] = useState(null); @@ -54,10 +56,17 @@ export function DashboardPage() { } }; - const totalTodaySeconds = + const completedTodaySeconds = todayEntries?.entries.reduce((total, entry) => { return total + calculateDuration(entry.startTime, entry.endTime, entry.breakMinutes); - }, 0) || 0; + }, 0) ?? 0; + + // Only add the running timer if it started today (not a timer left running from yesterday) + const timerStartedToday = + ongoingTimer !== null && + new Date(ongoingTimer.startTime) >= startOfDay(today); + + const totalTodaySeconds = completedTodaySeconds + (timerStartedToday ? elapsedSeconds : 0); const targetsWithData = targets?.filter(t => t.periods.length > 0) ?? []; @@ -78,6 +87,7 @@ export function DashboardPage() { label="Today" value={formatDurationHoursMinutes(totalTodaySeconds)} color="blue" + indicator={timerStartedToday} />