feat(07.1-05): user-tz on engagement overview + profile pages

- app/engagement/page.tsx: useUserTimezone in EngagementPage; thread tz
  into 7 toLocale* callsites (lines 844, 1012, 1108, 1227, 1255 — last
  two have 2 calls per line for date+time).
- app/engagement/profile/page.tsx: useUserTimezone in EngagementProfilePage;
  add tz prop to ActivityHeatmap; convert module-scope monthLabel(m) to
  monthLabel(m, tz); update 2 callsites of monthLabel.

Migrates 9 of 81 audit leak callsites.
This commit is contained in:
lorentz 2026-05-07 08:23:24 -04:00
parent b417988ee6
commit a709144685
2 changed files with 16 additions and 12 deletions

View file

@ -44,6 +44,7 @@ import {
Legend,
CartesianGrid,
} from 'recharts';
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
interface SummaryData {
totalStaff: number;
@ -460,6 +461,7 @@ function SummaryCard({
}
export default function EngagementPage() {
const tz = useUserTimezone();
const [period, setPeriod] = useState<'D7' | 'D30' | 'D90'>('D7');
const [activeTab, setActiveTab] = useState<'overview' | 'by-employee'>('by-employee');
const [summary, setSummary] = useState<SummaryData | null>(null);
@ -839,7 +841,7 @@ export default function EngagementPage() {
</TableCell>
<TableCell className="text-right text-xs text-muted-foreground hidden xl:table-cell">
{user.lastActivity
? new Date(user.lastActivity).toLocaleDateString()
? new Date(user.lastActivity).toLocaleDateString(undefined, { timeZone: tz })
: '—'}
</TableCell>
<TableCell className="text-right text-sm tabular-nums hidden xl:table-cell">
@ -1007,7 +1009,7 @@ export default function EngagementPage() {
}] : []),
]} />
{snap?.last_activity_date && (
<p className="text-xs text-muted-foreground mt-3">Last activity: {new Date(snap.last_activity_date).toLocaleDateString()}</p>
<p className="text-xs text-muted-foreground mt-3">Last activity: {new Date(snap.last_activity_date).toLocaleDateString(undefined, { timeZone: tz })}</p>
)}
</div>
@ -1103,7 +1105,7 @@ export default function EngagementPage() {
)}
</div>
<span className="text-xs text-muted-foreground shrink-0">
{d.toLocaleDateString()} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{d.toLocaleDateString(undefined, { timeZone: tz })} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZone: tz })}
{mtg.durationMinutes != null && ` · ${mtg.durationMinutes}m`}
</span>
</div>
@ -1222,7 +1224,7 @@ export default function EngagementPage() {
</div>
<div className="flex items-center gap-2 shrink-0 ml-2 text-xs text-muted-foreground">
<span>{formatDuration(call.durationSeconds)}</span>
<span>{new Date(call.startTime).toLocaleDateString()}</span>
<span>{new Date(call.startTime).toLocaleDateString(undefined, { timeZone: tz })}</span>
</div>
</div>
))}
@ -1250,7 +1252,7 @@ export default function EngagementPage() {
)}
</div>
<span className="text-xs text-muted-foreground shrink-0">
{d.toLocaleDateString()} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{d.toLocaleDateString(undefined, { timeZone: tz })} {d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZone: tz })}
{mtg.durationMinutes != null && ` · ${mtg.durationMinutes}m`}
</span>
</div>

View file

@ -25,6 +25,7 @@ import {
PolarRadiusAxis,
Radar,
} from 'recharts';
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
import { Users, RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
@ -100,7 +101,7 @@ function hoursLevel(h: number): number {
return 4;
}
function ActivityHeatmap({ daily }: { daily: DayData[] }) {
function ActivityHeatmap({ daily, tz }: { daily: DayData[]; tz: string }) {
const dailyMap: Record<string, DayData> = {};
for (const d of daily) dailyMap[d.date] = d;
@ -138,7 +139,7 @@ function ActivityHeatmap({ daily }: { daily: DayData[] }) {
if (m !== lastMonth) {
monthLabels.push({
weekIndex: wi,
label: d.toLocaleDateString('en-US', { month: 'short' }),
label: d.toLocaleDateString('en-US', { month: 'short', timeZone: tz }),
});
lastMonth = m;
}
@ -260,8 +261,8 @@ function buildRadarData(monthly: MonthData[]) {
];
}
function monthLabel(m: string) {
return new Date(m + '-02').toLocaleDateString('en-US', { month: 'short', year: 'numeric' });
function monthLabel(m: string, tz: string) {
return new Date(m + '-02').toLocaleDateString('en-US', { month: 'short', year: 'numeric', timeZone: tz });
}
interface BackfillStatus {
@ -276,6 +277,7 @@ interface BackfillStatus {
}
export default function EngagementProfilePage() {
const tz = useUserTimezone();
const [users, setUsers] = useState<UserOption[]>([]);
const [usersLoading, setUsersLoading] = useState(true);
const [selectedUserId, setSelectedUserId] = useState<string>('');
@ -496,7 +498,7 @@ export default function EngagementProfilePage() {
{peakMonth ? peakMonth.hoursWorked.toFixed(0) + 'h' : '—'}
</p>
<p className="text-xs text-muted-foreground">
{peakMonth ? monthLabel(peakMonth.month) : ''}
{peakMonth ? monthLabel(peakMonth.month, tz) : ''}
</p>
</CardContent>
</Card>
@ -510,7 +512,7 @@ export default function EngagementProfilePage() {
</CardHeader>
<CardContent>
{history.daily.length > 0 ? (
<ActivityHeatmap daily={history.daily} />
<ActivityHeatmap daily={history.daily} tz={tz} />
) : (
<p className="text-sm text-muted-foreground py-6 text-center">
No time entry data available
@ -612,7 +614,7 @@ export default function EngagementProfilePage() {
key={m.month}
className={cn(isEmpty && 'opacity-40')}
>
<TableCell className="font-medium">{monthLabel(m.month)}</TableCell>
<TableCell className="font-medium">{monthLabel(m.month, tz)}</TableCell>
<TableCell className="text-right num">
{m.hoursWorked > 0 ? m.hoursWorked.toFixed(1) : '—'}
</TableCell>