ProPhoneInput: fix validation and value

This commit is contained in:
2025-11-13 14:33:41 +03:00
parent 0764d3a641
commit a29aa896e0
4 changed files with 65 additions and 48 deletions

View File

@@ -73,8 +73,6 @@ export function GiftBottomSheet({
</Form.Item> </Form.Item>
<ProPhoneInput <ProPhoneInput
phone={giftForm.getFieldValue("receiverPhone")}
setPhone={(phone) => giftForm.setFieldValue("receiverPhone", phone)}
propName="receiverPhone" propName="receiverPhone"
label={t("address.receiverPhone")} label={t("address.receiverPhone")}
/> />
@@ -109,8 +107,6 @@ export function GiftBottomSheet({
</Form.Item> </Form.Item>
<ProPhoneInput <ProPhoneInput
phone={giftForm.getFieldValue("senderPhone")}
setPhone={(phone) => giftForm.setFieldValue("senderPhone", phone)}
propName="senderPhone" propName="senderPhone"
label={t("address.receiverPhone")} label={t("address.receiverPhone")}
/> />

View File

@@ -5,15 +5,21 @@ import { useTranslation } from "react-i18next";
import PhoneInput from "react-phone-input-2"; import PhoneInput from "react-phone-input-2";
import { useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import { ProBlack1 } from "ThemeConstants"; import { ProBlack1 } from "ThemeConstants";
import { PhoneNumberUtil } from "google-libphonenumber";
import useFormInstance from "antd/es/form/hooks/useFormInstance";
interface ProPhoneInput extends TitleProps { interface ProPhoneInput extends TitleProps {
phone: string; propName?: string;
setPhone: (phone: string) => void; label?: string;
propName?: string getValueCallback?: (value: string) => void;
label?: string
} }
const ProPhoneInput: FunctionComponent<ProPhoneInput> = ({ phone, setPhone, propName, label }) => { const ProPhoneInput: FunctionComponent<ProPhoneInput> = ({
propName,
label,
getValueCallback,
}) => {
const form = useFormInstance();
const { t } = useTranslation(); const { t } = useTranslation();
const { themeName } = useAppSelector((state) => state.theme); const { themeName } = useAppSelector((state) => state.theme);
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
@@ -23,25 +29,30 @@ const ProPhoneInput: FunctionComponent<ProPhoneInput> = ({ phone, setPhone, prop
name={propName} name={propName}
label={label} label={label}
rules={[ rules={[
{ required: true, message: "" }, { required: true, message: t("validation.phoneRequired") },
{ {
validator: (_, value) => { validator: (_, value) => {
if (!value || value.length <= 3) { // Initialize the PhoneNumberUtil instance
return Promise.reject(new Error("")); const phoneUtil = PhoneNumberUtil.getInstance();
} if (
value &&
!phoneUtil.isValidNumber(
phoneUtil.parseAndKeepRawInput(`+${value}`),
)
)
return Promise.reject(new Error(t("validation.invalidPhone")));
return Promise.resolve(); return Promise.resolve();
}, },
}, },
]} ]}
normalize={(value) => {
if (value && setPhone) {
setPhone(value);
}
return value;
}}
> >
<PhoneInputWrapper <PhoneInputWrapper
phone={phone} onChange={(value) => {
form.setFieldValue(propName, value);
getValueCallback?.(value);
}}
phone={form.getFieldValue(propName)}
themeName={themeName} themeName={themeName}
isRTL={isRTL} isRTL={isRTL}
placeholder={t("login.mobileNumber")} placeholder={t("login.mobileNumber")}
@@ -52,8 +63,17 @@ const ProPhoneInput: FunctionComponent<ProPhoneInput> = ({ phone, setPhone, prop
); );
}; };
export const PhoneInputWrapper = ({ phone, themeName, isRTL, placeholder, value, onChange, propName, label }: { export const PhoneInputWrapper = ({
phone: string; phone,
themeName,
isRTL,
placeholder,
value,
onChange,
propName,
label,
}: {
phone?: string;
themeName: string; themeName: string;
isRTL: boolean; isRTL: boolean;
placeholder: string; placeholder: string;
@@ -106,7 +126,6 @@ export const PhoneInputWrapper = ({ phone, themeName, isRTL, placeholder, value,
autoComplete: "tel", autoComplete: "tel",
type: "tel", type: "tel",
inputMode: "numeric", inputMode: "numeric",
pattern: "[0-9]*",
}} }}
/> />
</div> </div>

View File

@@ -1,23 +1,19 @@
import ProInputCard from "components/ProInputCard/ProInputCard.tsx"; import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
import ProPhoneInput from "components/ProPhoneInput"; import ProPhoneInput from "components/ProPhoneInput";
import { selectCart, updatePhone } from "features/order/orderSlice"; import { selectCart } from "features/order/orderSlice";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useAppDispatch, useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import { OrderType } from "pages/checkout/hooks/types.ts"; import { OrderType } from "pages/checkout/hooks/types.ts";
export default function PhoneCard() { export default function PhoneCard() {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const { orderType } = useAppSelector(selectCart);
const { phone, orderType } = useAppSelector(selectCart);
return ( return (
orderType !== OrderType.Gift && ( orderType !== OrderType.Gift && (
<> <>
<ProInputCard title={t("checkout.phoneNumber")}> <ProInputCard title={t("checkout.phoneNumber")}>
<ProPhoneInput <ProPhoneInput />
phone={phone}
setPhone={(phone) => dispatch(updatePhone(phone))}
/>
</ProInputCard> </ProInputCard>
</> </>
) )

View File

@@ -23,19 +23,22 @@ export default function LoginPage() {
const [sendOtp, { isLoading }] = useSendOtpMutation(); const [sendOtp, { isLoading }] = useSendOtpMutation();
const { subdomain } = useParams(); const { subdomain } = useParams();
const [phone, setPhone] = useState<string>(""); // const [phone, setPhone] = useState<string>("");
const [selectedDate, setSelectedDate] = useState<string>(""); const [selectedDate, setSelectedDate] = useState<string>("");
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const handleLogin = async () => { const handleLogin = async () => {
localStorage.setItem("userPhone", form.getFieldValue("phone")); form.validateFields().then(() => {
if (form.getFieldsValue()) if (form.getFieldsValue()) {
sendOtp(form.getFieldsValue()).then((response: any) => { localStorage.setItem("userPhone", form.getFieldValue("phone"));
message.info(t("login.OTPSentToYourPhoneNumber")); sendOtp(form.getFieldsValue()).then((response: any) => {
navigate(`/${subdomain}/otp`); message.info(t("login.OTPSentToYourPhoneNumber"));
localStorage.setItem("otp", response.data.result.otp); navigate(`/${subdomain}/otp`);
}); localStorage.setItem("otp", response.data.result.otp);
});
}
});
}; };
return ( return (
@@ -94,7 +97,11 @@ export default function LoginPage() {
/> />
</Form.Item> </Form.Item>
<Form.Item label={t("login.date")} name="date" required> <Form.Item
label={t("login.date")}
name="date"
rules={[{ required: true, message: "" }]}
>
<Input <Input
placeholder={t("login.DateOfBirth")} placeholder={t("login.DateOfBirth")}
size="large" size="large"
@@ -109,7 +116,7 @@ export default function LoginPage() {
/> />
</Form.Item> </Form.Item>
<ProPhoneInput phone={phone} setPhone={setPhone} /> <ProPhoneInput label={t("login.phone")} propName="phone" />
<Form.Item label={null}> <Form.Item label={null}>
<Button <Button
@@ -120,19 +127,18 @@ export default function LoginPage() {
style={{ style={{
width: "100%", width: "100%",
borderRadius: 1000, borderRadius: 1000,
backgroundColor: backgroundColor: isLoading
phone.length <= 3 || isLoading ? themeName === "light"
? themeName === "light" ? "rgba(233, 233, 233, 1)"
? "rgba(233, 233, 233, 1)" : DisabledColor
: DisabledColor : colors.primary,
: colors.primary,
color: "#FFF", color: "#FFF",
height: 50, height: 50,
border: "none", border: "none",
marginTop: "2rem", marginTop: "2rem",
boxShadow: "none", boxShadow: "none",
}} }}
disabled={phone.length <= 3 || isLoading} disabled={isLoading}
htmlType="submit" htmlType="submit"
> >
{t("login.sendCode")} {t("login.sendCode")}