Fix time format setting not applied to UI date/time displays (#75)

- Add time format utilities to date.ts (applyTimeFormat, formatDateTime,
    formatTimeOnly, TimeFormat type)
  - PrintersPage: Pass timeFormat to formatETA for printer card ETA display
  - ArchivesPage: Add timeFormat prop to ArchiveCard for archive timestamps
  - AMSHistoryModal: Apply timeFormat to chart labels, axis ticks, and tooltip
  - ProjectDetailPage: Use formatDateTime for timeline dates
  - QueuePage: Pass timeFormat to formatRelativeTime for scheduled prints
  - SystemInfoPage: Use formatDateTime for boot time display
  - NotificationLogViewer: Apply timeFormat to notification timestamps
  - Bump service worker cache version to v17

Closes #75
This commit is contained in:
maziggy
2026-01-11 13:49:03 +01:00
parent f7f4d88222
commit da2d768002
14 changed files with 8835 additions and 8692 deletions
+22 -8
View File
@@ -13,7 +13,7 @@ import {
ReferenceLine,
} from 'recharts';
import { api, type AMSHistoryResponse } from '../api/client';
import { parseUTCDate } from '../utils/date';
import { parseUTCDate, applyTimeFormat, type TimeFormat } from '../utils/date';
import { useTranslation } from 'react-i18next';
import { useTheme } from '../contexts/ThemeContext';
@@ -58,6 +58,13 @@ export function AMSHistoryModal({
const [mode, setMode] = useState<'humidity' | 'temperature'>(initialMode);
const isDark = themeMode === 'dark';
const { data: settings } = useQuery({
queryKey: ['settings'],
queryFn: api.getSettings,
});
const timeFormat: TimeFormat = settings?.time_format || 'system';
// Close on Escape key
useEffect(() => {
if (!isOpen) return;
@@ -82,15 +89,16 @@ export function AMSHistoryModal({
// Format data for chart
const chartData = data?.data.map(point => {
const date = parseUTCDate(point.recorded_at) || new Date();
const timeOptions: Intl.DateTimeFormatOptions = {
hour: '2-digit',
minute: '2-digit',
...(hours > 24 ? { day: 'numeric', month: 'short' } : {}),
};
return {
time: date.getTime(),
humidity: point.humidity,
temperature: point.temperature,
timeLabel: date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
...(hours > 24 ? { day: 'numeric', month: 'short' } : {}),
}),
timeLabel: date.toLocaleTimeString([], applyTimeFormat(timeOptions, timeFormat)),
};
}) || [];
@@ -311,7 +319,7 @@ export function AMSHistoryModal({
if (hours > 24) {
return date.toLocaleDateString([], { day: 'numeric', month: 'short' });
}
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
return date.toLocaleTimeString([], applyTimeFormat({ hour: '2-digit', minute: '2-digit' }, timeFormat));
}}
stroke={isDark ? '#9ca3af' : '#6b7280'}
tick={{ fontSize: 12 }}
@@ -329,7 +337,13 @@ export function AMSHistoryModal({
borderRadius: '8px',
color: isDark ? '#fff' : '#000',
}}
labelFormatter={(ts) => new Date(ts).toLocaleString()}
labelFormatter={(ts) => new Date(ts).toLocaleString(undefined, applyTimeFormat({
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
}, timeFormat))}
formatter={(value: number) => [
mode === 'humidity' ? `${value}%` : `${value}°C`,
mode === 'humidity' ? 'Humidity' : 'Temperature'
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { History, CheckCircle, XCircle, Loader2, Trash2, RefreshCw, ChevronDown, ChevronUp } from 'lucide-react';
import { api } from '../api/client';
import { parseUTCDate, formatDate as formatDateUtil } from '../utils/date';
import { parseUTCDate, formatTimeOnly, formatDateTime, type TimeFormat } from '../utils/date';
import type { NotificationLogEntry } from '../api/client';
import { Button } from './Button';
import { useToast } from '../contexts/ToastContext';
@@ -44,6 +44,13 @@ export function NotificationLogViewer({ onClose }: NotificationLogViewerProps) {
const [expandedId, setExpandedId] = useState<number | null>(null);
const [showFailedOnly, setShowFailedOnly] = useState(false);
const { data: settings } = useQuery({
queryKey: ['settings'],
queryFn: api.getSettings,
});
const timeFormat: TimeFormat = settings?.time_format || 'system';
const { data: logs, isLoading, refetch, isRefetching } = useQuery({
queryKey: ['notification-logs', days, showFailedOnly],
queryFn: () => api.getNotificationLogs({
@@ -80,7 +87,7 @@ export function NotificationLogViewer({ onClose }: NotificationLogViewerProps) {
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
return date.toLocaleDateString() + ' ' + formatTimeOnly(date, timeFormat);
};
return (
@@ -191,6 +198,7 @@ export function NotificationLogViewer({ onClose }: NotificationLogViewerProps) {
isExpanded={expandedId === log.id}
onToggle={() => setExpandedId(expandedId === log.id ? null : log.id)}
formatDate={formatDate}
formatFullDate={(dateStr) => formatDateTime(dateStr, timeFormat)}
/>
))}
</div>
@@ -213,11 +221,13 @@ function LogEntry({
isExpanded,
onToggle,
formatDate,
formatFullDate,
}: {
log: NotificationLogEntry;
isExpanded: boolean;
onToggle: () => void;
formatDate: (date: string) => string;
formatFullDate: (date: string) => string;
}) {
return (
<div
@@ -280,7 +290,7 @@ function LogEntry({
)}
<div className="flex gap-4 text-xs text-bambu-gray pt-1">
<span>Provider: {log.provider_type}</span>
<span>Time: {formatDateUtil(log.created_at)}</span>
<span>Time: {formatFullDate(log.created_at)}</span>
</div>
</div>
)}