- Expires: {new Date(device['Warranty Expiration Date']).toLocaleDateString()}
+ Expires: {new Date(device['Warranty Expiration Date']).toLocaleDateString(undefined, { timeZone: tz })}
{device['Warranty Days Left'] !== undefined && (
({device['Warranty Days Left']} days left)
)}
diff --git a/components/dashboard/resolution-trend.tsx b/components/dashboard/resolution-trend.tsx
index 5dc7876..a231573 100644
--- a/components/dashboard/resolution-trend.tsx
+++ b/components/dashboard/resolution-trend.tsx
@@ -12,6 +12,7 @@ import {
XAxis,
YAxis,
} from 'recharts';
+import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
interface ResolutionPoint {
date: string;
@@ -23,18 +24,19 @@ interface ResolutionTrendProps {
height?: number;
}
-function fmtDate(iso: string) {
- return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
+function fmtDate(iso: string, tz: string) {
+ return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', timeZone: tz });
}
export function ResolutionTrend({ data, height = 180 }: ResolutionTrendProps) {
+ const tz = useUserTimezone();
return (
fmtDate(iso, tz)}
interval="preserveStartEnd"
minTickGap={48}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
@@ -55,7 +57,7 @@ export function ResolutionTrend({ data, height = 180 }: ResolutionTrendProps) {
borderRadius: 6,
fontSize: 12,
}}
- labelFormatter={(value) => fmtDate(String(value))}
+ labelFormatter={(value) => fmtDate(String(value), tz)}
formatter={(value) =>
value == null ? ['—', 'avg'] : [`${Number(value).toFixed(1)} h`, 'avg']
}
diff --git a/components/dashboard/volume-trend.tsx b/components/dashboard/volume-trend.tsx
index 5b87a74..51c5b05 100644
--- a/components/dashboard/volume-trend.tsx
+++ b/components/dashboard/volume-trend.tsx
@@ -13,6 +13,7 @@ import {
XAxis,
YAxis,
} from 'recharts';
+import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
interface VolumePoint {
date: string;
@@ -24,11 +25,12 @@ interface VolumeTrendProps {
height?: number;
}
-function fmtDate(iso: string) {
- return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
+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 (
@@ -40,7 +42,7 @@ export function VolumeTrend({ data, height = 180 }: VolumeTrendProps) {
fmtDate(iso, tz)}
interval="preserveStartEnd"
minTickGap={48}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
@@ -61,7 +63,7 @@ export function VolumeTrend({ data, height = 180 }: VolumeTrendProps) {
borderRadius: 6,
fontSize: 12,
}}
- labelFormatter={(value) => fmtDate(String(value))}
+ labelFormatter={(value) => fmtDate(String(value), tz)}
formatter={(value) => [value ?? 0, 'opened']}
/>
(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
@@ -230,7 +232,7 @@ export function TicketDetailModal({ ticketNumber, open, onOpenChange }: TicketDe
if (!dateString) return 'N/A';
return new Date(dateString).toLocaleString('en-US', {
month: 'short', day: 'numeric', year: 'numeric',
- hour: '2-digit', minute: '2-digit',
+ hour: '2-digit', minute: '2-digit', timeZone: tz,
});
};
diff --git a/components/settings/active-sessions.tsx b/components/settings/active-sessions.tsx
index 5b567fb..28bdb12 100644
--- a/components/settings/active-sessions.tsx
+++ b/components/settings/active-sessions.tsx
@@ -5,6 +5,7 @@ import { Loader2, Monitor, Smartphone, Trash2, Globe } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
+import { useUserTimezone } from "@/lib/hooks/use-user-timezone";
interface Session {
id: string;
@@ -32,6 +33,7 @@ function parseUserAgent(ua: string): { device: string; browser: string } {
}
export function ActiveSessions({ userId }: ActiveSessionsProps) {
+ const tz = useUserTimezone();
const [sessions, setSessions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [revokingId, setRevokingId] = useState(null);
@@ -115,7 +117,7 @@ export function ActiveSessions({ userId }: ActiveSessionsProps) {
{session.ip_address || "Unknown IP"}
•
- {new Date(session.created_at).toLocaleDateString()}
+ {new Date(session.created_at).toLocaleDateString(undefined, { timeZone: tz })}
diff --git a/components/status/activity-sparkline.tsx b/components/status/activity-sparkline.tsx
index 1265540..02544bf 100644
--- a/components/status/activity-sparkline.tsx
+++ b/components/status/activity-sparkline.tsx
@@ -13,6 +13,7 @@
'use client';
import { cn } from '@/lib/utils';
+import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
interface ActivityBucket {
hour: string;
@@ -26,10 +27,11 @@ interface ActivitySparklineProps {
height?: number;
}
-function fmtHour(iso: string): string {
+function fmtHour(iso: string, tz: string): string {
return new Date(iso).toLocaleTimeString(undefined, {
hour: 'numeric',
minute: '2-digit',
+ timeZone: tz,
});
}
@@ -38,6 +40,7 @@ export function ActivitySparkline({
className,
height = 32,
}: ActivitySparklineProps) {
+ const tz = useUserTimezone();
if (data.length === 0) {
return null;
}
@@ -57,10 +60,10 @@ export function ActivitySparkline({
key={bucket.hour}
title={
empty
- ? `${fmtHour(bucket.hour)} · idle`
- : `${fmtHour(bucket.hour)} · ${bucket.success} ok · ${bucket.failure} fail`
+ ? `${fmtHour(bucket.hour, tz)} · idle`
+ : `${fmtHour(bucket.hour, tz)} · ${bucket.success} ok · ${bucket.failure} fail`
}
- aria-label={`${fmtHour(bucket.hour)}: ${bucket.success} ok, ${bucket.failure} fail`}
+ aria-label={`${fmtHour(bucket.hour, tz)}: ${bucket.success} ok, ${bucket.failure} fail`}
className="relative flex-1 min-w-[1px] flex flex-col-reverse rounded-[1px] overflow-hidden"
style={{ height: `${empty ? 12 : Math.max(totalPct, 8)}%` }}
data-bucket-index={i}