refactor(webui): use Core date-time formatting

This commit is contained in:
2026-07-21 13:52:59 +02:00
parent 7237679a85
commit 5dc9392290

View File

@@ -8,6 +8,7 @@ import {
DismissibleAlert, DismissibleAlert,
ExplorerTree, ExplorerTree,
fetchAuthGroups, fetchAuthGroups,
formatDateTime,
FormField, FormField,
LoadingFrame, LoadingFrame,
PasswordField, PasswordField,
@@ -19,7 +20,8 @@ import {
hasScope, hasScope,
type ApiSettings, type ApiSettings,
type AuthInfo, type AuthInfo,
type AuthUpdate type AuthUpdate,
type FormatDateTimeOptions
} from "@govoplan/core-webui"; } from "@govoplan/core-webui";
import { import {
createAddressBook, createAddressBook,
@@ -291,14 +293,17 @@ function syncSourceLabel(source: AddressSyncSource): string {
return source.display_name || source.external_address_book_ref || source.connector_type; return source.display_name || source.external_address_book_ref || source.connector_type;
} }
function formatDateTime(value?: string | null): string { const ADDRESS_DATE_TIME_OPTIONS: FormatDateTimeOptions = {
if (!value) return "Never"; fallback: "Never",
try { year: undefined,
return new Intl.DateTimeFormat(undefined, { dateStyle: "short", timeStyle: "short" }).format(new Date(value)); month: undefined,
} catch { day: undefined,
return value; hour: undefined,
} minute: undefined,
} timeZoneName: undefined,
dateStyle: "short",
timeStyle: "short"
};
function planSummary(plan: AddressSyncPlan | null): string { function planSummary(plan: AddressSyncPlan | null): string {
if (!plan) return "No preview loaded."; if (!plan) return "No preview loaded.";
@@ -1740,7 +1745,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
{selectedBookSyncSources.map((source) => ( {selectedBookSyncSources.map((source) => (
<p key={source.id}> <p key={source.id}>
{syncSourceLabel(source)} · {source.connector_type} · {source.status} {syncSourceLabel(source)} · {source.connector_type} · {source.status}
{source.last_success_at ? ` · last success ${formatDateTime(source.last_success_at)}` : ""} {source.last_success_at ? ` · last success ${formatDateTime(source.last_success_at, ADDRESS_DATE_TIME_OPTIONS)}` : ""}
{source.last_error ? ` · ${source.last_error}` : ""} {source.last_error ? ` · ${source.last_error}` : ""}
</p> </p>
))} ))}
@@ -2314,7 +2319,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
<StatusBadge status={syncInspector.source.status} /> <StatusBadge status={syncInspector.source.status} />
{syncInspector.source.read_only && <StatusBadge status="read-only" />} {syncInspector.source.read_only && <StatusBadge status="read-only" />}
</div> </div>
<p className="muted">Last attempt: {formatDateTime(syncInspector.source.last_attempted_at)} · Last success: {formatDateTime(syncInspector.source.last_success_at)}</p> <p className="muted">Last attempt: {formatDateTime(syncInspector.source.last_attempted_at, ADDRESS_DATE_TIME_OPTIONS)} · Last success: {formatDateTime(syncInspector.source.last_success_at, ADDRESS_DATE_TIME_OPTIONS)}</p>
{syncInspector.source.last_error && <DismissibleAlert tone="danger" resetKey={syncInspector.source.last_error}>{syncInspector.source.last_error}</DismissibleAlert>} {syncInspector.source.last_error && <DismissibleAlert tone="danger" resetKey={syncInspector.source.last_error}>{syncInspector.source.last_error}</DismissibleAlert>}
<div className="button-row compact-actions"> <div className="button-row compact-actions">
<Button <Button
@@ -2383,7 +2388,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
<div className="address-sync-record-list"> <div className="address-sync-record-list">
{syncTombstones.slice(0, 20).map((tombstone) => ( {syncTombstones.slice(0, 20).map((tombstone) => (
<div className="address-sync-record-row" key={tombstone.id}> <div className="address-sync-record-row" key={tombstone.id}>
<span><strong>{tombstone.remote_uid || tombstone.resource_href || tombstone.contact_id}</strong><small>{formatDateTime(tombstone.synced_at || tombstone.remote_deleted_at || tombstone.local_deleted_at)}</small></span> <span><strong>{tombstone.remote_uid || tombstone.resource_href || tombstone.contact_id}</strong><small>{formatDateTime(tombstone.synced_at || tombstone.remote_deleted_at || tombstone.local_deleted_at, ADDRESS_DATE_TIME_OPTIONS)}</small></span>
</div> </div>
))} ))}
</div> </div>