mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
125 lines
3.3 KiB
TypeScript
125 lines
3.3 KiB
TypeScript
import { Input, TextInput } from "@mantine/core";
|
|
import {
|
|
Controller,
|
|
type Control,
|
|
type FieldValues,
|
|
type Path,
|
|
} from "react-hook-form";
|
|
import RPNInput, { isValidPhoneNumber } from "react-phone-number-input";
|
|
import "react-phone-number-input/style.css";
|
|
import "./phone-field.css";
|
|
|
|
/** Re-exported for zod `.refine()` checks on phone fields. */
|
|
export const isValidPhone = (value?: string | null): boolean =>
|
|
!!value && isValidPhoneNumber(value);
|
|
|
|
/**
|
|
* Normalize a raw (often eTrade) phone string to Ethiopian E.164 (+251…).
|
|
* eTrade returns local numbers like "0912345678" / "0355235416"; the phone
|
|
* input needs +251… to parse, so we drop a leading 0 and prepend +251. Numbers
|
|
* already in +… form, or that can't be coerced, are returned trimmed/as-is.
|
|
*/
|
|
export const toEthiopianE164 = (raw?: string | null): string => {
|
|
if (!raw) return "";
|
|
const trimmed = raw.trim();
|
|
if (trimmed.startsWith("+")) return trimmed.replace(/[^\d+]/g, "");
|
|
// Keep digits only, drop a single leading zero (national trunk prefix).
|
|
const digits = trimmed.replace(/\D/g, "").replace(/^0/, "");
|
|
if (!digits) return "";
|
|
// Already includes the 251 country code.
|
|
if (digits.startsWith("251")) return `+${digits}`;
|
|
return `+251${digits}`;
|
|
};
|
|
|
|
export interface PhoneFieldProps {
|
|
label?: string;
|
|
value?: string;
|
|
onChange: (value: string | undefined) => void;
|
|
onBlur?: () => void;
|
|
error?: string;
|
|
required?: boolean;
|
|
disabled?: boolean;
|
|
placeholder?: string;
|
|
}
|
|
|
|
/**
|
|
* Professional phone input: searchable country selector (all countries, default
|
|
* Ethiopia), live formatting, emits a single E.164 value (e.g. +251912345678).
|
|
* Visually aligned with the portal's Mantine form fields.
|
|
*/
|
|
export function PhoneField({
|
|
label,
|
|
value,
|
|
onChange,
|
|
// onBlur,
|
|
error,
|
|
required,
|
|
disabled,
|
|
placeholder = "912 345 678",
|
|
}: PhoneFieldProps) {
|
|
return (
|
|
<Input.Wrapper
|
|
label={label}
|
|
required={required}
|
|
error={error}
|
|
styles={{
|
|
label: { fontWeight: 600, fontSize: 14, color: "#10202F", },
|
|
}}
|
|
>
|
|
<div className={`edr-phone-wrapper ${error ? " edr-phone-wrapper--error" : ""}`}>
|
|
<RPNInput
|
|
international
|
|
defaultCountry="ET"
|
|
countryCallingCodeEditable={false}
|
|
addInternationalOption
|
|
value={value}
|
|
onChange={onChange}
|
|
inputComponent={TextInput}
|
|
disabled={disabled}
|
|
placeholder={placeholder}
|
|
/>
|
|
</div>
|
|
</Input.Wrapper>
|
|
);
|
|
}
|
|
|
|
interface ControlledPhoneFieldProps<T extends FieldValues> {
|
|
control: Control<T>;
|
|
name: Path<T>;
|
|
label?: string;
|
|
required?: boolean;
|
|
disabled?: boolean;
|
|
placeholder?: string;
|
|
}
|
|
|
|
/** RHF Controller wrapper so forms drop in one line. */
|
|
export function ControlledPhoneField<T extends FieldValues>({
|
|
control,
|
|
name,
|
|
label,
|
|
required,
|
|
disabled,
|
|
placeholder,
|
|
}: ControlledPhoneFieldProps<T>) {
|
|
return (
|
|
<Controller
|
|
control={control}
|
|
name={name}
|
|
render={({ field, fieldState }) => (
|
|
<PhoneField
|
|
label={label}
|
|
required={required}
|
|
disabled={disabled}
|
|
placeholder={placeholder}
|
|
value={(field.value as string) ?? ""}
|
|
onChange={(v) => field.onChange(v ?? "")}
|
|
onBlur={field.onBlur}
|
|
error={fieldState.error?.message}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export default PhoneField;
|