Refactor AddressFormContent and ProfilePage to integrate CountrySelect for nationality selection and update address payload handling

This commit is contained in:
estifanos
2026-08-08 07:40:33 +00:00
parent 451e9a7f35
commit 56de3727fa
4 changed files with 20 additions and 19 deletions

View File

@@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react';
import { Select, SimpleGrid, Text, TextInput } from '@mantine/core';
import type { FieldErrors, UseFormRegister, UseFormSetValue, UseFormWatch, UseFormTrigger } from 'react-hook-form';
import { z } from 'zod';
import { ethiopianPhone, optionalEthiopianPhone } from '@ema-platform/ui';
import { ethiopianPhone, optionalEthiopianPhone, CountrySelect } from '@ema-platform/ui';
import { LocationPicker } from '../../location/components/LocationPicker';
import { useGetLocationTypesQuery } from '../../location/api/location-api';
import type { Location } from '../../location/types/location';
@@ -10,11 +10,11 @@ import type { Location } from '../../location/types/location';
export const addressSchema = z.object({
idType: z.string().min(1, 'Select ID type'),
idNumber: z.string().trim().min(1, 'Enter ID number'),
nationality: z.string().trim().min(1, 'Enter nationality'),
// Alpha-2 country code from CountrySelect; converted to a full name at submit.
nationality: z.string().min(1, 'Select nationality'),
primaryPhoneNumber: ethiopianPhone,
secondaryPhoneNumber: optionalEthiopianPhone,
email: z.string().trim().email('Invalid email').optional().or(z.literal('')),
website: z.string().trim().url('Enter a valid URL').optional().or(z.literal('')),
regionId: z.string().optional(),
cityId: z.string().optional(),
subcityId: z.string().optional(),
@@ -115,11 +115,11 @@ export function AddressFormContent({
{...register('idNumber')}
error={errors.idNumber?.message}
/>
<TextInput
<CountrySelect
label="Nationality"
placeholder="e.g. Ethiopian"
required
{...register('nationality')}
value={watch('nationality') || null}
onChange={(val) => setValue('nationality', val || '', { shouldValidate: true })}
error={errors.nationality?.message}
/>
<TextInput
@@ -144,12 +144,6 @@ export function AddressFormContent({
{...register('email')}
error={errors.email?.message}
/>
<TextInput
label="Website"
placeholder="https://example.com"
{...register('website')}
error={errors.website?.message}
/>
</SimpleGrid>
<Text fw={600} fz="sm" tt="uppercase" c="gray.6" mt="lg" mb="sm">

View File

@@ -47,7 +47,7 @@ import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useTranslation } from 'react-i18next';
import { notify, PageHeader, useErrorHandler, passwordSchema as strongPasswordSchema, PasswordRequirements } from '@ema-platform/ui';
import { notify, PageHeader, useErrorHandler, passwordSchema as strongPasswordSchema, PasswordRequirements, getCountryCode } from '@ema-platform/ui';
import { useApiMutation } from '@ema-platform/api';
import { setUser, useCurrentProfile } from '@ema-platform/auth';
import type { CurrentProfile } from '@ema-platform/auth';
@@ -201,11 +201,11 @@ export function ProfilePage() {
setLoadedAddress({
idType: currentProfile.address?.idType || '',
idNumber: currentProfile.address?.idNumber || '',
nationality: currentProfile.address?.nationality || '',
// Stored as a country name; the select works in alpha-2 codes.
nationality: getCountryCode(currentProfile.address?.nationality) || '',
primaryPhoneNumber: user?.phoneNumber || '',
secondaryPhoneNumber: currentProfile.address?.secondaryPhoneNumber || '',
email: user?.email || '',
website: currentProfile.address?.website || '',
regionId: currentProfile.address?.regionId || '',
cityId: currentProfile.address?.cityId || '',
subcityId: currentProfile.address?.subCityId || '',

View File

@@ -1,3 +1,4 @@
import { getCountryName } from '@ema-platform/ui';
import type { AddressValues } from '../components/AddressFormContent';
/** Exact request body of `POST /addresss/profile/{profileId}`. */
@@ -14,7 +15,7 @@ export interface AddressPayload {
primaryPhoneNumber: string;
secondaryPhoneNumber?: string;
email?: string;
website?: string;
website: null;
postalAddress?: string;
emergencyContactName?: string;
emergencyContactPhone?: string;
@@ -28,7 +29,8 @@ export function toAddressPayload(values: AddressValues): AddressPayload {
return {
idType: values.idType.trim(),
idNumber: values.idNumber.trim(),
nationality: values.nationality.trim(),
// Form holds the alpha-2 code (CountrySelect); API stores the full name.
nationality: getCountryName(values.nationality),
regionId: clean(values.regionId),
cityId: clean(values.cityId),
subcityId: clean(values.subcityId),
@@ -38,7 +40,7 @@ export function toAddressPayload(values: AddressValues): AddressPayload {
primaryPhoneNumber: values.primaryPhoneNumber,
secondaryPhoneNumber: clean(values.secondaryPhoneNumber),
email: clean(values.email),
website: clean(values.website),
website: null, // no website field in the form — always sent as null
postalAddress: clean(values.postalAddress),
emergencyContactName: clean(values.emergencyContactName),
emergencyContactPhone: clean(values.emergencyContactPhone),

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Group, Select, Text, type ComboboxItem, type SelectProps } from '@mantine/core';
import { registerLocale, getNames, getName } from 'i18n-iso-countries';
import { registerLocale, getNames, getName, getAlpha2Code } from 'i18n-iso-countries';
import * as Flags from 'country-flag-icons/react/3x2';
import en from 'i18n-iso-countries/langs/en.json';
import am from 'i18n-iso-countries/langs/am.json';
@@ -21,6 +21,11 @@ export function getCountryName(code: string | null | undefined, lang: CountryLan
return code ? (getName(code, lang) ?? '') : '';
}
/** Alpha-2 code for a stored country name (e.g. "Ethiopian" data); undefined when unmatched. */
export function getCountryCode(name: string | null | undefined, lang: CountryLang = 'en'): string | undefined {
return name ? getAlpha2Code(name, lang) : undefined;
}
function CountryFlag({ code }: { code: string }) {
const Flag = Flags[code as keyof typeof Flags];
return Flag ? <Flag style={{ width: 22, borderRadius: 2, display: 'block' }} /> : null;