wulf-pulse/components/dashboard/volume-trend.tsx
lorentz 8f955a0ff9 feat(07.1-05): user-tz on shared client components
- DetailModal: thread tz through resolveLabel(...) module helper +
  default export's 3 inline date/time calls.
- IntegrationStatusTabs: thread tz through fmtDate helper +
  VeeamTab sub-component prop.
- SyncScheduler: thread tz into closure-scoped formatDate helper.
- audit-log-table, user-table, user-sessions, active-sessions: inline
  toLocale calls in component body.
- analysis-view: useUserTimezone in AnalysisView; thread tz into 4
  toLocaleString calls.
- resolution-trend, volume-trend (recharts): module-scope fmtDate(iso)
  → fmtDate(iso, tz); useUserTimezone in named export; thread tz into
  axis tickFormatter + tooltip labelFormatter.
- ticket-detail-modal: thread tz into formatDate arrow inside
  TicketDetailModal.
- TimelineView: useUserTimezone; thread tz into 4 toLocale*String calls
  (hour/day/month/event-time formatters).
- ScoreCard: useUserTimezone in AggregateScoreCard; thread tz into the
  date-range latest call.
- addigy-tab: useUserTimezone in AddigyTab; thread tz into 2 inline calls.
- activity-sparkline: module-scope fmtHour(iso) → fmtHour(iso, tz);
  useUserTimezone in ActivitySparkline; update 3 callsites in title/aria.
- compliance-detail-table: thread tz from ComplianceDetailTable into
  ContractCoverageModal sub-component (2 inline date calls).
- company-backup-detail: module-scope formatDate(d) → formatDate(d, tz);
  useUserTimezone in CompanyBackupDetail; update 3 callsites.

Migrates 31 of 81 audit leak callsites.
2026-05-07 08:43:27 -04:00

80 lines
2.3 KiB
TypeScript

/* VolumeTrend — area chart of tickets opened per day for the last 30 days.
*
* Single series, Wulf Blue fill at 20%. No grid, minimal axes — the
* shape is what matters. Tooltip carries the exact count + date. */
'use client';
import {
Area,
AreaChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
interface VolumePoint {
date: string;
count: number;
}
interface VolumeTrendProps {
data: VolumePoint[];
height?: number;
}
function fmtDate(iso: string, tz: string) {
return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', timeZone: tz });
}
export function VolumeTrend({ data, height = 180 }: VolumeTrendProps) {
const tz = useUserTimezone();
return (
<ResponsiveContainer width="100%" height={height}>
<AreaChart data={data} margin={{ top: 4, right: 8, bottom: 4, left: 0 }}>
<defs>
<linearGradient id="volumeFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="var(--primary)" stopOpacity={0.35} />
<stop offset="100%" stopColor="var(--primary)" stopOpacity={0.02} />
</linearGradient>
</defs>
<XAxis
dataKey="date"
tickFormatter={(iso: string) => fmtDate(iso, tz)}
interval="preserveStartEnd"
minTickGap={48}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
axisLine={false}
tickLine={false}
/>
<YAxis
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
axisLine={false}
tickLine={false}
width={28}
allowDecimals={false}
/>
<Tooltip
contentStyle={{
background: 'var(--popover)',
border: '1px solid var(--border)',
borderRadius: 6,
fontSize: 12,
}}
labelFormatter={(value) => fmtDate(String(value), tz)}
formatter={(value) => [value ?? 0, 'opened']}
/>
<Area
type="monotone"
dataKey="count"
stroke="var(--primary)"
strokeWidth={1.5}
fill="url(#volumeFill)"
isAnimationActive={false}
/>
</AreaChart>
</ResponsiveContainer>
);
}