mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 13:02:50 +00:00
feat: integrate AmharicDatePicker component across multiple pages and update translations
This commit is contained in:
@@ -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();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 ?? ''} />
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -81,6 +81,7 @@ export const am: Translations = {
|
||||
learnMore: 'ተጨማሪ ይወቁ',
|
||||
toggleTheme: 'ብርሃን / ጨለማ ገጽታ ቀይር',
|
||||
welcome: 'እንኳን ደህና መጡ',
|
||||
switchCalendar: 'የቀን መቁጠሪያ ዓይነት ቀይር',
|
||||
},
|
||||
|
||||
auth: {
|
||||
|
||||
@@ -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
69
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user