feat: integrate AmharicDatePicker component across multiple pages and update translations

This commit is contained in:
estifanos
2026-07-31 10:27:50 +00:00
parent b5858c37be
commit 48bc31d321
17 changed files with 205 additions and 76 deletions

View File

@@ -1,6 +1,13 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ActionIcon, Button, Group, Popover, TextInput } from '@mantine/core';
import {
ActionIcon,
Button,
Group,
MantineSize,
Popover,
TextInput,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { DayPicker as EthiopicDayPicker } from '@daypicker/ethiopic';
import { DayPicker as GregorianDayPicker } from '@daypicker/react';
@@ -52,23 +59,48 @@ const ETH_FORMATTERS = {
formatMonthDropdown: (month: Date) => ethMonthName(month),
};
export function toEthiopicDateLabel(date: Date): string {
// Local-date parse/format for the "YYYY-MM-DD" wire format (matches native
// <input type="date"> semantics). Deliberately NOT `new Date(iso)` (parses as
// UTC midnight, off-by-one in negative-offset zones) and NOT
// `.toISOString()` (shifts by the local offset when formatting) — same class
// of bug the UTC-noon workaround above already had to fix once in this file.
function parseISO(value?: string | null): Date | null {
if (!value) return null;
const [y, m, d] = value.split('-').map(Number);
return y && m && d ? new Date(y, m - 1, d) : null;
}
function formatISO(date: Date): string {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
export function toEthiopicDateLabel(date: Date | string): string {
const d = typeof date === 'string' ? parseISO(date) : date;
if (!d) return '';
try {
const eth = toEthDateTime(date);
const eth = toEthDateTime(d);
return `EC ${eth.year}-${String(eth.month).padStart(2, '0')}-${String(eth.date).padStart(2, '0')}`;
} catch {
return date.toLocaleDateString('en-US');
return d.toLocaleDateString('en-US');
}
}
const YEAR_DROPDOWN_END = new Date(new Date().getFullYear() + 50, 11, 31);
export interface AmharicDatePickerProps {
label?: string;
value?: Date | null;
onChange?: (date: Date | null) => void;
label?: React.ReactNode;
value?: string | null;
onChange?: (value: string) => void;
required?: boolean;
placeholder?: string;
error?: React.ReactNode;
disabled?: boolean;
size?: MantineSize;
name?: string;
onBlur?: () => void;
}
export function AmharicDatePicker({
@@ -77,21 +109,28 @@ export function AmharicDatePicker({
onChange,
required,
placeholder,
error,
disabled,
size,
name,
onBlur,
}: AmharicDatePickerProps) {
const { i18n } = useTranslation();
const { t, i18n } = useTranslation();
const [calendarType, setCalendarType] = useState<'EN' | 'AMH'>(() =>
i18n.language?.startsWith('am') ? 'AMH' : 'EN',
);
const [opened, { close, toggle }] = useDisclosure(false);
const displayValue = value
const selected = parseISO(value);
const displayValue = selected
? calendarType === 'EN'
? value.toLocaleDateString('en-US', {
? selected.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
: toAmharicDisplay(value)
: toAmharicDisplay(selected)
: '';
return (
@@ -109,24 +148,32 @@ export function AmharicDatePicker({
required={required}
value={displayValue}
readOnly
disabled={disabled}
size={size}
name={name}
error={error}
placeholder={placeholder}
onClick={toggle}
onClick={() => !disabled && toggle()}
onBlur={onBlur}
leftSection={
<Button
type="button"
variant="light"
size="compact-xs"
tabIndex={0}
disabled={disabled}
onClick={(e) => {
e.stopPropagation();
setCalendarType((prev) => (prev === 'EN' ? 'AMH' : 'EN'));
}}
aria-label="Switch calendar type"
aria-label={t('common.switchCalendar')}
>
{calendarType}
</Button>
}
leftSectionWidth="calc(4.375rem * var(--mantine-scale))"
rightSection={
<ActionIcon size="md" variant="transparent" onClick={toggle}>
<ActionIcon size="md" variant="transparent" disabled={disabled} onClick={() => toggle()}>
<IconCalendarEvent size={20} />
</ActionIcon>
}
@@ -138,14 +185,14 @@ export function AmharicDatePicker({
<EthiopicDayPicker
className="amharic-daypicker"
mode="single"
selected={value ?? undefined}
defaultMonth={value ?? undefined}
selected={selected ?? undefined}
defaultMonth={selected ?? undefined}
endMonth={YEAR_DROPDOWN_END}
numerals="latn"
captionLayout="dropdown"
formatters={ETH_FORMATTERS}
onSelect={(date: Date | undefined) => {
onChange?.(date ?? null);
onChange?.(date ? formatISO(date) : '');
close();
}}
/>
@@ -153,12 +200,12 @@ export function AmharicDatePicker({
<GregorianDayPicker
className="amharic-daypicker"
mode="single"
selected={value ?? undefined}
defaultMonth={value ?? undefined}
selected={selected ?? undefined}
defaultMonth={selected ?? undefined}
endMonth={YEAR_DROPDOWN_END}
captionLayout="dropdown"
onSelect={(date: Date | undefined) => {
onChange?.(date ?? null);
onChange?.(date ? formatISO(date) : '');
close();
}}
/>
@@ -169,7 +216,7 @@ export function AmharicDatePicker({
variant="subtle"
size="xs"
onClick={() => {
onChange?.(null);
onChange?.('');
close();
}}
>
@@ -179,7 +226,7 @@ export function AmharicDatePicker({
variant="light"
size="xs"
onClick={() => {
onChange?.(new Date());
onChange?.(formatISO(new Date()));
close();
}}
>

View File

@@ -1,4 +1,5 @@
import { useRef, useState } from 'react';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -202,9 +203,9 @@ function UploadModal({
<TextInput label="Issuing Institution" placeholder="e.g. Bahirdar Maritime School" required value={issuer} onChange={(e) => setIssuer(e.currentTarget.value)} size="sm" />
<TextInput label="Certificate Number" placeholder="e.g. PST-2024-001" required value={certNumber} onChange={(e) => setCertNumber(e.currentTarget.value)} size="sm" />
<SimpleGrid cols={2} spacing="sm">
<TextInput label="Issue Date" type="date" required value={issueDate} onChange={(e) => setIssueDate(e.currentTarget.value)} size="sm" />
<AmharicDatePicker label="Issue Date" required value={issueDate} onChange={setIssueDate} size="sm" />
{item.refreshYears > 0 && (
<TextInput label={`Expiry Date (${item.refreshYears}-yr refresh)`} type="date" value={expiryDate} onChange={(e) => setExpiryDate(e.currentTarget.value)} size="sm" />
<AmharicDatePicker label={`Expiry Date (${item.refreshYears}-yr refresh)`} value={expiryDate} onChange={setExpiryDate} size="sm" />
)}
</SimpleGrid>
<div>

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -999,11 +1000,10 @@ export function CoCApplicationPage() {
size="sm"
required
/>
<TextInput
<AmharicDatePicker
label="Payment Date"
type="date"
value={paymentDate}
onChange={(e) => setPaymentDate(e.currentTarget.value)}
onChange={setPaymentDate}
size="sm"
required
/>

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { CountrySelect, getCountryName } from '@ema-platform/ui';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -133,8 +134,8 @@ function ApplicationWizard({ onDone }: { onDone: () => void }) {
<CountrySelect label="Issuing Country" value={country} onChange={setCountry} required />
<TextInput label="Issuing Authority / Administration" placeholder="e.g. Maritime Industry Authority (MARINA)" value={issuer} onChange={(e) => setIssuer(e.currentTarget.value)} required />
<TextInput label="Certificate Type" placeholder="e.g. Officer in Charge of a Navigational Watch" value={cocType} onChange={(e) => setCocType(e.currentTarget.value)} required />
<TextInput label="Issue Date" type="date" value={issueDate} onChange={(e) => setIssueDate(e.currentTarget.value)} required />
<TextInput label="Expiry Date" type="date" value={expiryDate} onChange={(e) => setExpiryDate(e.currentTarget.value)} required />
<AmharicDatePicker label="Issue Date" value={issueDate} onChange={setIssueDate} required />
<AmharicDatePicker label="Expiry Date" value={expiryDate} onChange={setExpiryDate} required />
</SimpleGrid>
</Paper>
)}

View File

@@ -1,6 +1,7 @@
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useApiMutation } from '@ema-platform/api';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -447,7 +448,7 @@ export function JointInvestmentLicenseApplicationPage() {
<SectionHead title="Business License and Registration Information" />
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
<TextInput label="Budget Year" required value={budgetYear} onChange={(e) => setBudgetYear(e.currentTarget.value)} />
<TextInput label="License Validity Date" required placeholder="YYYY-MM-DD" value={licenseValidityDate} onChange={(e) => setLicenseValidityDate(e.currentTarget.value)} />
<AmharicDatePicker label="License Validity Date" required value={licenseValidityDate} onChange={setLicenseValidityDate} />
</SimpleGrid>
</Stack>
)}

View File

@@ -1,4 +1,5 @@
import { useRef, useState } from 'react';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -224,18 +225,16 @@ export function MedicalCertificatePage() {
size="sm"
/>
<SimpleGrid cols={2} spacing="sm">
<TextInput
<AmharicDatePicker
label="Issue Date"
type="date"
value={issuedDate}
onChange={(e) => setIssuedDate(e.currentTarget.value)}
onChange={setIssuedDate}
size="sm"
/>
<TextInput
<AmharicDatePicker
label="Expiry Date"
type="date"
value={expiryDate}
onChange={(e) => setExpiryDate(e.currentTarget.value)}
onChange={setExpiryDate}
size="sm"
/>
</SimpleGrid>

View File

@@ -1,6 +1,7 @@
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useApiMutation } from '@ema-platform/api';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -572,8 +573,8 @@ export function MtoLicenseApplicationPage() {
<DocCard slot={{ key: 'customsBondDoc', label: 'Customs Bond Document', description: 'Customs bond documentation', required: true, icon: IconShieldCheck }} file={customsBondDoc} onFile={setCustomsBondDoc} />
</SimpleGrid>
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" mt="xs">
<TextInput label="Insurance Validity Date" required placeholder="YYYY-MM-DD" value={insuranceValidityDate} onChange={(e) => setInsuranceValidityDate(e.currentTarget.value)} />
<TextInput label="Bond Validity Date" required placeholder="YYYY-MM-DD" value={bondValidityDate} onChange={(e) => setBondValidityDate(e.currentTarget.value)} />
<AmharicDatePicker label="Insurance Validity Date" required value={insuranceValidityDate} onChange={setInsuranceValidityDate} />
<AmharicDatePicker label="Bond Validity Date" required value={bondValidityDate} onChange={setBondValidityDate} />
</SimpleGrid>
</Stack>
)}

View File

@@ -1,6 +1,7 @@
import { Loader, Select, SimpleGrid, TextInput } from '@mantine/core';
import type { FieldErrors, UseFormRegister, UseFormSetValue, UseFormWatch, UseFormTrigger } from 'react-hook-form';
import { z } from 'zod';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
export const profileSchema = z.object({
professionId: z.string().min(1, 'Select your profession'),
@@ -86,11 +87,13 @@ export function ProfileFormContent({
onBlur={() => trigger('gender')}
name="gender"
/>
<TextInput
<AmharicDatePicker
label="Date of Birth"
type="date"
required
{...register('dob')}
value={watch('dob')}
onChange={(val) => setValue('dob', val, { shouldValidate: true })}
onBlur={() => trigger('dob')}
name="dob"
error={errors.dob?.message}
/>
<TextInput

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
ActionIcon,
Alert,
@@ -462,6 +463,9 @@ function HistoryTab({ entries }: { entries: HistoryEntry[] }) {
// Add Record Modal (generic)
// ---------------------------------------------------------------------------
function AddTrainingModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
const [issueDate, setIssueDate] = useState('');
const [expiryDate, setExpiryDate] = useState('');
return (
<Modal opened={opened} onClose={onClose} title="Add Training Record" size="lg">
<Stack gap="sm">
@@ -471,8 +475,8 @@ function AddTrainingModal({ opened, onClose }: { opened: boolean; onClose: () =>
<TextInput label="Certificate No." placeholder="CERT-0000" />
</SimpleGrid>
<SimpleGrid cols={2} spacing="sm">
<TextInput label="Issue Date" type="date" />
<TextInput label="Expiry Date" type="date" />
<AmharicDatePicker label="Issue Date" value={issueDate} onChange={setIssueDate} />
<AmharicDatePicker label="Expiry Date" value={expiryDate} onChange={setExpiryDate} />
</SimpleGrid>
<Select label="Status" data={['Approved', 'Pending', 'Expired']} defaultValue="Pending" />
<Group justify="flex-end" mt="sm">
@@ -485,14 +489,17 @@ function AddTrainingModal({ opened, onClose }: { opened: boolean; onClose: () =>
}
function AddMedicalModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
const [issueDate, setIssueDate] = useState('');
const [expiryDate, setExpiryDate] = useState('');
return (
<Modal opened={opened} onClose={onClose} title="Add Medical Record" size="lg">
<Stack gap="sm">
<TextInput label="Exam Type" placeholder="e.g. STCW Medical Certificate" required />
<TextInput label="Issued By" placeholder="Issuing authority" />
<SimpleGrid cols={2} spacing="sm">
<TextInput label="Issue Date" type="date" />
<TextInput label="Expiry Date" type="date" />
<AmharicDatePicker label="Issue Date" value={issueDate} onChange={setIssueDate} />
<AmharicDatePicker label="Expiry Date" value={expiryDate} onChange={setExpiryDate} />
</SimpleGrid>
<Select label="Result" data={['Fit', 'Unfit', 'Conditional']} defaultValue="Fit" />
<Textarea label="Remarks" placeholder="Any notes" autosize minRows={2} />
@@ -507,6 +514,8 @@ function AddMedicalModal({ opened, onClose }: { opened: boolean; onClose: () =>
function AddSeaServiceModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
const [flag, setFlag] = useState<string | null>(null);
const [fromDate, setFromDate] = useState('');
const [toDate, setToDate] = useState('');
return (
<Modal opened={opened} onClose={onClose} title="Add Sea Service Record" size="lg">
@@ -520,8 +529,8 @@ function AddSeaServiceModal({ opened, onClose }: { opened: boolean; onClose: ()
<CountrySelect label="Flag" value={flag} onChange={setFlag} />
</SimpleGrid>
<SimpleGrid cols={2} spacing="sm">
<TextInput label="From" type="date" />
<TextInput label="To" type="date" />
<AmharicDatePicker label="From" value={fromDate} onChange={setFromDate} />
<AmharicDatePicker label="To" value={toDate} onChange={setToDate} />
</SimpleGrid>
<TextInput label="Engagement Port" placeholder="Port name" />
<Group justify="flex-end" mt="sm">
@@ -534,6 +543,9 @@ function AddSeaServiceModal({ opened, onClose }: { opened: boolean; onClose: ()
}
function AddCertModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
const [issueDate, setIssueDate] = useState('');
const [expiryDate, setExpiryDate] = useState('');
return (
<Modal opened={opened} onClose={onClose} title="Add Certification" size="lg">
<Stack gap="sm">
@@ -544,8 +556,8 @@ function AddCertModal({ opened, onClose }: { opened: boolean; onClose: () => voi
</SimpleGrid>
<TextInput label="Issued By" placeholder="Issuing authority" />
<SimpleGrid cols={2} spacing="sm">
<TextInput label="Issue Date" type="date" />
<TextInput label="Expiry Date" type="date" />
<AmharicDatePicker label="Issue Date" value={issueDate} onChange={setIssueDate} />
<AmharicDatePicker label="Expiry Date" value={expiryDate} onChange={setExpiryDate} />
</SimpleGrid>
<Select label="Status" data={['Valid', 'Expired', 'Pending']} defaultValue="Valid" />
<Group justify="flex-end" mt="sm">

View File

@@ -264,7 +264,7 @@ export function SeafarerRegistrationPage() {
const [middleName, setMiddleName] = useState<BilingualValue>({ en: '', am: '' });
const [lastName, setLastName] = useState<BilingualValue>({ en: '', am: '' });
const [gender, setGender] = useState<string | null>(null);
const [dob, setDob] = useState<Date | null>(null);
const [dob, setDob] = useState('');
const [placeOfBirth, setPlaceOfBirth] = useState('');
const [nationality, setNationality] = useState<string | null>('Ethiopian');
const [maritalStatus, setMaritalStatus] = useState<string | null>(null);
@@ -307,7 +307,7 @@ export function SeafarerRegistrationPage() {
setSubmitting(true);
try {
const result = await submitSeafarerRegistration({
personalInfo: { firstName, middleName, lastName, gender, dob: dob?.toISOString().split('T')[0] ?? '', placeOfBirth, nationality, maritalStatus, nationalIdNumber, passportNumber, passportExpiry },
personalInfo: { firstName, middleName, lastName, gender, dob, placeOfBirth, nationality, maritalStatus, nationalIdNumber, passportNumber, passportExpiry },
contactDetails: { mobile, email, locationId, permanentAddress, currentAddress, emergency: { name: emergencyName, relationship: emergencyRel, phone: emergencyPhone } },
documents: Object.fromEntries(Object.entries(files).map(([k, v]) => [k, v?.name ?? null])),
});
@@ -365,7 +365,7 @@ export function SeafarerRegistrationPage() {
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
<TextInput label="National ID Number" placeholder="ET-000000" required value={nationalIdNumber} onChange={(e) => setNationalIdNumber(e.currentTarget.value)} />
<TextInput label="Passport Number" placeholder="EP000000" value={passportNumber} onChange={(e) => setPassportNumber(e.currentTarget.value)} />
<TextInput label="Passport Expiry Date" type="date" value={passportExpiry} onChange={(e) => setPassportExpiry(e.currentTarget.value)} />
<AmharicDatePicker label="Passport Expiry Date" value={passportExpiry} onChange={setPassportExpiry} />
</SimpleGrid>
<Alert variant="light" color="blue" icon={<IconInfoCircle size={17} />}>
@@ -458,7 +458,7 @@ export function SeafarerRegistrationPage() {
<ReviewRow label="Middle Name" value={`${middleName.en}${middleName.am ? ` / ${middleName.am}` : ''}`} />
<ReviewRow label="Last Name" value={`${lastName.en}${lastName.am ? ` / ${lastName.am}` : ''}`} />
<ReviewRow label="Gender" value={gender ?? ''} />
<ReviewRow label="Date of Birth" value={`${dob?.toLocaleDateString('en-US') ?? ''}${dob ? ` (${toEthiopicDateLabel(dob)})` : ''}`} />
<ReviewRow label="Date of Birth" value={`${dob}${dob ? ` (${toEthiopicDateLabel(dob)})` : ''}`} />
<ReviewRow label="Place of Birth" value={placeOfBirth} />
<ReviewRow label="Nationality" value={nationality ?? ''} />
<ReviewRow label="Marital Status" value={maritalStatus ?? ''} />

View File

@@ -1,4 +1,5 @@
import { useRef, useState } from "react";
import { AmharicDatePicker } from "../../../components/AmharicDatePicker";
import {
Alert,
Badge,
@@ -315,20 +316,18 @@ function BSTCard({
onChange={(e) => onIssuer(e.currentTarget.value)}
/>
<SimpleGrid cols={slot.refreshYears > 0 ? 2 : 1} spacing="xs">
<TextInput
<AmharicDatePicker
label="Issue Date"
type="date"
size="xs"
value={issueDate}
onChange={(e) => onIssueDate(e.currentTarget.value)}
onChange={onIssueDate}
/>
{slot.refreshYears > 0 && (
<TextInput
<AmharicDatePicker
label={`Expiry (${slot.refreshYears}yr)`}
type="date"
size="xs"
value={expiryDate}
onChange={(e) => onExpiryDate(e.currentTarget.value)}
onChange={onExpiryDate}
/>
)}
</SimpleGrid>
@@ -665,19 +664,17 @@ export function SeamanBookApplicationPage() {
/>
</SimpleGrid>
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
<TextInput
<AmharicDatePicker
label="Issue Date"
type="date"
required
value={medIssueDate}
onChange={(e) => setMedIssueDate(e.currentTarget.value)}
onChange={setMedIssueDate}
/>
<TextInput
<AmharicDatePicker
label="Expiry Date"
type="date"
required
value={medExpiryDate}
onChange={(e) => setMedExpiryDate(e.currentTarget.value)}
onChange={setMedExpiryDate}
/>
</SimpleGrid>
<SectionHead title="Upload Certificate" />
@@ -1006,12 +1003,11 @@ export function SeamanBookApplicationPage() {
value={paymentRef}
onChange={(e) => setPaymentRef(e.currentTarget.value)}
/>
<TextInput
<AmharicDatePicker
label="Payment Date"
type="date"
required
value={paymentDate}
onChange={(e) => setPaymentDate(e.currentTarget.value)}
onChange={setPaymentDate}
/>
</SimpleGrid>
</>

View File

@@ -1,6 +1,7 @@
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useApiMutation } from '@ema-platform/api';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -324,8 +325,8 @@ export function ShippingAgentLicenseApplicationPage() {
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
<TextInput label="Shipping Company Name" required value={shippingCompanyName} onChange={(e) => setShippingCompanyName(e.currentTarget.value)} />
<TextInput label="Agreement Reference Number" required value={agreementRefNumber} onChange={(e) => setAgreementRefNumber(e.currentTarget.value)} />
<TextInput label="Agreement Start Date" type="date" required value={agreementStartDate} onChange={(e) => setAgreementStartDate(e.currentTarget.value)} />
<TextInput label="Agreement End Date" type="date" required value={agreementEndDate} onChange={(e) => setAgreementEndDate(e.currentTarget.value)} />
<AmharicDatePicker label="Agreement Start Date" required value={agreementStartDate} onChange={setAgreementStartDate} />
<AmharicDatePicker label="Agreement End Date" required value={agreementEndDate} onChange={setAgreementEndDate} />
</SimpleGrid>
</Stack>
)}

View File

@@ -1,6 +1,7 @@
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useApiMutation } from '@ema-platform/api';
import { AmharicDatePicker } from '../../../components/AmharicDatePicker';
import {
Alert,
Badge,
@@ -372,7 +373,7 @@ export function WaiverApplicationPage() {
<SectionHead title="Invoice Information" />
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
<TextInput label="Invoice Number" required value={invoiceNumber} onChange={(e) => setInvoiceNumber(e.currentTarget.value)} />
<TextInput type="date" label="Invoice Date" required value={invoiceDate} onChange={(e) => setInvoiceDate(e.currentTarget.value)} />
<AmharicDatePicker label="Invoice Date" required value={invoiceDate} onChange={setInvoiceDate} />
<NumberInput label="Invoice Amount" required min={0} value={invoiceAmount} onChange={setInvoiceAmount} />
</SimpleGrid>
</Stack>
@@ -393,10 +394,10 @@ export function WaiverApplicationPage() {
<TextInput label="Shipping Line / Carrier Name" required value={carrierName} onChange={(e) => setCarrierName(e.currentTarget.value)} />
<TextInput label="Bill of Lading Number" value={billOfLadingNumber} onChange={(e) => setBillOfLadingNumber(e.currentTarget.value)} />
{isPreWaiver && (
<TextInput type="date" label="Estimated Arrival Date" required value={estimatedArrivalDate} onChange={(e) => setEstimatedArrivalDate(e.currentTarget.value)} />
<AmharicDatePicker label="Estimated Arrival Date" required value={estimatedArrivalDate} onChange={setEstimatedArrivalDate} />
)}
{isPostWaiver && (
<TextInput type="date" label="Actual Arrival Date" required value={actualArrivalDate} onChange={(e) => setActualArrivalDate(e.currentTarget.value)} />
<AmharicDatePicker label="Actual Arrival Date" required value={actualArrivalDate} onChange={setActualArrivalDate} />
)}
</SimpleGrid>
</Stack>

View File

@@ -81,6 +81,7 @@ export const am: Translations = {
learnMore: 'ተጨማሪ ይወቁ',
toggleTheme: 'ብርሃን / ጨለማ ገጽታ ቀይር',
welcome: 'እንኳን ደህና መጡ',
switchCalendar: 'የቀን መቁጠሪያ ዓይነት ቀይር',
},
auth: {

View File

@@ -79,6 +79,7 @@ export const en = {
learnMore: 'Learn more',
toggleTheme: 'Toggle light / dark mode',
welcome: 'Welcome',
switchCalendar: 'Switch calendar type',
},
auth: {

69
package-lock.json generated
View File

@@ -25,6 +25,7 @@
"country-flag-icons": "^1.6.20",
"date-fns": "^4.1.0",
"dayjs": "^1.11.20",
"ethiopian-calendar-date-converter": "^2.1.6",
"i18n-iso-countries": "^7.14.0",
"i18next": "^25.6.0",
"js-cookie": "^3.0.8",
@@ -2043,7 +2044,7 @@
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
@@ -2054,7 +2055,7 @@
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
@@ -2064,7 +2065,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
@@ -4133,6 +4134,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
"integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -8403,6 +8405,66 @@
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.10.0",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.10.0",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.4",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"peerDependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.1",
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz",
@@ -8950,6 +9012,7 @@
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {

View File

@@ -30,6 +30,7 @@
"country-flag-icons": "^1.6.20",
"date-fns": "^4.1.0",
"dayjs": "^1.11.20",
"ethiopian-calendar-date-converter": "^2.1.6",
"i18n-iso-countries": "^7.14.0",
"i18next": "^25.6.0",
"js-cookie": "^3.0.8",