diff --git a/client/src/components/Admin/AdminMcpTokensPanel.tsx b/client/src/components/Admin/AdminMcpTokensPanel.tsx index 483ce9242..6f9e3a9bd 100644 --- a/client/src/components/Admin/AdminMcpTokensPanel.tsx +++ b/client/src/components/Admin/AdminMcpTokensPanel.tsx @@ -1,6 +1,7 @@ import { type ReactNode } from 'react' import { Key, Trash2, User, Loader2, Shield, KeyRound, Bot } from 'lucide-react' import { useTranslation } from '../../i18n' +import { useFormatDate } from '../../hooks/useDateFormat' import ConfirmDialog from '../shared/ConfirmDialog' import { Tooltip } from '../shared/Tooltip' import { fs } from '../shared/DialogShell' @@ -64,9 +65,10 @@ export default function AdminMcpTokensPanel() { sessions, sessionsLoading, tokens, tokensLoading, expandedScopes, toggleScopes, revokeConfirmId, setRevokeConfirmId, deleteConfirmId, setDeleteConfirmId, handleRevoke, handleDelete, } = useMcpTokensAdmin() - const { t, locale } = useTranslation() + const { t } = useTranslation() + const { formatDate } = useFormatDate() - const date = (iso: string) => new Date(iso).toLocaleDateString(locale) + const date = (iso: string) => formatDate(new Date(iso)) return (
{t('dawarich.accept.recorded', { - from: new Date(suggestion.startedAt).toLocaleString(locale), + from: formatDateTime(new Date(suggestion.startedAt)), to: new Date(suggestion.endedAt).toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit' }), })}
diff --git a/client/src/components/Files/FileManager.helpers.ts b/client/src/components/Files/FileManager.helpers.ts index c93a9d59b..3848d9c8d 100644 --- a/client/src/components/Files/FileManager.helpers.ts +++ b/client/src/components/Files/FileManager.helpers.ts @@ -1,5 +1,7 @@ import { FileText, FileImage, File, FileVideo, Plane, Train, Car, Ship, Bus, Sailboat, CableCar, Bike, CarTaxiFront, Route } from 'lucide-react' import { downloadFile } from '../../utils/fileDownload' +import { readDateFormat } from '../../hooks/useDateFormat' +import { formatDisplayDate } from '../../utils/dateFormat' export function isImage(mimeType?: string | null) { if (!mimeType) return false @@ -57,10 +59,10 @@ export function triggerDownload(url: string, filename: string) { downloadFile(url, filename).catch(() => {}) } -export function formatDateWithLocale(dateStr?: string | null, locale?: string) { +export function formatDateWithLocale(dateStr?: string | null, locale?: string, dateFormat: string = readDateFormat()) { if (!dateStr) return '' try { - return new Date(dateStr).toLocaleDateString(locale, { day: '2-digit', month: '2-digit', year: 'numeric' }) + return formatDisplayDate(new Date(dateStr), locale, { day: '2-digit', month: '2-digit', year: 'numeric' }, dateFormat) } catch { return '' } } diff --git a/client/src/components/Files/docsync/DocSyncBits.tsx b/client/src/components/Files/docsync/DocSyncBits.tsx index ae3db07e4..15e706e4e 100644 --- a/client/src/components/Files/docsync/DocSyncBits.tsx +++ b/client/src/components/Files/docsync/DocSyncBits.tsx @@ -1,5 +1,6 @@ import type { ReactNode } from 'react' import { useTranslation } from '../../../i18n/TranslationContext' +import { useFormatDate } from '../../../hooks/useDateFormat' import { relativeTime } from '../../../utils/relativeTime' import { Tooltip } from '../../shared/Tooltip' @@ -83,11 +84,12 @@ export function StateBadge({ state, compact }: { state: string; compact?: boolea /** "2h ago", or "never" when a binding has not run yet. */ export function LastRun({ at }: { at: string | null }) { - const { t, language, locale } = useTranslation() + const { t, language } = useTranslation() + const { formatDateTime } = useFormatDate() if (!at) return {t('docsync.binding.neverRun')} const ms = Date.parse(at) if (Number.isNaN(ms)) return null - return- {t('settings.apiKeys.createdAt')} {formatStamp(apiKey.created_at, locale)} + {t('settings.apiKeys.createdAt')} {formatStamp(apiKey.created_at, locale, dateFormat)} {', '} {apiKey.last_used_at - ? `${t('settings.apiKeys.usedAt')} ${formatStamp(apiKey.last_used_at, locale)}` + ? `${t('settings.apiKeys.usedAt')} ${formatStamp(apiKey.last_used_at, locale, dateFormat)}` : t('settings.apiKeys.neverUsed')}
{/* What the key may read. Shown on the row rather than behind a detail diff --git a/client/src/components/Settings/DawarichConnectionSection.tsx b/client/src/components/Settings/DawarichConnectionSection.tsx index 61ffd4d3c..97c6c0704 100644 --- a/client/src/components/Settings/DawarichConnectionSection.tsx +++ b/client/src/components/Settings/DawarichConnectionSection.tsx @@ -1,6 +1,7 @@ import React from 'react' import { Loader2, Save, Plug, RefreshCw, Unplug } from 'lucide-react' import { useTranslation } from '../../i18n' +import { useFormatDate } from '../../hooks/useDateFormat' import { useDawarichConnection } from '../../hooks/useDawarichConnection' import DawarichIcon from '../shared/DawarichIcon' import Section from './Section' @@ -23,7 +24,7 @@ import { fs } from '../shared/DialogShell' * no reason is the version of this card that generates support questions. */ export default function DawarichConnectionSection(): React.ReactElement { - const { t, locale } = useTranslation() + const { t } = useTranslation() const S = useDawarichConnection() return ( @@ -123,7 +124,7 @@ export default function DawarichConnectionSection(): React.ReactElement { )} -{state.lastSyncAt - ? t('dawarich.lastSync', { when: new Date(state.lastSyncAt).toLocaleString(locale) }) + ? t('dawarich.lastSync', { when: formatDateTime(new Date(state.lastSyncAt)) }) : t('dawarich.neverSynced')} {state.lastSyncState === 'partial' && `, ${t('dawarich.syncPartial')}`}
diff --git a/client/src/components/Settings/DisplaySettingsTab.test.tsx b/client/src/components/Settings/DisplaySettingsTab.test.tsx index da9ab45cc..7d7d28426 100644 --- a/client/src/components/Settings/DisplaySettingsTab.test.tsx +++ b/client/src/components/Settings/DisplaySettingsTab.test.tsx @@ -1,4 +1,4 @@ -// FE-COMP-DISPLAY-001 to FE-COMP-DISPLAY-055 +// FE-COMP-DISPLAY-001 to FE-COMP-DISPLAY-057 import { render, screen, within, fireEvent } from '../../../tests/helpers/render'; import userEvent from '@testing-library/user-event'; import { http, HttpResponse } from 'msw'; @@ -484,6 +484,35 @@ describe('DisplaySettingsTab week start (#2029)', () => { }); }); +// ── Date format (056, 057) ──────────────────────────────────────────────────── + +describe('DisplaySettingsTab date format (#2971)', () => { + it('FE-COMP-DISPLAY-056: shows the language format when unset and saves the format picked', async () => { + const user = userEvent.setup(); + const updateSetting = vi.fn().mockResolvedValue(undefined); + seedStore(useSettingsStore, { settings: buildSettings({ date_format: undefined }), updateSetting }); + render({session.client_name}
{t('settings.oauth.sessionScopes')}: {session.scopes.join(', ')} - {t('settings.oauth.sessionExpires')} {new Date(session.access_token_expires_at).toLocaleDateString(locale)} + {t('settings.oauth.sessionExpires')} {formatDate(new Date(session.access_token_expires_at))}