diff --git a/app/admin/sync/datto-rmm/page.tsx b/app/admin/sync/datto-rmm/page.tsx index 2f1cee9..15d4975 100644 --- a/app/admin/sync/datto-rmm/page.tsx +++ b/app/admin/sync/datto-rmm/page.tsx @@ -17,10 +17,11 @@ import { ArrowLeft, Activity, History, Monitor, Loader2, RefreshCw, ExternalLink, Server, Wifi, WifiOff, AlertTriangle, Bell, XCircle, CheckCircle2, Clock, } from 'lucide-react'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; -function fmtDate(d: string | null) { +function fmtDate(d: string | null, tz: string) { if (!d) return 'Never'; - return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz }); } function StatCard({ label, value, sub, icon: Icon, cls }: { label: string; value: string | number; sub?: string; icon?: React.ElementType; cls?: string }) { @@ -35,7 +36,7 @@ function StatCard({ label, value, sub, icon: Icon, cls }: { label: string; value ); } -function StatusTab({ data, onSync, syncing }: { data: any; onSync: () => void; syncing: boolean }) { +function StatusTab({ data, onSync, syncing, tz }: { data: any; onSync: () => void; syncing: boolean; tz: string }) { if (!data) return
; const devs = data.devices ?? {}; @@ -46,7 +47,7 @@ function StatusTab({ data, onSync, syncing }: { data: any; onSync: () => void; s

{data.configured ? 'Connected' : 'Not configured'}

-

Last sync: {fmtDate(data.lastSync)}

+

Last sync: {fmtDate(data.lastSync, tz)}

@@ -99,7 +100,7 @@ function StatusTab({ data, onSync, syncing }: { data: any; onSync: () => void; s ); } -function HistoryTab({ refreshKey }: { refreshKey: number }) { +function HistoryTab({ refreshKey, tz }: { refreshKey: number; tz: string }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); @@ -140,7 +141,7 @@ function HistoryTab({ refreshKey }: { refreshKey: number }) { {row.status} {row.records_added ?? 0} - {fmtDate(row.started_at)} + {fmtDate(row.started_at, tz)} {dur != null ? `${dur}s` : '—'} @@ -154,6 +155,7 @@ function HistoryTab({ refreshKey }: { refreshKey: number }) { } export default function DattoRmmPage() { + const tz = useUserTimezone(); const [status, setStatus] = useState(null); const [syncing, setSyncing] = useState(false); const [refreshKey, setRefreshKey] = useState(0); @@ -222,10 +224,10 @@ export default function DattoRmmPage() { - + - +
diff --git a/app/admin/sync/duo/page.tsx b/app/admin/sync/duo/page.tsx index 9ec527d..ca050ca 100644 --- a/app/admin/sync/duo/page.tsx +++ b/app/admin/sync/duo/page.tsx @@ -12,6 +12,7 @@ import { TableRow, } from '@/components/ui/table'; import { RefreshCw, ArrowLeft, Loader2, CheckCircle2, AlertTriangle, Shield, Users, Smartphone, ScrollText, Layers, AppWindow, ChevronDown, ChevronUp, ShieldOff, ShieldAlert, ShieldX } from 'lucide-react'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; interface DuoStatus { connected: boolean; @@ -55,6 +56,7 @@ interface DuoAccount { } export default function DuoSyncPage() { + const tz = useUserTimezone(); const [status, setStatus] = useState(null); const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(true); @@ -114,7 +116,7 @@ export default function DuoSyncPage() { const fmtDate = (d: string | null) => { if (!d) return 'Never'; - return new Date(d).toLocaleString(); + return new Date(d).toLocaleString(undefined, { timeZone: tz }); }; if (loading) { diff --git a/app/admin/sync/itglue/page.tsx b/app/admin/sync/itglue/page.tsx index e7418bd..8ea45a8 100644 --- a/app/admin/sync/itglue/page.tsx +++ b/app/admin/sync/itglue/page.tsx @@ -18,12 +18,13 @@ import { ExternalLink, CheckCircle2, XCircle, Clock, AlertTriangle, Building2, Monitor, Users, Key, FileText, Globe, Shield, Package, } from 'lucide-react'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; -function fmtDate(d: string | null) { +function fmtDate(d: string | null, tz: string) { if (!d) return 'Never'; return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', - hour: '2-digit', minute: '2-digit', + hour: '2-digit', minute: '2-digit', timeZone: tz, }); } @@ -65,9 +66,9 @@ function SyncStatusBadge({ status }: { status: string }) { } function StatusTab({ - syncData, onSync, syncing, + syncData, onSync, syncing, tz, }: { - syncData: any; onSync: () => void; syncing: boolean; + syncData: any; onSync: () => void; syncing: boolean; tz: string; }) { if (!syncData) { return ( @@ -90,7 +91,7 @@ function StatusTab({ Connected to IT Glue

- Last sync: {fmtDate(latest?.completed_at ?? null)} + Last sync: {fmtDate(latest?.completed_at ?? null, tz)} {latest?.duration_ms && ` · ${fmtDuration(latest.duration_ms)}`}

@@ -165,7 +166,7 @@ function StatusTab({ ); } -function HistoryTab({ history }: { history: any[] }) { +function HistoryTab({ history, tz }: { history: any[]; tz: string }) { if (!history.length) { return (
@@ -192,7 +193,7 @@ function HistoryTab({ history }: { history: any[] }) { {row.triggered_by ?? 'system'} {(row.total_upserted ?? 0).toLocaleString()} - {fmtDate(row.started_at)} + {fmtDate(row.started_at, tz)} {fmtDuration(row.duration_ms)} ))} @@ -261,6 +262,7 @@ function AboutTab() { } export default function ITGluePage() { + const tz = useUserTimezone(); const [syncData, setSyncData] = useState(null); const [syncing, setSyncing] = useState(false); @@ -341,11 +343,11 @@ export default function ITGluePage() { - + - + diff --git a/app/admin/sync/mimecast/page.tsx b/app/admin/sync/mimecast/page.tsx index 1b08a4a..1fc51ab 100644 --- a/app/admin/sync/mimecast/page.tsx +++ b/app/admin/sync/mimecast/page.tsx @@ -22,11 +22,12 @@ import { import { StatusBadge } from '@/components/ui/status-badge'; import { Checkbox } from '@/components/ui/checkbox'; import SyncScheduler from '@/components/admin/SyncScheduler'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; -function fmtDate(d: string | null | undefined) { +function fmtDate(d: string | null | undefined, tz: string) { if (!d) return 'Never'; return new Date(d).toLocaleString(undefined, { - month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', + month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz, }); } @@ -70,7 +71,7 @@ function ThreatLevelBadge({ level }: { level: string }) { } // ── Status Tab ──────────────────────────────────────────────────────────────── -function StatusTab({ data, onSync, syncing }: { data: any; onSync: (t: string) => void; syncing: boolean }) { +function StatusTab({ data, onSync, syncing, tz }: { data: any; onSync: (t: string) => void; syncing: boolean; tz: string }) { if (!data) return (
@@ -96,7 +97,7 @@ function StatusTab({ data, onSync, syncing }: { data: any; onSync: (t: string) = )}

- Last sync: {fmtDate(stats.lastSync)} · Oldest message: {fmtDate(stats.oldestMessage)} + Last sync: {fmtDate(stats.lastSync, tz)} · Oldest message: {fmtDate(stats.oldestMessage, tz)}

@@ -139,7 +140,7 @@ function StatusTab({ data, onSync, syncing }: { data: any; onSync: (t: string) = } // ── Messages Tab ────────────────────────────────────────────────────────────── -function MessagesTab() { +function MessagesTab({ tz }: { tz: string }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); @@ -232,7 +233,7 @@ function MessagesTab() { {r.subject ?? '—'} {r.direction ?? '—'} - {fmtDate(r.sent_datetime)} + {fmtDate(r.sent_datetime, tz)} ))} @@ -244,7 +245,7 @@ function MessagesTab() { } // ── Threats Tab ─────────────────────────────────────────────────────────────── -function ThreatsTab() { +function ThreatsTab({ tz }: { tz: string }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); @@ -283,7 +284,7 @@ function ThreatsTab() { {r.url ?? r.file_name ?? '—'} - {fmtDate(r.event_datetime)} + {fmtDate(r.event_datetime, tz)} ))} @@ -404,7 +405,7 @@ function CloudUserTab() { } // ── History Tab ─────────────────────────────────────────────────────────────── -function HistoryTab() { +function HistoryTab({ tz }: { tz: string }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); @@ -453,7 +454,7 @@ function HistoryTab() { {fmtNum(meta.messagesUpserted ?? r.records_added)} {fmtNum(meta.threatsUpserted)} - {fmtDate(r.started_at)} + {fmtDate(r.started_at, tz)} {durStr} ); @@ -593,11 +594,12 @@ function analyzeMessage(m: any): Analysis { }; } -function MessageAnalysisDialog({ message, onClose, onRelease, releasing }: { +function MessageAnalysisDialog({ message, onClose, onRelease, releasing, tz }: { message: any; onClose: () => void; onRelease: (m: any) => void; releasing: boolean; + tz: string; }) { if (!message) return null; const analysis = analyzeMessage(message); @@ -648,7 +650,7 @@ function MessageAnalysisDialog({ message, onClose, onRelease, releasing }: {
Received - {new Date(message.dateReceived).toLocaleString()} + {new Date(message.dateReceived).toLocaleString(undefined, { timeZone: tz })}
Policy @@ -734,7 +736,7 @@ const TENANT_OPTIONS = [ { id: '2', name: 'Seubert & Associates' }, ]; -function HeldMailTab() { +function HeldMailTab({ tz }: { tz: string }) { const [data, setData] = useState(null); const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(false); @@ -921,7 +923,7 @@ function HeldMailTab() { {filtered.map((m: any) => ( - {new Date(m.dateReceived).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })} + {new Date(m.dateReceived).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz })}
{m.to}
@@ -982,6 +984,7 @@ function HeldMailTab() { onClose={() => setAnalysisMessage(null)} onRelease={release} releasing={analysisMessage ? !!releasing[analysisMessage.id] : false} + tz={tz} />
); @@ -1112,11 +1115,12 @@ function analyzeDelivered(m: any): { headline: string; explanation: string; seve }; } -function DeliveredAnalysisDialog({ message, onClose, onFindSimilar, allMessages }: { +function DeliveredAnalysisDialog({ message, onClose, onFindSimilar, allMessages, tz }: { message: any; onClose: () => void; onFindSimilar?: (type: 'sender' | 'ip' | 'subject', value: string) => void; allMessages?: any[]; + tz: string; }) { const [remedStep, setRemedStep] = useState<'idle' | 'searching' | 'confirm' | 'removing' | 'done'>('idle'); const [remedMatches, setRemedMatches] = useState([]); @@ -1236,7 +1240,7 @@ function DeliveredAnalysisDialog({ message, onClose, onFindSimilar, allMessages
Received - {new Date(message.received).toLocaleString()} + {new Date(message.received).toLocaleString(undefined, { timeZone: tz })}
Status @@ -1402,7 +1406,7 @@ function DeliveredAnalysisDialog({ message, onClose, onFindSimilar, allMessages
{m.subject || '(no subject)'}
- {new Date(m.receivedDateTime).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })} + {new Date(m.receivedDateTime).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz })} {!m.isRead && unread}
@@ -1457,7 +1461,7 @@ function DeliveredAnalysisDialog({ message, onClose, onFindSimilar, allMessages ); } -function DeliveredMailTab() { +function DeliveredMailTab({ tz }: { tz: string }) { const [tenantId, setTenantId] = useState('1'); const [to, setTo] = useState(''); const [from, setFrom] = useState(''); @@ -1837,7 +1841,7 @@ function DeliveredMailTab() { : '' }> - {new Date(m.received).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })} + {new Date(m.received).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz })}
{m.to}
@@ -1887,6 +1891,7 @@ function DeliveredMailTab() { onClose={() => setAnalysisMessage(null)} onFindSimilar={handleFindSimilar} allMessages={messages} + tz={tz} />
); @@ -1894,6 +1899,7 @@ function DeliveredMailTab() { // ── Page ────────────────────────────────────────────────────────────────────── export default function MimecastSyncPage() { + const tz = useUserTimezone(); const [statusData, setStatusData] = useState(null); const [syncing, setSyncing] = useState(false); const [lastResult, setLastResult] = useState(null); @@ -1972,14 +1978,14 @@ export default function MimecastSyncPage() { - + - - - - + + + + - +
diff --git a/app/admin/sync/sentinelone/page.tsx b/app/admin/sync/sentinelone/page.tsx index 2a1aba9..18ff32c 100644 --- a/app/admin/sync/sentinelone/page.tsx +++ b/app/admin/sync/sentinelone/page.tsx @@ -9,10 +9,11 @@ import { ArrowLeft, RefreshCw, Play, CheckCircle2, XCircle, Clock, Shield, Monitor, AlertTriangle, Activity, } from 'lucide-react'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; -function fmtDate(d: string | null) { +function fmtDate(d: string | null, tz: string) { if (!d) return '—'; - return new Date(d).toLocaleString(); + return new Date(d).toLocaleString(undefined, { timeZone: tz }); } function fmtDuration(ms: number | null) { if (!ms) return '—'; @@ -21,6 +22,7 @@ function fmtDuration(ms: number | null) { } export default function SentinelOneSyncPage() { + const tz = useUserTimezone(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); @@ -120,7 +122,7 @@ export default function SentinelOneSyncPage() {
{lastSync.status}
- {fmtDate(lastSync.completed_at || lastSync.started_at)} · {fmtDuration(lastSync.duration_ms)} · {lastSync.total_upserted?.toLocaleString()} records + {fmtDate(lastSync.completed_at || lastSync.started_at, tz)} · {fmtDuration(lastSync.duration_ms)} · {lastSync.total_upserted?.toLocaleString()} records
@@ -159,7 +161,7 @@ export default function SentinelOneSyncPage() { {h.status === 'completed' ? : h.status === 'running' ? : } - {fmtDate(h.started_at)} + {fmtDate(h.started_at, tz)}
{h.total_upserted?.toLocaleString() ?? 0} records diff --git a/app/admin/sync/veeam/page.tsx b/app/admin/sync/veeam/page.tsx index fc65edc..03291da 100644 --- a/app/admin/sync/veeam/page.tsx +++ b/app/admin/sync/veeam/page.tsx @@ -19,11 +19,12 @@ import { } from '@/components/ui/table'; import { StatusBadge } from '@/components/ui/status-badge'; import SyncScheduler from '@/components/admin/SyncScheduler'; +import { useUserTimezone } from '@/lib/hooks/use-user-timezone'; // ── Helpers ─────────────────────────────────────────────────────────────────── -function fmtDate(d: string | null | undefined) { +function fmtDate(d: string | null | undefined, tz: string) { if (!d) return 'Never'; - return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: tz }); } function fmtDur(ms: number) { if (ms < 60000) return `${Math.round(ms / 1000)}s`; @@ -54,7 +55,7 @@ function SyncStatusBadge({ status }: { status: string }) { } // ── Status Tab ──────────────────────────────────────────────────────────────── -function VeeamStatusTab({ data, onSync, syncing }: { data: any; onSync: (t: string) => void; syncing: boolean }) { +function VeeamStatusTab({ data, onSync, syncing, tz }: { data: any; onSync: (t: string) => void; syncing: boolean; tz: string }) { if (!data) return (
@@ -73,7 +74,7 @@ function VeeamStatusTab({ data, onSync, syncing }: { data: any; onSync: (t: stri

{data.configured ? 'Connected to VSPC' : 'Not configured'}

-

Last sync: {fmtDate(data.lastSync)}

+

Last sync: {fmtDate(data.lastSync, tz)}

@@ -323,7 +324,7 @@ function AgentsTab({ refreshKey }: { refreshKey: number }) { } // ── Alarms Tab ──────────────────────────────────────────────────────────────── -function AlarmsTab({ refreshKey }: { refreshKey: number }) { +function AlarmsTab({ refreshKey, tz }: { refreshKey: number; tz: string }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -386,7 +387,7 @@ function AlarmsTab({ refreshKey }: { refreshKey: number }) { {a.repeat_count ?? 0} - {fmtDate(a.last_activation_time)} + {fmtDate(a.last_activation_time, tz)} {a.last_activation_message?.trim() || '—'} @@ -400,7 +401,7 @@ function AlarmsTab({ refreshKey }: { refreshKey: number }) { } // ── RPO Tab ─────────────────────────────────────────────────────────────────── -function RpoTab({ refreshKey }: { refreshKey: number }) { +function RpoTab({ refreshKey, tz }: { refreshKey: number; tz: string }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [running, setRunning] = useState(false); @@ -503,7 +504,7 @@ function RpoTab({ refreshKey }: { refreshKey: number }) { {j.job_name} {j.org_name} - {fmtDate(j.last_end_time)} + {fmtDate(j.last_end_time, tz)} {display} {j.failure_category ?? '—'} @@ -545,7 +546,7 @@ function RpoTab({ refreshKey }: { refreshKey: number }) { {j.job_name} {j.org_name} - {fmtDate(j.last_end_time)} + {fmtDate(j.last_end_time, tz)} {j.hours_since_backup !== null ? `${j.hours_since_backup}h` : '—'} @@ -562,6 +563,7 @@ function RpoTab({ refreshKey }: { refreshKey: number }) { // ── Page ────────────────────────────────────────────────────────────────────── export default function VeeamSyncPage() { + const tz = useUserTimezone(); const [status, setStatus] = useState(null); const [syncing, setSyncing] = useState(false); const [refreshKey, setRefreshKey] = useState(0); @@ -633,11 +635,11 @@ export default function VeeamSyncPage() { Schedules - - - + + + - +