feat(07.1-05): user-tz on analyzer pages
- itglue/applications, applications/[id], configurations, configurations/[id], sites/[companyId], queue, ticket/[ticketNumber], tickets, reports, reports/[id]: useUserTimezone() in default export; thread tz into every inline toLocale*String call. - analyzer/tickets/page.tsx converts module-scope formatRelative(iso) helper to formatRelative(iso, tz); updates 1 callsite. Migrates 16 of 81 audit leak callsites.
This commit is contained in:
parent
23b179f2a7
commit
96edfb4444
10 changed files with 38 additions and 18 deletions
|
|
@ -23,6 +23,7 @@ import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Undo2,
|
Undo2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
import { useSession } from '@/lib/auth-client';
|
import { useSession } from '@/lib/auth-client';
|
||||||
|
|
||||||
interface FieldRow {
|
interface FieldRow {
|
||||||
|
|
@ -157,6 +158,7 @@ export default function ApplicationAuditPage({
|
||||||
}: {
|
}: {
|
||||||
params: Promise<{ id: string }>;
|
params: Promise<{ id: string }>;
|
||||||
}) {
|
}) {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const { id } = use(params);
|
const { id } = use(params);
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const role = (session?.user as { role?: string } | undefined)?.role ?? 'user';
|
const role = (session?.user as { role?: string } | undefined)?.role ?? 'user';
|
||||||
|
|
@ -408,7 +410,7 @@ export default function ApplicationAuditPage({
|
||||||
<CardTitle className="text-base">
|
<CardTitle className="text-base">
|
||||||
Audit findings
|
Audit findings
|
||||||
<span className="ml-2 text-xs text-muted-foreground font-normal">
|
<span className="ml-2 text-xs text-muted-foreground font-normal">
|
||||||
{new Date(audit.generated_at).toLocaleString()}
|
{new Date(audit.generated_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{' · '}
|
{' · '}
|
||||||
{audit.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
{audit.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
||||||
{audit.estimated_cost_usd !== null
|
{audit.estimated_cost_usd !== null
|
||||||
|
|
@ -717,7 +719,7 @@ export default function ApplicationAuditPage({
|
||||||
</Badge>
|
</Badge>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{new Date(w.performed_at).toLocaleString()}
|
{new Date(w.performed_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs mt-1 break-words">
|
<p className="text-xs mt-1 break-words">
|
||||||
<span className="text-muted-foreground">Before: </span>
|
<span className="text-muted-foreground">Before: </span>
|
||||||
|
|
@ -776,7 +778,7 @@ export default function ApplicationAuditPage({
|
||||||
className="py-2 flex items-center justify-between text-sm"
|
className="py-2 flex items-center justify-between text-sm"
|
||||||
>
|
>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{new Date(h.generated_at).toLocaleString()}
|
{new Date(h.generated_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{' · '}
|
{' · '}
|
||||||
{h.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
{h.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/badge';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
interface ApplicationRow {
|
interface ApplicationRow {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -32,6 +33,7 @@ function scoreBadgeVariant(
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ApplicationsListPage() {
|
export default function ApplicationsListPage() {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [rows, setRows] = useState<ApplicationRow[] | null>(null);
|
const [rows, setRows] = useState<ApplicationRow[] | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
|
|
@ -126,7 +128,7 @@ export default function ApplicationsListPage() {
|
||||||
{' · '}
|
{' · '}
|
||||||
last audited{' '}
|
last audited{' '}
|
||||||
{r.latestAudit.generatedAt
|
{r.latestAudit.generatedAt
|
||||||
? new Date(r.latestAudit.generatedAt).toLocaleDateString()
|
? new Date(r.latestAudit.generatedAt).toLocaleDateString(undefined, { timeZone: tz })
|
||||||
: 'unknown'}
|
: 'unknown'}
|
||||||
{' '}
|
{' '}
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
Server,
|
Server,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useSession } from '@/lib/auth-client';
|
import { useSession } from '@/lib/auth-client';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
interface FieldRow {
|
interface FieldRow {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -136,6 +137,7 @@ export default function ConfigurationAuditPage({
|
||||||
params: Promise<{ id: string }>;
|
params: Promise<{ id: string }>;
|
||||||
}) {
|
}) {
|
||||||
const { id } = use(params);
|
const { id } = use(params);
|
||||||
|
const tz = useUserTimezone();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const role = (session?.user as { role?: string } | undefined)?.role ?? 'user';
|
const role = (session?.user as { role?: string } | undefined)?.role ?? 'user';
|
||||||
const canWrite = role === 'admin' || role === 'super-admin';
|
const canWrite = role === 'admin' || role === 'super-admin';
|
||||||
|
|
@ -390,7 +392,7 @@ export default function ConfigurationAuditPage({
|
||||||
<CardTitle className="text-base">
|
<CardTitle className="text-base">
|
||||||
Audit findings
|
Audit findings
|
||||||
<span className="ml-2 text-xs text-muted-foreground font-normal">
|
<span className="ml-2 text-xs text-muted-foreground font-normal">
|
||||||
{new Date(audit.generated_at).toLocaleString()}
|
{new Date(audit.generated_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{' · '}
|
{' · '}
|
||||||
{audit.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
{audit.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
||||||
{audit.estimated_cost_usd !== null
|
{audit.estimated_cost_usd !== null
|
||||||
|
|
@ -685,7 +687,7 @@ export default function ConfigurationAuditPage({
|
||||||
</Badge>
|
</Badge>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{new Date(w.performed_at).toLocaleString()}
|
{new Date(w.performed_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs mt-1 break-words">
|
<p className="text-xs mt-1 break-words">
|
||||||
<span className="text-muted-foreground">Before: </span>
|
<span className="text-muted-foreground">Before: </span>
|
||||||
|
|
@ -738,7 +740,7 @@ export default function ConfigurationAuditPage({
|
||||||
{history.map((h) => (
|
{history.map((h) => (
|
||||||
<li key={h.id} className="py-2 flex items-center justify-between text-sm">
|
<li key={h.id} className="py-2 flex items-center justify-between text-sm">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{new Date(h.generated_at).toLocaleString()}
|
{new Date(h.generated_at).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{' · '}
|
{' · '}
|
||||||
{h.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
{h.provider === 'openrouter' ? 'DeepSeek' : 'Claude'}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/badge';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
interface ConfigurationRow {
|
interface ConfigurationRow {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -34,6 +35,7 @@ function scoreBadgeVariant(
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ConfigurationsListPage() {
|
export default function ConfigurationsListPage() {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [rows, setRows] = useState<ConfigurationRow[] | null>(null);
|
const [rows, setRows] = useState<ConfigurationRow[] | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
|
|
@ -127,7 +129,7 @@ export default function ConfigurationsListPage() {
|
||||||
<>
|
<>
|
||||||
{' · '}last audited{' '}
|
{' · '}last audited{' '}
|
||||||
{r.latestAudit.generatedAt
|
{r.latestAudit.generatedAt
|
||||||
? new Date(r.latestAudit.generatedAt).toLocaleDateString()
|
? new Date(r.latestAudit.generatedAt).toLocaleDateString(undefined, { timeZone: tz })
|
||||||
: 'unknown'}
|
: 'unknown'}
|
||||||
{' '}
|
{' '}
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { RmmScriptPicker } from '@/components/rmm/rmm-script-picker';
|
import { RmmScriptPicker } from '@/components/rmm/rmm-script-picker';
|
||||||
import { Server } from 'lucide-react';
|
import { Server } from 'lucide-react';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
interface SiteInfo {
|
interface SiteInfo {
|
||||||
companyId: string;
|
companyId: string;
|
||||||
|
|
@ -45,6 +46,7 @@ export default function SiteDiscoveryPage({
|
||||||
params: Promise<{ companyId: string }>;
|
params: Promise<{ companyId: string }>;
|
||||||
}) {
|
}) {
|
||||||
const { companyId } = use(params);
|
const { companyId } = use(params);
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [info, setInfo] = useState<SiteInfo | null>(null);
|
const [info, setInfo] = useState<SiteInfo | null>(null);
|
||||||
const [executions, setExecutions] = useState<ExecRow[] | null>(null);
|
const [executions, setExecutions] = useState<ExecRow[] | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -209,7 +211,7 @@ export default function SiteDiscoveryPage({
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-0.5">
|
<p className="text-xs text-muted-foreground mt-0.5">
|
||||||
{e.targetHostname ?? '—'} ·{' '}
|
{e.targetHostname ?? '—'} ·{' '}
|
||||||
{new Date(e.queuedAt).toLocaleString()}
|
{new Date(e.queuedAt).toLocaleString(undefined, { timeZone: tz })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { AlertTriangle } from 'lucide-react';
|
import { AlertTriangle } from 'lucide-react';
|
||||||
import type { PersistedAnalysis } from '@/lib/types/analyzer';
|
import type { PersistedAnalysis } from '@/lib/types/analyzer';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
export default function AnalyzerQueuePage() {
|
export default function AnalyzerQueuePage() {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [analyses, setAnalyses] = useState<PersistedAnalysis[] | null>(null);
|
const [analyses, setAnalyses] = useState<PersistedAnalysis[] | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
|
@ -100,7 +102,7 @@ export default function AnalyzerQueuePage() {
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{new Date(a.triggeredAt).toLocaleString()}
|
{new Date(a.triggeredAt).toLocaleString(undefined, { timeZone: tz })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-end gap-1 shrink-0">
|
<div className="flex flex-col items-end gap-1 shrink-0">
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
XCircle,
|
XCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { AnalysisMarkdown } from '@/components/analyzer/analysis-markdown';
|
import { AnalysisMarkdown } from '@/components/analyzer/analysis-markdown';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
type Report = {
|
type Report = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -124,6 +125,7 @@ export default function AggregateReportPage({
|
||||||
params: Promise<{ id: string }>;
|
params: Promise<{ id: string }>;
|
||||||
}) {
|
}) {
|
||||||
const { id } = use(params);
|
const { id } = use(params);
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [report, setReport] = useState<Report | null>(null);
|
const [report, setReport] = useState<Report | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -183,7 +185,7 @@ export default function AggregateReportPage({
|
||||||
{report.reportTitle ?? `Aggregate report · ${report.ticketCount} tickets`}
|
{report.reportTitle ?? `Aggregate report · ${report.ticketCount} tickets`}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{new Date(report.generatedAt).toLocaleString()}
|
{new Date(report.generatedAt).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{report.modelUsed && ` · ${report.modelUsed}`}
|
{report.modelUsed && ` · ${report.modelUsed}`}
|
||||||
{report.estimatedCostUsd !== null && (
|
{report.estimatedCostUsd !== null && (
|
||||||
<> · ${report.estimatedCostUsd.toFixed(4)}</>
|
<> · ${report.estimatedCostUsd.toFixed(4)}</>
|
||||||
|
|
@ -196,9 +198,9 @@ export default function AggregateReportPage({
|
||||||
{report.dateRangeActual?.earliest && report.dateRangeActual?.latest && (
|
{report.dateRangeActual?.earliest && report.dateRangeActual?.latest && (
|
||||||
<>
|
<>
|
||||||
{' · '}
|
{' · '}
|
||||||
{new Date(report.dateRangeActual.earliest).toLocaleDateString()}
|
{new Date(report.dateRangeActual.earliest).toLocaleDateString(undefined, { timeZone: tz })}
|
||||||
{' – '}
|
{' – '}
|
||||||
{new Date(report.dateRangeActual.latest).toLocaleDateString()}
|
{new Date(report.dateRangeActual.latest).toLocaleDateString(undefined, { timeZone: tz })}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table';
|
} from '@/components/ui/table';
|
||||||
import { CheckCircle2, XCircle, Loader2 } from 'lucide-react';
|
import { CheckCircle2, XCircle, Loader2 } from 'lucide-react';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
interface ReportSummary {
|
interface ReportSummary {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -27,6 +28,7 @@ interface ReportSummary {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ReportsListPage() {
|
export default function ReportsListPage() {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [reports, setReports] = useState<ReportSummary[]>([]);
|
const [reports, setReports] = useState<ReportSummary[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -133,7 +135,7 @@ export default function ReportsListPage() {
|
||||||
: `$${r.estimatedCostUsd.toFixed(4)}`}
|
: `$${r.estimatedCostUsd.toFixed(4)}`}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-xs text-muted-foreground">
|
<TableCell className="text-xs text-muted-foreground">
|
||||||
{new Date(r.generatedAt).toLocaleString()}
|
{new Date(r.generatedAt).toLocaleString(undefined, { timeZone: tz })}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<Button variant="outline" size="sm" asChild>
|
<Button variant="outline" size="sm" asChild>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
import { PageHeader } from '@/components/navigation/page-header';
|
import { PageHeader } from '@/components/navigation/page-header';
|
||||||
import { Sparkles, Zap } from 'lucide-react';
|
import { Sparkles, Zap } from 'lucide-react';
|
||||||
import type { PersistedAnalysis } from '@/lib/types/analyzer';
|
import type { PersistedAnalysis } from '@/lib/types/analyzer';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
export default function TicketAnalyzerPage({
|
export default function TicketAnalyzerPage({
|
||||||
params,
|
params,
|
||||||
|
|
@ -22,6 +23,7 @@ export default function TicketAnalyzerPage({
|
||||||
params: Promise<{ ticketNumber: string }>;
|
params: Promise<{ ticketNumber: string }>;
|
||||||
}) {
|
}) {
|
||||||
const { ticketNumber } = use(params);
|
const { ticketNumber } = use(params);
|
||||||
|
const tz = useUserTimezone();
|
||||||
const [analyses, setAnalyses] = useState<PersistedAnalysis[] | null>(null);
|
const [analyses, setAnalyses] = useState<PersistedAnalysis[] | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [provider, setProvider] = useState<AnalyzerProvider>('anthropic');
|
const [provider, setProvider] = useState<AnalyzerProvider>('anthropic');
|
||||||
|
|
@ -140,7 +142,7 @@ export default function TicketAnalyzerPage({
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{new Date(a.triggeredAt).toLocaleString()}
|
{new Date(a.triggeredAt).toLocaleString(undefined, { timeZone: tz })}
|
||||||
{' · '}
|
{' · '}
|
||||||
{tierLabel}
|
{tierLabel}
|
||||||
{' · '}${a.estimatedCostUsd.toFixed(4)}
|
{' · '}${a.estimatedCostUsd.toFixed(4)}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import {
|
||||||
Clock,
|
Clock,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { useUserTimezone } from '@/lib/hooks/use-user-timezone';
|
||||||
|
|
||||||
type Period =
|
type Period =
|
||||||
| 'today'
|
| 'today'
|
||||||
|
|
@ -98,7 +99,7 @@ interface FilterOptions {
|
||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
const SELECTION_LS_KEY = 'analyzer:ticket-selection:v1';
|
const SELECTION_LS_KEY = 'analyzer:ticket-selection:v1';
|
||||||
|
|
||||||
function formatRelative(iso: string | null): string {
|
function formatRelative(iso: string | null, tz: string): string {
|
||||||
if (!iso) return '—';
|
if (!iso) return '—';
|
||||||
const d = new Date(iso);
|
const d = new Date(iso);
|
||||||
const diffMs = Date.now() - d.getTime();
|
const diffMs = Date.now() - d.getTime();
|
||||||
|
|
@ -109,7 +110,7 @@ function formatRelative(iso: string | null): string {
|
||||||
if (diffHr < 24) return `${diffHr}h ago`;
|
if (diffHr < 24) return `${diffHr}h ago`;
|
||||||
const diffDays = Math.round(diffHr / 24);
|
const diffDays = Math.round(diffHr / 24);
|
||||||
if (diffDays < 7) return `${diffDays}d ago`;
|
if (diffDays < 7) return `${diffDays}d ago`;
|
||||||
return d.toLocaleDateString();
|
return d.toLocaleDateString(undefined, { timeZone: tz });
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatAge(days: number | null): string {
|
function formatAge(days: number | null): string {
|
||||||
|
|
@ -142,6 +143,7 @@ function writeSelection(ids: string[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AnalyzerBrowseTicketsPage() {
|
export default function AnalyzerBrowseTicketsPage() {
|
||||||
|
const tz = useUserTimezone();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [period, setPeriod] = useState<Period>('last_30d');
|
const [period, setPeriod] = useState<Period>('last_30d');
|
||||||
|
|
@ -896,7 +898,7 @@ export default function AnalyzerBrowseTicketsPage() {
|
||||||
className="text-xs text-muted-foreground"
|
className="text-xs text-muted-foreground"
|
||||||
title={t.lastActivityAtAutotask ?? ''}
|
title={t.lastActivityAtAutotask ?? ''}
|
||||||
>
|
>
|
||||||
{formatRelative(t.lastActivityAtAutotask)}
|
{formatRelative(t.lastActivityAtAutotask, tz)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-xs truncate max-w-[120px]">
|
<TableCell className="text-xs truncate max-w-[120px]">
|
||||||
{t.assignedResourceName ?? (
|
{t.assignedResourceName ?? (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue