fix(08-02): coerce Postgres numeric hours_worked to Number before toFixed

Postgres returns NUMERIC columns as strings via pg, so calling
.toFixed(1) on time_entries.hours_worked from /api/engagement/user/[userId]
threw at runtime. The period-level hours in the same response are already
parseFloat'd; the recentEntries and matchedEntries arrays pass rows through
verbatim, so wrap with Number() at render.
This commit is contained in:
lorentz 2026-05-07 22:02:52 -04:00
parent b3ad92cd86
commit 81079ad89f
2 changed files with 2 additions and 2 deletions

View file

@ -71,7 +71,7 @@ export function EngagementRecentEntries({ entries }: EngagementRecentEntriesProp
<span className="flex-1 min-w-0 flex items-baseline gap-2">
<span className="text-sm font-medium tabular-nums shrink-0">{dateLabel}</span>
<span className="text-sm text-muted-foreground tabular-nums shrink-0">
{entry.hours_worked.toFixed(1)}h
{Number(entry.hours_worked).toFixed(1)}h
</span>
{isBillable && (
<Badge variant="secondary" className="shrink-0">Billable</Badge>

View file

@ -109,7 +109,7 @@ export function EngagementRecentMeetings({ meetings }: EngagementRecentMeetingsP
<ul className="space-y-1">
{meeting.matchedEntries.map((te, j) => (
<li key={j} className="flex flex-wrap gap-x-2">
<span className="text-foreground tabular-nums">{te.hours_worked.toFixed(1)}h</span>
<span className="text-foreground tabular-nums">{Number(te.hours_worked).toFixed(1)}h</span>
{te.company_name && <span>· {te.company_name}</span>}
{te.notes && <span className="truncate">· {te.notes.split('\n')[0]?.slice(0, 80) ?? ''}</span>}
</li>