- 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.
80 lines
2.3 KiB
TypeScript
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>
|
|
);
|
|
}
|