mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-30 09:58:13 +00:00
feat: add toGregorianDateLabel function and update date displays across multiple pages
This commit is contained in:
@@ -59,22 +59,23 @@ const ETH_FORMATTERS = {
|
|||||||
formatMonthDropdown: (month: Date) => ethMonthName(month),
|
formatMonthDropdown: (month: Date) => ethMonthName(month),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Local-date parse/format for the "YYYY-MM-DD" wire format (matches native
|
// Wire format is a full ISO-8601 instant string (e.g.
|
||||||
// <input type="date"> semantics). Deliberately NOT `new Date(iso)` (parses as
|
// "2026-07-31T00:00:00.000Z"), what the backend expects for date fields.
|
||||||
// UTC midnight, off-by-one in negative-offset zones) and NOT
|
// The picker only selects a calendar day, so the time is pinned to UTC
|
||||||
// `.toISOString()` (shifts by the local offset when formatting) — same class
|
// midnight — reading back with getUTC*() (not local get*()) keeps the
|
||||||
// of bug the UTC-noon workaround above already had to fix once in this file.
|
// calendar day stable regardless of the runtime's timezone, avoiding the
|
||||||
|
// same off-by-one class of bug the UTC-noon workaround above exists for.
|
||||||
function parseISO(value?: string | null): Date | null {
|
function parseISO(value?: string | null): Date | null {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
const [y, m, d] = value.split('-').map(Number);
|
const d = new Date(value);
|
||||||
return y && m && d ? new Date(y, m - 1, d) : null;
|
if (isNaN(d.getTime())) return null;
|
||||||
|
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatISO(date: Date): string {
|
function formatISO(date: Date): string {
|
||||||
const y = date.getFullYear();
|
return new Date(
|
||||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
|
||||||
const d = String(date.getDate()).padStart(2, '0');
|
).toISOString();
|
||||||
return `${y}-${m}-${d}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toEthiopicDateLabel(date: Date | string): string {
|
export function toEthiopicDateLabel(date: Date | string): string {
|
||||||
@@ -88,6 +89,12 @@ export function toEthiopicDateLabel(date: Date | string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toGregorianDateLabel(date: Date | string): string {
|
||||||
|
const d = typeof date === 'string' ? parseISO(date) : date;
|
||||||
|
if (!d) return '';
|
||||||
|
return d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||||
|
}
|
||||||
|
|
||||||
const YEAR_DROPDOWN_END = new Date(new Date().getFullYear() + 50, 11, 31);
|
const YEAR_DROPDOWN_END = new Date(new Date().getFullYear() + 50, 11, 31);
|
||||||
|
|
||||||
export interface AmharicDatePickerProps {
|
export interface AmharicDatePickerProps {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import {
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { notify, BilingualInput, useErrorHandler } from '@ema-platform/ui';
|
import { notify, BilingualInput, useErrorHandler } from '@ema-platform/ui';
|
||||||
import type { BilingualValue } from '@ema-platform/ui';
|
import type { BilingualValue } from '@ema-platform/ui';
|
||||||
import { AmharicDatePicker, toEthiopicDateLabel } from '../../../components/AmharicDatePicker';
|
import { AmharicDatePicker, toEthiopicDateLabel, toGregorianDateLabel } from '../../../components/AmharicDatePicker';
|
||||||
import { LocationPicker } from '../../location/components/LocationPicker';
|
import { LocationPicker } from '../../location/components/LocationPicker';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -458,13 +458,13 @@ export function SeafarerRegistrationPage() {
|
|||||||
<ReviewRow label="Middle Name" value={`${middleName.en}${middleName.am ? ` / ${middleName.am}` : ''}`} />
|
<ReviewRow label="Middle Name" value={`${middleName.en}${middleName.am ? ` / ${middleName.am}` : ''}`} />
|
||||||
<ReviewRow label="Last Name" value={`${lastName.en}${lastName.am ? ` / ${lastName.am}` : ''}`} />
|
<ReviewRow label="Last Name" value={`${lastName.en}${lastName.am ? ` / ${lastName.am}` : ''}`} />
|
||||||
<ReviewRow label="Gender" value={gender ?? ''} />
|
<ReviewRow label="Gender" value={gender ?? ''} />
|
||||||
<ReviewRow label="Date of Birth" value={`${dob}${dob ? ` (${toEthiopicDateLabel(dob)})` : ''}`} />
|
<ReviewRow label="Date of Birth" value={`${dob ? toGregorianDateLabel(dob) : ''}${dob ? ` (${toEthiopicDateLabel(dob)})` : ''}`} />
|
||||||
<ReviewRow label="Place of Birth" value={placeOfBirth} />
|
<ReviewRow label="Place of Birth" value={placeOfBirth} />
|
||||||
<ReviewRow label="Nationality" value={nationality ?? ''} />
|
<ReviewRow label="Nationality" value={nationality ?? ''} />
|
||||||
<ReviewRow label="Marital Status" value={maritalStatus ?? ''} />
|
<ReviewRow label="Marital Status" value={maritalStatus ?? ''} />
|
||||||
<ReviewRow label="National ID No." value={nationalIdNumber} />
|
<ReviewRow label="National ID No." value={nationalIdNumber} />
|
||||||
<ReviewRow label="Passport No." value={passportNumber} />
|
<ReviewRow label="Passport No." value={passportNumber} />
|
||||||
<ReviewRow label="Passport Expiry" value={passportExpiry} />
|
<ReviewRow label="Passport Expiry" value={passportExpiry ? toGregorianDateLabel(passportExpiry) : ''} />
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { AmharicDatePicker } from "../../../components/AmharicDatePicker";
|
import { AmharicDatePicker, toGregorianDateLabel } from "../../../components/AmharicDatePicker";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -1228,8 +1228,8 @@ export function SeamanBookApplicationPage() {
|
|||||||
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
||||||
<ReviewRow label="Certificate No." value={medCertNumber} />
|
<ReviewRow label="Certificate No." value={medCertNumber} />
|
||||||
<ReviewRow label="Issuing Centre" value={medIssuer} />
|
<ReviewRow label="Issuing Centre" value={medIssuer} />
|
||||||
<ReviewRow label="Issue Date" value={medIssueDate} />
|
<ReviewRow label="Issue Date" value={medIssueDate ? toGregorianDateLabel(medIssueDate) : ''} />
|
||||||
<ReviewRow label="Expiry Date" value={medExpiryDate} />
|
<ReviewRow label="Expiry Date" value={medExpiryDate ? toGregorianDateLabel(medExpiryDate) : ''} />
|
||||||
<ReviewRow label="Document" value={medFile?.name ?? "—"} />
|
<ReviewRow label="Document" value={medFile?.name ?? "—"} />
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -1254,7 +1254,7 @@ export function SeamanBookApplicationPage() {
|
|||||||
: paymentRef
|
: paymentRef
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ReviewRow label="Payment Date" value={paymentDate} />
|
<ReviewRow label="Payment Date" value={paymentDate ? toGregorianDateLabel(paymentDate) : ''} />
|
||||||
<ReviewRow
|
<ReviewRow
|
||||||
label="Total Paid"
|
label="Total Paid"
|
||||||
value={`ETB ${TOTAL.toFixed(2)}`}
|
value={`ETB ${TOTAL.toFixed(2)}`}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useApiMutation } from '@ema-platform/api';
|
import { useApiMutation } from '@ema-platform/api';
|
||||||
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
|
import { AmharicDatePicker, toGregorianDateLabel } from '../../../components/AmharicDatePicker';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -422,8 +422,8 @@ export function ShippingAgentLicenseApplicationPage() {
|
|||||||
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
||||||
<ReviewRow label="Shipping Company Name" value={shippingCompanyName} />
|
<ReviewRow label="Shipping Company Name" value={shippingCompanyName} />
|
||||||
<ReviewRow label="Agreement Reference Number" value={agreementRefNumber} />
|
<ReviewRow label="Agreement Reference Number" value={agreementRefNumber} />
|
||||||
<ReviewRow label="Agreement Start Date" value={agreementStartDate} />
|
<ReviewRow label="Agreement Start Date" value={agreementStartDate ? toGregorianDateLabel(agreementStartDate) : ''} />
|
||||||
<ReviewRow label="Agreement End Date" value={agreementEndDate} />
|
<ReviewRow label="Agreement End Date" value={agreementEndDate ? toGregorianDateLabel(agreementEndDate) : ''} />
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useApiMutation } from '@ema-platform/api';
|
import { useApiMutation } from '@ema-platform/api';
|
||||||
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
|
import { AmharicDatePicker, toGregorianDateLabel } from '../../../components/AmharicDatePicker';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -454,8 +454,8 @@ export function WaiverApplicationPage() {
|
|||||||
<ReviewRow label="To Port" value={toPortName} />
|
<ReviewRow label="To Port" value={toPortName} />
|
||||||
<ReviewRow label="Vessel Name" value={vesselName} />
|
<ReviewRow label="Vessel Name" value={vesselName} />
|
||||||
<ReviewRow label="Carrier Name" value={carrierName} />
|
<ReviewRow label="Carrier Name" value={carrierName} />
|
||||||
{isPreWaiver && <ReviewRow label="Estimated Arrival Date" value={estimatedArrivalDate} />}
|
{isPreWaiver && <ReviewRow label="Estimated Arrival Date" value={estimatedArrivalDate ? toGregorianDateLabel(estimatedArrivalDate) : ''} />}
|
||||||
{isPostWaiver && <ReviewRow label="Actual Arrival Date" value={actualArrivalDate} />}
|
{isPostWaiver && <ReviewRow label="Actual Arrival Date" value={actualArrivalDate ? toGregorianDateLabel(actualArrivalDate) : ''} />}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user