diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index a221cd60..7890c3b3 100644 --- a/client/src/pages/DashboardPage.tsx +++ b/client/src/pages/DashboardPage.tsx @@ -37,17 +37,33 @@ const GRADIENTS = [ ] function tripGradient(id: number): string { return GRADIENTS[id % GRADIENTS.length] } -// Day + short month for the boarding pass / cards, e.g. { d: '10', m: 'Sep' }. -function splitDate(dateStr: string | null | undefined, locale: string): { d: string; m: string } | null { +// Day + short month for the boarding pass / cards, plus the year — but only +// when it isn't the current year (this year's trips stay clutter-free), e.g. +// { d: '10', m: 'Sep', y: '' } now vs { …, y: '2024' } for an older trip. +function splitDate(dateStr: string | null | undefined, locale: string): { d: string; m: string; y: string } | null { if (!dateStr) return null const date = new Date(dateStr + 'T00:00:00Z') if (isNaN(date.getTime())) return null // malformed date — render a dash, never crash + const otherYear = date.getUTCFullYear() !== new Date().getUTCFullYear() return { d: date.toLocaleDateString(locale, { day: 'numeric', timeZone: 'UTC' }), m: date.toLocaleDateString(locale, { month: 'short', timeZone: 'UTC' }), + y: otherYear ? date.toLocaleDateString(locale, { year: 'numeric', timeZone: 'UTC' }) : '', } } +// Localized date for the cards. The year is included only when it isn't the +// current year, and order/punctuation follow the locale (EN "Sep 10, 2026", +// DE "10. Sep 2026" — vs a plain "Sep 10" this year), never a hard-coded layout. +function fullDate(dateStr: string | null | undefined, locale: string): string | null { + if (!dateStr) return null + const date = new Date(dateStr + 'T00:00:00Z') + if (isNaN(date.getTime())) return null + const opts: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short', timeZone: 'UTC' } + if (date.getUTCFullYear() !== new Date().getUTCFullYear()) opts.year = 'numeric' + return date.toLocaleDateString(locale, opts) +} + function buddyColor(seed: number): string { const pairs = [ ['#6366f1', '#8b5cf6'], ['#10b981', '#059669'], ['#f59e0b', '#d97706'], @@ -322,10 +338,10 @@ function BoardingPassHero({ trip, bundle, locale, onOpen, onEdit, onCopy, onArch
{t('dashboard.hero.tripDates')}
- {start ?
{start.d}
{start.m}
+ {start ?
{start.d}
{start.m}{start.y ? ` ${start.y}` : ''}
:
}
- {end ?
{end.d}
{end.m}
+ {end ?
{end.d}
{end.m}{end.y ? ` ${end.y}` : ''}
:
}
@@ -530,9 +546,9 @@ function TripCard({ trip, locale, onOpen, onEdit, onCopy, onArchive, onDelete }:
{start && end ? ( <> - {start.m} {start.d} + {fullDate(trip.start_date, locale)} - {end.m} {end.d} + {fullDate(trip.end_date, locale)} ) : {t('dashboard.hero.noDates')}}
diff --git a/client/src/utils/formatters.ts b/client/src/utils/formatters.ts index 441ab116..afa8cf25 100644 --- a/client/src/utils/formatters.ts +++ b/client/src/utils/formatters.ts @@ -93,11 +93,15 @@ export function formatMoney( export function formatDate(dateStr: string | null | undefined, locale: string, timeZone?: string): string | null { if (!dateStr) return null + const date = new Date(dateStr + 'T00:00:00Z') const opts: Intl.DateTimeFormatOptions = { weekday: 'short', day: 'numeric', month: 'short', timeZone: timeZone || 'UTC', } - return new Date(dateStr + 'T00:00:00Z').toLocaleDateString(locale, opts) + // Show the year only when it isn't the current year, so this year's dates stay + // compact while older/future ones are unambiguous. + if (date.getUTCFullYear() !== new Date().getUTCFullYear()) opts.year = 'numeric' + return date.toLocaleDateString(locale, opts) } export function formatTime(timeStr: string | null | undefined, locale: string, timeFormat: string): string {