feat: let users save their national code from the profile page
Wires up the previously-disabled "edit profile" button/modal to save national_id via POST /profile/national-id, so it's no longer only collectible through the inline field during a Shahkar-gated purchase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { MdLogout } from "react-icons/md";
|
import { MdEdit, MdLogout } from "react-icons/md";
|
||||||
import Button from "../../../components/button";
|
import Button from "../../../components/button";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { setToken } from "../../../redux/slices/token";
|
import { setToken } from "../../../redux/slices/token";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { LoaderIcon } from "react-hot-toast";
|
import { LoaderIcon } from "react-hot-toast";
|
||||||
import { ProfileDetailsProps } from "../../../types/props";
|
import { ProfileDetailsProps } from "../../../types/props";
|
||||||
|
import { useState } from "react";
|
||||||
|
import ModalEditProfile from "./components/ModalEditProfile";
|
||||||
|
|
||||||
const ProfileDetails = ({
|
const ProfileDetails = ({
|
||||||
data,
|
data,
|
||||||
@@ -14,7 +16,7 @@ const ProfileDetails = ({
|
|||||||
}: ProfileDetailsProps) => {
|
}: ProfileDetailsProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="relative z-[10] w-full min-h-32 max-md:min-h-20 flex flex-row-reverse max-[400px]:gap-3 justify-between flex-wrap-reverse glass items-center py-2 px-4">
|
<div className="relative z-[10] w-full min-h-32 max-md:min-h-20 flex flex-row-reverse max-[400px]:gap-3 justify-between flex-wrap-reverse glass items-center py-2 px-4">
|
||||||
<EditProfile />
|
<EditProfile nationalId={data?.result?.user_profile?.national_id} />
|
||||||
<ProductDetails
|
<ProductDetails
|
||||||
enable_config={data?.result?.enable_config}
|
enable_config={data?.result?.enable_config}
|
||||||
disable_config={data?.result?.disable_config}
|
disable_config={data?.result?.disable_config}
|
||||||
@@ -28,18 +30,22 @@ const ProfileDetails = ({
|
|||||||
|
|
||||||
export default ProfileDetails;
|
export default ProfileDetails;
|
||||||
|
|
||||||
const EditProfile = () => {
|
const EditProfile = ({ nationalId }: { nationalId?: string | null }) => {
|
||||||
// const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
return (
|
return (
|
||||||
<div className="max-[400px]:w-full w-auto h-full flex justify-end items-center gap-2">
|
<div className="max-[400px]:w-full w-auto h-full flex justify-end items-center gap-2">
|
||||||
{/* <ModalEditProfile isOpen={isOpen} setIsOpen={setIsOpen} /> */}
|
<ModalEditProfile
|
||||||
{/* <Button
|
isOpen={isOpen}
|
||||||
|
setIsOpen={setIsOpen}
|
||||||
|
nationalId={nationalId}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
extra="!py-4 max-md:!py-2 max-md:px-2"
|
extra="!py-4 max-md:!py-2 max-md:px-2"
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
>
|
>
|
||||||
<MdEdit className="scale-[1.4] max-md:scale-100" />
|
<MdEdit className="scale-[1.4] max-md:scale-100" />
|
||||||
</Button> */}
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
extra="!py-4 max-[400px]:w-full max-md:!py-2 max-md:px-2 !bg-rose-600 flex gap-1 justify-center items-center !text-white"
|
extra="!py-4 max-[400px]:w-full max-md:!py-2 max-md:px-2 !bg-rose-600 flex gap-1 justify-center items-center !text-white"
|
||||||
onClick={() => dispatch(setToken(""))}
|
onClick={() => dispatch(setToken(""))}
|
||||||
|
|||||||
@@ -1,23 +1,94 @@
|
|||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||||
import MuiModal from "../../../../common/modal";
|
import MuiModal from "../../../../common/modal";
|
||||||
import Input from "../../../../components/input";
|
import Input from "../../../../components/input";
|
||||||
|
import Alert from "../../../../components/alert/Alert";
|
||||||
|
import useAxios from "../../../../hooks/useAxios";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { AxiosError, AxiosResponse } from "axios";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { queryClient } from "../../../../main";
|
||||||
|
|
||||||
const ModalEditProfile = ({
|
const ModalEditProfile = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
|
nationalId,
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
nationalId?: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
|
const axios = useAxios();
|
||||||
|
const [nationalCode, setNationalCode] = useState(nationalId ?? "");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNationalCode(nationalId ?? "");
|
||||||
|
}, [nationalId, isOpen]);
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: ["updateNationalId"],
|
||||||
|
mutationFn: (national_id: string) => {
|
||||||
|
return axios.post("/profile/national-id", { national_id });
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["profile"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitHandler = () => {
|
||||||
|
if (nationalCode.trim().length !== 10) {
|
||||||
|
toast.error("کد ملی باید ۱۰ رقم باشد");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.promise(mutation.mutateAsync(nationalCode.trim()), {
|
||||||
|
loading: "درحال ذخیره...",
|
||||||
|
success: (res: AxiosResponse) => {
|
||||||
|
setIsOpen(false);
|
||||||
|
return `${res.data.alert}`;
|
||||||
|
},
|
||||||
|
error: (err: AxiosError) => {
|
||||||
|
let errorMessage = "یک خطای غیرمنتظره رخ داد";
|
||||||
|
if (
|
||||||
|
err.response?.data &&
|
||||||
|
typeof err.response.data === "object" &&
|
||||||
|
"alert" in err.response.data
|
||||||
|
) {
|
||||||
|
errorMessage = (err.response.data as { alert: string }).alert;
|
||||||
|
} else if (
|
||||||
|
err.response?.data &&
|
||||||
|
typeof err.response.data === "object" &&
|
||||||
|
"message" in err.response.data
|
||||||
|
) {
|
||||||
|
errorMessage = (err.response.data as { message: string }).message;
|
||||||
|
}
|
||||||
|
return errorMessage;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiModal
|
<MuiModal
|
||||||
isOpenModal={isOpen}
|
isOpenModal={isOpen}
|
||||||
setIsOpenModal={setIsOpen}
|
setIsOpenModal={setIsOpen}
|
||||||
submitText="تایید"
|
submitText="ذخیره"
|
||||||
|
submitOnClick={submitHandler}
|
||||||
title="ویرایش پروفایل"
|
title="ویرایش پروفایل"
|
||||||
>
|
>
|
||||||
<div className="relative w-full h-full px-2 md:px-4">
|
<div className="relative w-full h-full px-2 md:px-4 flex flex-col gap-3">
|
||||||
<Input type="text" label="ویرایش نام :" labelStyle="!text-start" extra="!w-full" />
|
<Alert type="warning" extra="!mb-0 text-start">
|
||||||
|
کد ملی برای احراز هویت شاهکار در خریدهای با مبلغ بالا لازم است.
|
||||||
|
</Alert>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
label="کد ملی :"
|
||||||
|
labelStyle="!text-start"
|
||||||
|
extra="!w-full"
|
||||||
|
placeholder="کد ملی ۱۰ رقمی"
|
||||||
|
value={nationalCode}
|
||||||
|
onChange={(e) =>
|
||||||
|
setNationalCode(e.target.value.replace(/[^0-9]/g, ""))
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MuiModal>
|
</MuiModal>
|
||||||
);
|
);
|
||||||
|
|||||||
1
src/types/props.d.ts
vendored
1
src/types/props.d.ts
vendored
@@ -217,6 +217,7 @@ type UserProfile = {
|
|||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
mobile: string;
|
mobile: string;
|
||||||
|
national_id?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// تعریف تایپ پیکربندیها
|
// تعریف تایپ پیکربندیها
|
||||||
|
|||||||
Reference in New Issue
Block a user