mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-07 18:55:43 +00:00
feat: implement date display utility across various pages
- Added `useDateDisplayer` hook and `dateDisplayer` function to format dates consistently based on the user's language. - Updated multiple components and pages in both backoffice and portal applications to utilize the new date display functionality, ensuring proper formatting for dates in lists, tables, and detail views. - Introduced Ethiopian date formatting for Amharic language support. - Refactored date handling in components such as ExamAppealsPage, ResultPage, SeafarerRegistryPage, and others to improve localization and user experience.
This commit is contained in:
57
libs/shared/src/lib/date/date-displayer.ts
Normal file
57
libs/shared/src/lib/date/date-displayer.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { ethTimeLabel, toAmharicDisplay } from './ethiopic';
|
||||
|
||||
/** Wire values carrying no time of day, e.g. a date column serialised as `2026-08-10`. */
|
||||
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
/** Midnight UTC — how backends commonly serialise a plain date column. */
|
||||
const UTC_MIDNIGHT = /T00:00:00(\.0+)?Z$/;
|
||||
|
||||
/**
|
||||
* The one way a date is shown to a user.
|
||||
*
|
||||
* Timestamps render in the viewer's local timezone — `2026-08-10T06:57:34.507Z`
|
||||
* reads as `Aug 10, 2026 9:57am` in Addis. Values with no real time of day
|
||||
* render as a bare date: a birth date or a licence expiry stamped `12:00am`
|
||||
* reads as precision the data does not have. Those are also formatted in UTC,
|
||||
* because parsing `2026-08-10` yields UTC midnight, and converting that to a
|
||||
* behind-UTC local timezone would show the previous day.
|
||||
*
|
||||
* `language` is a parameter rather than read from i18next because each app runs
|
||||
* a DEDICATED i18n instance, not the global singleton (see `app/i18n/config.ts`)
|
||||
* — the same reason `localized()` in licensing.helpers.ts takes one. Components
|
||||
* should not call this directly; use `useDateDisplayer()` so the text actually
|
||||
* re-renders when the language changes.
|
||||
*/
|
||||
export function dateDisplayer(
|
||||
value: string | number | Date | null | undefined,
|
||||
language = 'en',
|
||||
): string {
|
||||
if (value === null || value === undefined || value === '') return '—';
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return '—';
|
||||
|
||||
const dateOnly =
|
||||
typeof value === 'string' && (DATE_ONLY.test(value) || UTC_MIDNIGHT.test(value));
|
||||
|
||||
if (language.startsWith('am')) {
|
||||
const day = toAmharicDisplay(date); // ሐምሌ 22/2018
|
||||
return dateOnly ? day : `${day} - ${ethTimeLabel(date)}`;
|
||||
}
|
||||
|
||||
const day = date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
...(dateOnly ? { timeZone: 'UTC' } : {}),
|
||||
});
|
||||
if (dateOnly) return day;
|
||||
|
||||
// Intl gives "9:57 AM"; the house format is "9:57am".
|
||||
const time = date
|
||||
.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
|
||||
.replace(' ', '')
|
||||
.toLowerCase();
|
||||
|
||||
return `${day} ${time}`;
|
||||
}
|
||||
Reference in New Issue
Block a user