feat: show live estimated hours/earnings while timer is running

Summary cards now include the in-progress session's hours and earnings,
rounded up to the nearest 15-min increment (matching clock-out billing
logic). A secondary "+Xh est." line appears below each stat when a timer
is active, updating every second as the elapsed counter ticks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 18:54:05 -04:00
parent 2b4608bfb4
commit 9762ede7ec
+18 -2
View File
@@ -209,6 +209,12 @@ export default function TimeClockPage() {
const completedEntries = entries.filter((e) => e.endedAt !== null); const completedEntries = entries.filter((e) => e.endedAt !== null);
// Live estimate for running timer, rounded up to 15-min increments (no minimum while running)
const estimatedHours = running ? Math.ceil(elapsed / 900) * 0.25 : 0;
const estimatedEarnings = running ? estimatedHours * (running.rate ?? 0) : 0;
const displayHours = (summary?.totalHours ?? 0) + estimatedHours;
const displayEarnings = (summary?.totalEarnings ?? 0) + estimatedEarnings;
return ( return (
<div className="page-enter space-y-6 pb-6"> <div className="page-enter space-y-6 pb-6">
<PageHeader <PageHeader
@@ -236,8 +242,13 @@ export default function TimeClockPage() {
Total Hours Total Hours
</p> </p>
<p className="mt-1 text-2xl font-bold"> <p className="mt-1 text-2xl font-bold">
{formatDuration(summary?.totalHours)} {formatDuration(displayHours || undefined)}
</p> </p>
{running && estimatedHours > 0 && (
<p className="text-muted-foreground mt-0.5 text-xs">
+{formatDuration(estimatedHours)} est.
</p>
)}
</CardContent> </CardContent>
</Card> </Card>
<Card> <Card>
@@ -246,8 +257,13 @@ export default function TimeClockPage() {
Earnings Earnings
</p> </p>
<p className="text-primary mt-1 text-2xl font-bold"> <p className="text-primary mt-1 text-2xl font-bold">
{formatCurrency(summary?.totalEarnings ?? 0)} {formatCurrency(displayEarnings)}
</p> </p>
{running && estimatedEarnings > 0 && (
<p className="text-muted-foreground mt-0.5 text-xs">
+{formatCurrency(estimatedEarnings)} est.
</p>
)}
</CardContent> </CardContent>
</Card> </Card>
<Card className="col-span-2 sm:col-span-1"> <Card className="col-span-2 sm:col-span-1">