From 00835e373342a5ad2de69feee4f612ca8338c762 Mon Sep 17 00:00:00 2001 From: Alihaghighattalab Date: Tue, 3 Sep 2024 16:29:35 +0330 Subject: [PATCH] complete asan service gps --- package-lock.json | 20 +- package.json | 4 +- public/index.html | 52 +-- src/App.tsx | 21 ++ src/components/awards/award-not-complete.tsx | 2 +- src/components/awards/awards-complete.tsx | 17 +- src/components/awards/index.tsx | 49 ++- src/components/common/combo-box.tsx | 6 +- src/components/common/datepicker.tsx | 12 +- src/components/common/input.tsx | 7 +- src/components/common/table.tsx | 17 +- src/components/common/upload.tsx | 48 +++ src/components/forms/login-form.tsx | 9 +- src/components/home/imei.tsx | 42 ++- src/components/my-account/index.tsx | 296 ++++++++++-------- .../my-account/reset-password-box.tsx | 71 +++++ src/components/ui/dashboard/header.tsx | 16 +- .../ui/dashboard/sidebar-mobile.tsx | 3 + src/components/wallet/wallet-address.tsx | 63 +++- src/index.css | 2 +- src/index.tsx | 1 - src/schema/index.ts | 58 ++-- src/services/api/imei.ts | 5 + src/services/api/user.ts | 6 + src/services/api/wallet.ts | 3 +- src/store/user.ts | 56 ++++ src/types/index.ts | 89 ++++-- src/utility/transactions.tsx | 2 +- 28 files changed, 693 insertions(+), 284 deletions(-) create mode 100644 src/components/common/upload.tsx create mode 100644 src/components/my-account/reset-password-box.tsx create mode 100644 src/services/api/imei.ts create mode 100644 src/services/api/user.ts create mode 100644 src/store/user.ts diff --git a/package-lock.json b/package-lock.json index 77dc5c7..4d2dbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "clsx": "^2.1.1", "framer-motion": "^11.3.28", "iconsax-react": "^0.0.8", - "iranianbanklogos": "^3.1.2", "moment-jalaali": "^0.10.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -37,7 +36,8 @@ "typescript": "^4.9.5", "web-vitals": "^2.1.4", "yup": "^1.4.0", - "zustand": "^4.5.5" + "zustand": "^4.5.5", + "zustand-persist": "^0.4.0" }, "devDependencies": { "@tanstack/eslint-plugin-query": "^5.51.15", @@ -10587,12 +10587,6 @@ "node": ">= 10" } }, - "node_modules/iranianbanklogos": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/iranianbanklogos/-/iranianbanklogos-3.1.2.tgz", - "integrity": "sha512-F6GnrhrC9UO1c4CW8uJloXAkg7pgx/Y8ok6qixb6L0knwIanG6Idudi8/Ebuu3c9PVd59wDTtTZQvtszr+Yyvw==", - "license": "ISC" - }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -20313,6 +20307,16 @@ "optional": true } } + }, + "node_modules/zustand-persist": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/zustand-persist/-/zustand-persist-0.4.0.tgz", + "integrity": "sha512-u6bBIc4yZRpSKBKuTNhoqvoIb09gGHk2NkiPg4K7MPIWTYZg70PlpBn48QEDnKZwfNurnf58TaW5BuMGIMf5hw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "zustand": ">=3.6.3" + } } } } diff --git a/package.json b/package.json index 6da7e64..83b05c8 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "clsx": "^2.1.1", "framer-motion": "^11.3.28", "iconsax-react": "^0.0.8", - "iranianbanklogos": "^3.1.2", "moment-jalaali": "^0.10.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -32,7 +31,8 @@ "typescript": "^4.9.5", "web-vitals": "^2.1.4", "yup": "^1.4.0", - "zustand": "^4.5.5" + "zustand": "^4.5.5", + "zustand-persist": "^0.4.0" }, "scripts": { "start": "react-scripts start", diff --git a/public/index.html b/public/index.html index aa069f2..fc0e57b 100644 --- a/public/index.html +++ b/public/index.html @@ -1,43 +1,19 @@ - - - - - - - - - - - React App - - - -
- - - + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 9159cec..4663553 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,29 @@ +import { useMutation } from "@tanstack/react-query"; import { Imei } from "./components/home/imei"; import { StatusBox } from "./components/home/status-box"; +import { getUsersInformation } from "./services/api/user"; import { TransitionPages } from "./transition"; +import { useUserStore } from "./store/user"; +import { handleError } from "./utility/error-handle"; +import { useEffect } from "react"; function App() { + const { updateUser } = useUserStore() + const { mutate: userMutate } = useMutation({ + mutationFn: getUsersInformation, + mutationKey: ["user"] + }) + const handleGetUsersInformation = async () => { + await userMutate({}, { + onSuccess: (res: any) => { + updateUser(res?.data) + }, + onError: (err: any) => handleError(err) + }) + } + useEffect(() => { + handleGetUsersInformation() + }, []) return (
diff --git a/src/components/awards/award-not-complete.tsx b/src/components/awards/award-not-complete.tsx index 79813d8..a9e612d 100644 --- a/src/components/awards/award-not-complete.tsx +++ b/src/components/awards/award-not-complete.tsx @@ -21,8 +21,8 @@ export const AwardNotComplete: FC = ({ award }) => {

{award?.desc?.length >= 70 ? `${award?.desc?.slice(0, 70)} ...` : award?.desc}

-

{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}

+

{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}

diff --git a/src/components/awards/awards-complete.tsx b/src/components/awards/awards-complete.tsx index 7f2f1c8..52df6b8 100644 --- a/src/components/awards/awards-complete.tsx +++ b/src/components/awards/awards-complete.tsx @@ -1,16 +1,21 @@ import { Cup, Transmit } from "iconsax-react" +import { AwardsResponse } from "../../types" +import { FC } from "react" +import { formatDateToPersian } from "../../utility/getDate" - -export const AwardComplete = () => { +type Props = { + award: AwardsResponse +} +export const AwardComplete: FC = ({ award }) => { return ( -
+
-

۲۵۰ هزار تومان تخفیف برای اقامت در تمام ویلاها و کلبه های جاجیگا با حداقل رزرو ۲ میلیون تومان.

-

1403/06/12

+

{award?.req?.desc}

+

{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}

-

75

+

{award?.score}

diff --git a/src/components/awards/index.tsx b/src/components/awards/index.tsx index 3b7b563..298b40d 100644 --- a/src/components/awards/index.tsx +++ b/src/components/awards/index.tsx @@ -1,31 +1,48 @@ -import { useQuery } from "@tanstack/react-query" -import { AwardNotComplete } from "./award-not-complete" -import { getAwardsList } from "../../services/api/awards" -import { AwardsResponse } from "../../types" -import { AwardSkeleton } from "./award-skeleton" -import { handleError } from "../../utility/error-handle" -import { useEffect } from "react" +import { useQuery } from "@tanstack/react-query"; +import { AwardNotComplete } from "./award-not-complete"; +import { getAwardsList } from "../../services/api/awards"; +import { AwardsResponse, AwardStatus } from "../../types"; +import { AwardSkeleton } from "./award-skeleton"; +import { handleError } from "../../utility/error-handle"; +import { useEffect, useState } from "react"; +import { AwardComplete } from "./awards-complete"; +import { useUserStore } from "../../store/user"; export const Awards = () => { + const { user } = useUserStore() + const [status, setStatus] = useState({ + awards: 0, + my_score: 0 + }); const { data, isLoading, error } = useQuery({ queryKey: ["awards"], queryFn: getAwardsList - }) + }); useEffect(() => { - handleError(error) - }, [error]) + handleError(error); + }, [error]); - if (isLoading) return + useEffect(() => { + const awardsTemp: number = data?.data?.length; + const tempScores: any = data?.data?.filter((award: AwardsResponse) => award?.status === 1); + const myScore = tempScores?.reduce((totalScore: number, award: AwardsResponse) => totalScore + award?.score, 0); + setStatus({ + awards: awardsTemp, + my_score: myScore + }); + }, [data]); + + if (isLoading) return ; return (
-

جوایز (5)

-

امتیازهای من: 65

+

جوایز ({status?.awards})

+

امتیازهای من: {status?.my_score}

- {data?.data?.map((award: AwardsResponse) => )} + {data?.data?.map((award: AwardsResponse, index: number) => award?.status === 0 ? : )}
- ) -} \ No newline at end of file + ); +}; diff --git a/src/components/common/combo-box.tsx b/src/components/common/combo-box.tsx index 2534bfb..95a0cbf 100644 --- a/src/components/common/combo-box.tsx +++ b/src/components/common/combo-box.tsx @@ -32,7 +32,7 @@ export const ComboBox: FC = ({ field, placeholder, className, data = [],
person?.name} onChange={(event) => setQuery(event.target.value)} /> @@ -45,9 +45,9 @@ export const ComboBox: FC = ({ field, placeholder, className, data = [], transition className="bg-white shadow min-w-[220px] !max-h-[300px] mt-2 rounded-xl p-2" > - {filteredValue.map((value) => ( + {filteredValue.map((value, index: number) => ( diff --git a/src/components/common/datepicker.tsx b/src/components/common/datepicker.tsx index 7aac5bf..5e0c8f9 100644 --- a/src/components/common/datepicker.tsx +++ b/src/components/common/datepicker.tsx @@ -6,13 +6,14 @@ import persian_fa from "react-date-object/locales/persian_fa" import { getJalaaliDate } from '../../utility/getDate'; type Props = { - field: any + field: any, + placeholder?: string } -export const DatepickerComponent: FC = ({ field }) => { +export const DatepickerComponent: FC = ({ field, placeholder }) => { return (
-
+
= ({ field }) => { value={field.value} onChange={field.onChange} showOtherDays - placeholder={getJalaaliDate()} + placeholder={placeholder ? placeholder : getJalaaliDate()} calendar={persian} locale={persian_fa} calendarPosition="bottom-right" - inputClass='w-full !min-w-full absolute input-style top-0 z-10 cursor-pointer' + arrow={false} + inputClass={`w-full !min-w-full absolute input-style top-0 cursor-pointer`} />
) diff --git a/src/components/common/input.tsx b/src/components/common/input.tsx index f9084a1..a49097b 100644 --- a/src/components/common/input.tsx +++ b/src/components/common/input.tsx @@ -11,10 +11,11 @@ type Props = { field?: any, className?: string, value?: string, - onChange?: (value: string) => void + onChange?: (value: string) => void, + disabled?: boolean } -export const Input: FC = ({ placeholder, type = "text", icon, field, className, value, onChange }) => { +export const Input: FC = ({ placeholder, type = "text", icon, field, className, value, onChange, disabled = false }) => { const [hidden, setHidden] = useState(false) const handleHidden = () => setHidden(prev => !prev) return ( @@ -27,7 +28,7 @@ export const Input: FC = ({ placeholder, type = "text", icon, field, clas : icon} {placeholder === "شماره کارت" && field.value && findBankByPAN(field.value).icon}
-
) } \ No newline at end of file diff --git a/src/components/common/table.tsx b/src/components/common/table.tsx index 7076dfb..9d14d96 100644 --- a/src/components/common/table.tsx +++ b/src/components/common/table.tsx @@ -1,4 +1,4 @@ -import { flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from "@tanstack/react-table" +import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table" import { ArrowDown2, ArrowLeft2, ArrowRight2, ArrowUp2, SearchNormal1 } from "iconsax-react" import { FC, useEffect, useState } from "react" import { Input } from "./input" @@ -24,20 +24,17 @@ type ADSortType = { export const Table: FC = ({ columns, data, tableStatus = false, tableScore = 0, isLoading = false, totalData = 0 }) => { const [aDSort, setADSort] = useState({ key: "", - value: 1 + value: -1 }) const { updateParams, params, clearParams } = useParamsStore() const totalPages = Math.ceil(totalData / params?.rows) const table = useReactTable({ data: data ? data : [], columns: columns ? columns : [], - debugTable: true, getCoreRowModel: getCoreRowModel(), enableColumnResizing: true, columnResizeMode: 'onChange', - debugHeaders: true, - debugColumns: true, - getPaginationRowModel: getPaginationRowModel() + pageCount: params?.rows, }) const handleInputValue = (value: string) => { @@ -71,14 +68,16 @@ export const Table: FC = ({ columns, data, tableStatus = false, tableScor
- handleInputValue(e?.target?.value)} className="bg-auth-form placeholder:text-[#777577]" icon={} /> + { + handleInputValue(e?.target?.value) + }} className="bg-auth-form placeholder:text-[#777577]" icon={} />
{tableStatus && `امتیاز من : ${tableScore}`}
-
+
{table.getHeaderGroups().map((headerGroup: any, index: number) => ( @@ -92,7 +91,7 @@ export const Table: FC = ({ columns, data, tableStatus = false, tableScor }}>
{flexRender(header.column.columnDef.header, header.getContext())} - {{ asc: , desc: }[header.column.getIsSorted() as string] ?? null} + {aDSort?.value === 1 && aDSort?.key === header.getContext()?.column?.id ? : }
))} diff --git a/src/components/common/upload.tsx b/src/components/common/upload.tsx new file mode 100644 index 0000000..c7ba5a9 --- /dev/null +++ b/src/components/common/upload.tsx @@ -0,0 +1,48 @@ +import { Trash } from 'iconsax-react'; +import { FC } from 'react'; + +type Props = { + setSelectedImage: (file: any) => any, + selectedImage: any +} + +export const ImageUpload: FC = ({ selectedImage, setSelectedImage }) => { + const handleImageChange = (event: any) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = () => { + setSelectedImage(file); + }; + reader.readAsDataURL(file); + } + }; + + const handleImageDelete = () => { + setSelectedImage(null); + }; + + return ( +
+ + +
+ ); +} \ No newline at end of file diff --git a/src/components/forms/login-form.tsx b/src/components/forms/login-form.tsx index a61871c..2a32e28 100644 --- a/src/components/forms/login-form.tsx +++ b/src/components/forms/login-form.tsx @@ -15,8 +15,10 @@ export const LoginForm = () => { const navigate = useNavigate() const { mutate, isPending } = useMutation({ - mutationFn: loginMutation + mutationFn: loginMutation, + mutationKey: ["login"] }) + const { handleSubmit, formState: { errors }, @@ -24,12 +26,13 @@ export const LoginForm = () => { } = useForm({ resolver: yupResolver(loginSchema) }) + const handleLoginSubmit: SubmitHandler = async (data) => { - mutate({ + await mutate({ ...data, remember_me: true }, { - onSuccess: (data: any) => { + onSuccess: async (data: any) => { window.localStorage.setItem("token", data?.data?.token) toast.success("با موفقیت وارد شدین") navigate("/") diff --git a/src/components/home/imei.tsx b/src/components/home/imei.tsx index 4f19850..900d8fc 100644 --- a/src/components/home/imei.tsx +++ b/src/components/home/imei.tsx @@ -1,21 +1,55 @@ import { Button } from "@headlessui/react" import { Input } from "../common/input" +import { useMutation } from "@tanstack/react-query" +import { checkIMEI } from "../../services/api/imei" +import { ButtonComponent } from "../common/button" +import { Controller, SubmitHandler, useForm } from "react-hook-form" +import { CheckIMEI } from "../../types" +import { yupResolver } from "@hookform/resolvers/yup" +import { checkIMEISchema } from "../../schema" +import { ErrorComponent } from "../common/error" +import toast from "react-hot-toast" +import { handleError } from "../../utility/error-handle" export const Imei = () => { + const { + handleSubmit, + formState: { errors }, + control + } = useForm({ + resolver: yupResolver(checkIMEISchema) + }) + const { mutate, isPending } = useMutation({ + mutationFn: checkIMEI, + mutationKey: ["IMEI"] + }) + const handleCheckIMEI: SubmitHandler = async (data) => { + await mutate(data, { + onSuccess: (res: any) => { + toast.success("کد IMEI معتبر است") + }, + onError: (err: any) => handleError(err) + }) + } return ( -
+
binary
- + ( +
+ + +
+ )} />

لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز

- +
-
+ ) } \ No newline at end of file diff --git a/src/components/my-account/index.tsx b/src/components/my-account/index.tsx index 75130b2..32cd9c6 100644 --- a/src/components/my-account/index.tsx +++ b/src/components/my-account/index.tsx @@ -1,142 +1,194 @@ -import { Button } from "@headlessui/react" import { Input } from "../common/input" import { DatepickerComponent } from "../common/datepicker" import { Controller, SubmitHandler, useForm } from "react-hook-form" -import { MyAccountInterface } from "../../types" +import { CitiesType, ComboBoxItems, ProvinceType, UpdateUserInterface } from "../../types" import { yupResolver } from "@hookform/resolvers/yup" -import { MyAccountSchema } from "../../schema" +import { UpdateUserSchema } from "../../schema" import { ErrorComponent } from "../common/error" import { ComboBox } from "../common/combo-box" +import { ResetPasswordBox } from "./reset-password-box" +import { useEffect, useState } from "react" +import { getProvincesList } from "../../services/api/province" +import { useMutation, useQuery } from "@tanstack/react-query" +import { getCitiesList } from "../../services/api/cities" +import { useUserStore } from "../../store/user" +import { ButtonComponent } from "../common/button" +import { updateUSerInformations } from "../../services/api/user" +import toast from "react-hot-toast" +import { handleError } from "../../utility/error-handle" +import { ImageUpload } from "../common/upload" export const MyAccountPage = () => { - const { handleSubmit, formState: { errors }, control } = useForm({ - resolver: yupResolver(MyAccountSchema) + const [selectedImage, setSelectedImage] = useState(null); + const { user, updateUser } = useUserStore() + const { mutate, isPending } = useMutation({ + mutationFn: updateUSerInformations, + mutationKey: ["update-user"] + }) + const { handleSubmit, formState: { errors }, control, watch, reset } = useForm({ + resolver: yupResolver(UpdateUserSchema), + }) + const provinceName: any = watch("province_name"); + const [provinceData, setProvinceData] = useState([]) + const [citiesData, setCitiesData] = useState([]) + const { data: provinces } = useQuery({ + queryFn: getProvincesList, + queryKey: ["province"] + }) + const { data: cities } = useQuery({ + queryFn: getCitiesList, + queryKey: ["cities"] }) - const handleMyAccountForm: SubmitHandler = async (data) => { - console.log("data =>", data) + const handleUpdateUser: SubmitHandler = async (data) => { + let formData = new FormData() + formData.append("first_name", data?.first_name) + formData.append("last_name", data?.last_name) + formData.append("province_name", data?.province_name?.name) + formData.append("city_name", data?.city_name?.name) + formData.append("shopName", data?.shopName) + formData.append("birthDate", data?.birthDate) + formData.append("address", data?.address) + formData.append("cell_number", data?.cell_number) + formData.append("image", selectedImage) + await mutate({ formData, id: user?.id }, { + onSuccess: (res: any) => { + toast?.success("اطلاعات کاربر با موفقیت بروز رسانی شد") + updateUser(res?.data?.user) + }, + onError: (err: any) => handleError(err) + }) } + useEffect(() => { + const tempArray: ComboBoxItems[] = [...provinceData] + provinces?.data?.data?.map((province: ProvinceType) => tempArray.push({ + id: province?.ProvinceID, + name: province?.ProvinceName + })) + setProvinceData(tempArray) + }, [provinces]) + + useEffect(() => { + const tempCities: any = [] + const newCities = cities?.data?.data?.filter((city: CitiesType) => city?.ProvinceName?.toLocaleLowerCase()?.includes(provinceName?.name?.toLowerCase())) + newCities?.map((city: CitiesType) => tempCities?.push({ + id: city?.CityID, + name: city?.CityName + })) + setCitiesData(tempCities) + }, [provinceName]); + + + useEffect(() => { + reset({ + address: user?.address, + birthDate: user?.birthDate, + city_name: { id: 1, name: user?.city_name }, + province_name: { id: 1, name: user?.province_name }, + mobile_number: user?.mobile_number, + first_name: user?.first_name, + last_name: user?.last_name, + national_code: user?.national_code, + shopName: user?.shopName, + cell_number: user?.cell_number, + }) + }, [reset]) return ( -
-
-
-

اطلاعات اولیه

-

- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون -

-
-
-
- ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> +
+ +
+
+

اطلاعات اولیه

+

+ لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون +

-
-
-
-
-
-

اطلاعات تکمیلی

-

- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون -

-
-
-
- ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> - ( -
- - -
- )} /> +
+
+ ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + +
+
-
-
-
-
-

تغییر رمز عبور

-

- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون -

+
+
+

اطلاعات تکمیلی

+

+ لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون +

+
+
+
+ ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> +
+
+
-
- ( -
- - -
- )} /> -
- ( -
- - -
- )} /> - ( -
- - -
- )} /> +
+
-
-
- -
- + + +
) } \ No newline at end of file diff --git a/src/components/my-account/reset-password-box.tsx b/src/components/my-account/reset-password-box.tsx new file mode 100644 index 0000000..13246d4 --- /dev/null +++ b/src/components/my-account/reset-password-box.tsx @@ -0,0 +1,71 @@ +import { Controller, SubmitHandler, useForm } from "react-hook-form" +import { ResetPasswordUserSchema } from "../../schema" +import { ResetPasswordUser } from "../../types" +import { yupResolver } from "@hookform/resolvers/yup" +import { ErrorComponent } from "../common/error" +import { Input } from "../common/input" +import { ButtonComponent } from "../common/button" +import { useMutation } from "@tanstack/react-query" +import { changeUserPassword } from "../../services/api/user" +import toast from "react-hot-toast" +import { handleError } from "../../utility/error-handle" + +export const ResetPasswordBox = () => { + const { mutate, isPending } = useMutation({ + mutationFn: changeUserPassword, + mutationKey: ["user-password"] + }) + const { handleSubmit, formState: { errors }, control } = useForm({ + resolver: yupResolver(ResetPasswordUserSchema) + }) + + const handleResetUserPassword: SubmitHandler = async (data) => { + await mutate({ + ...data + }, + { + onSuccess: () => { + toast.success("تغییر رمز عبور با موفقیت انجام شد") + }, + onError: (err: any) => { + handleError(err) + } + }) + } + return ( +
+
+
+

تغییر رمز عبور

+

+ لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون +

+
+
+ ( +
+ + +
+ )} /> +
+ ( +
+ + +
+ )} /> + ( +
+ + +
+ )} /> +
+
+
+ +
+ + ) +} \ No newline at end of file diff --git a/src/components/ui/dashboard/header.tsx b/src/components/ui/dashboard/header.tsx index 67e7ba6..23aff4f 100644 --- a/src/components/ui/dashboard/header.tsx +++ b/src/components/ui/dashboard/header.tsx @@ -1,20 +1,26 @@ -import { Call } from "iconsax-react" -import React, { FC } from "react" +import { Call, Profile } from "iconsax-react" +import { FC } from "react" +import { useUserStore } from "../../../store/user" type Props = { setShow: () => void } export const Header: FC = ({ setShow }) => { + const { user } = useUserStore() return (
burger menu - person -

علی مصلحی

+ {user?.profilePic !== "noPic" ? person :
+
} +

{user?.first_name} {user?.last_name}

-

021-45597000

+

{user?.mobile_number}

diff --git a/src/components/ui/dashboard/sidebar-mobile.tsx b/src/components/ui/dashboard/sidebar-mobile.tsx index e5fb658..8c26f59 100644 --- a/src/components/ui/dashboard/sidebar-mobile.tsx +++ b/src/components/ui/dashboard/sidebar-mobile.tsx @@ -4,6 +4,7 @@ import { sidebarItems } from "../../../utility/sidebar"; import { SidebarItem } from "./sidebar-item"; import { CloseCircle, LogoutCurve } from "iconsax-react"; import { useNavigate } from "react-router-dom"; +import { useUserStore } from "../../../store/user"; type Props = { setShow: (value: boolean) => void, @@ -11,11 +12,13 @@ type Props = { }; export const SidebarMobile: FC = ({ setShow, show }) => { + const { clearUser } = useUserStore() const navigate = useNavigate() const handleCloseSidebar = () => setShow(false); const handleExit = () => { window.localStorage.clear() navigate("/auth/login") + clearUser() } return ( <> diff --git a/src/components/wallet/wallet-address.tsx b/src/components/wallet/wallet-address.tsx index ba394a9..26a474c 100644 --- a/src/components/wallet/wallet-address.tsx +++ b/src/components/wallet/wallet-address.tsx @@ -1,21 +1,72 @@ -import { Button } from "@headlessui/react" import { ComboBox } from "../common/combo-box" -import { FC } from "react" +import { FC, useEffect, useState } from "react" +import { useUserStore } from "../../store/user" +import { ButtonComponent } from "../common/button" +import { Controller, SubmitHandler, useForm } from "react-hook-form" +import { CardBank, SendWithdraw } from "../../types" +import { ErrorComponent } from "../common/error" +import { useMutation } from "@tanstack/react-query" +import { sendwithdraw } from "../../services/api/wallet" +import toast from "react-hot-toast" +import { handleError } from "../../utility/error-handle" type Props = { pending: boolean } export const WalletAddress: FC = ({ pending }) => { + const [selectedCard, setSelectedCard] = useState(null) + const { mutate, isPending } = useMutation({ + mutationFn: sendwithdraw, + mutationKey: ["withdraw"] + }) + const { handleSubmit, formState: { errors }, control, watch } = useForm({ + }) + const cardDetail: any = watch("card") + const [userBanks, setUserBanks] = useState([]) + const { user } = useUserStore() + useEffect(() => { + const tempUserBanks: any = [...userBanks] + user?.cardBank?.map((cards: any) => tempUserBanks.push({ + id: cards?.IBAN, + name: cards?.PAN, + })) + setUserBanks(tempUserBanks) + + }, [user]) + useEffect(() => { + const selectedCard: CardBank | CardBank[] | [] = user?.cardBank?.filter((cardBanks: any) => cardBanks?.IBAN === cardDetail?.id && cardBanks?.PAN === cardDetail?.name) + setSelectedCard(selectedCard?.[0]) + }, [cardDetail]) + const handleSendWithdraw: SubmitHandler = async () => { + await mutate({ + card: { + holderName: selectedCard?.holderName, + PAN: selectedCard?.PAN, + IBAN: selectedCard?.IBAN + } + }, { + onSuccess: (res: any) => { + toast.success("درخواست شما با موفقیت ثبت شد") + }, + onError: (err: any) => handleError(err) + }) + } return ( -
+
wallet

ثبت درخواست برداشت از حساب

- {!pending && } + + {!pending && ( + <> + + + + )} />}
{!pending &&

لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز @@ -24,9 +75,9 @@ export const WalletAddress: FC = ({ pending }) => { {`همکار گرامی طبق درخواست قبلی، عملیات واریز مبلغ 765,975 تومان به حساب شما در حال اجراست. متاسفانه پیش از تکمیل درخواست، امکان شروع عملیات جدید برای شما وجود ندارد.`}

}
- +
-
+ ) } \ No newline at end of file diff --git a/src/index.css b/src/index.css index 113ccf3..a0019a6 100644 --- a/src/index.css +++ b/src/index.css @@ -36,7 +36,7 @@ } button { - @apply text-base font-medium flex w-full justify-center bg-primary-color max-w-[442px] max-h-[55px] rounded-[20px] p-[18px] text-white + @apply text-base font-medium flex w-full justify-center bg-primary-color max-h-[55px] rounded-[20px] p-[18px] text-white } table { diff --git a/src/index.tsx b/src/index.tsx index 6f346e1..ec9dcb8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,7 +3,6 @@ import ReactDOM from 'react-dom/client'; import { RouterProvider } from 'react-router-dom'; import { router } from './router'; import './index.css' -import "iranianbanklogos/dist/ibl.css"; import { Toaster } from 'react-hot-toast'; import { RQProvider } from './rq-provider'; diff --git a/src/schema/index.ts b/src/schema/index.ts index c41bc2b..4744522 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -76,34 +76,23 @@ export const RegisterSchema = yup.object({ .required("تکرار رمز عبور الزامی میباشد"), }) -export const MyAccountSchema = yup.object({ - phoneNumber: yup.string() - .required("وارد کردن شماره تماس الزامی میباشد") +export const UpdateUserSchema = yup.object({ + first_name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"), + last_name: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"), + city_name: yup.object().required("وارد کردن شهر الزامی مباشد"), + province_name: yup.object().required("وارد کردن استان الزامی مباشد"), + shopName: yup.string().required("وارد کردن نام فروشگاه الزامی میباشد").min(2, "نام فروشگاه نمیتواند کمتر از 3 کاراکتر باشد").max(30, "نام فروشگاه نمیتواند بیشتر از 30 کاراکتر باشد"), + birthDate: yup.string().required("وارد کردن تاریخ تولد الزامی میباشد"), + address: yup.string().required("وارد کردن آدرس الزامی میباشد").min(5, "آدرس نمیتواند کمتر از 5 کاراکتر باشد").max(50, "آدرس نمیتواند بیشتر از از 50 کاراکتر باشد"), + cell_number: yup.string() + .min(11, "شماره تلفن ثابت نمیتوند کمتر از 11 رقم باشد") + .max(11, "شماره ثابت نمیتواند بیشتر از 11 رقم باشد") + .matches(/^[0-9]{11}$/, "شماره تماس نامعتبر").required("وارد کردن شماره تلفن ثابت الزامی میباشد"), + national_code: yup.string().min(10, "کد ملی نمیتواند کمتر از 10 کاراکتر باشد").max(10, "کد ملی نمیتونه بیشتر از 10 کاراکتر باشه").matches(/^[0-9]{10}$/, "کد نامعتبر"), + mobile_number: yup.string() .min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد") .max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد") .matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"), - marketName: yup.string().required("وارد کردن نام فروشگاه الزامی میباشد").min(2, "نام فروشگاه نمیتواند کمتر از 3 کاراکتر باشد").max(30, "نام فروشگاه نمیتواند بیشتر از 30 کاراکتر باشد"), - name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"), - family: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"), - city: yup.object().required("وارد کردن شهر الزامی مباشد"), - capCity: yup.object().required("وارد کردن استان الزامی مباشد"), - address: yup.string().required("وارد کردن آدرس الزامی میباشد"), - phoneNumber1: yup.string() - .required("وارد کردن شماره تماس الزامی میباشد") - .min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد") - .max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد") - .matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"), - staticNumber: yup.string() - .required("وارد کردن شماره تماس الزامی میباشد") - .min(8, "شماره تماس نمیتوند کمتر از 8 رقم باشد") - .max(8, "شماره تماس نمیتواند بیشتر از 8 رقم باشد") - .matches(/^[0-9]{8}$/, "شماره تماس نامعتبر"), - date: yup.string().required("وارد کردن تاریخ الزامی میباشد"), - currentPassword: yup.string().required("وارد کردن رمز عبور فعلی الزامی میباشد"), - newPassword: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"), - repeatPassword: yup.string() - .oneOf([yup.ref('newPassword')], "رمز عبور و تکرار آن باید یکسان باشند") - .required("تکرار رمز عبور الزامی میاشد"), }) export const AddNewCardSchema = yup.object({ @@ -119,4 +108,23 @@ export const AddNewCardSchema = yup.object({ .min(26, "شماره شبا نمیتوند کمتر از 26 رقم باشد") .max(26, " شماره شبا نمیتواند بیشتر از 26 رقم باشد") .matches(/^IR[0-9]{24}$/, "شماره شبا نامعتبر"), +}) + +export const ResetPasswordUserSchema = yup.object({ + password: yup.string().min(8, "رمز عبور نمی‌تواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی می‌باشد"), + newPassword: yup.string().min(8, "رمز عبور جدید نمی‌تواند کمتر از 8 کاراکتر باشد") + .notOneOf([yup.ref('password')], "رمز عبور جدید نباید با رمز عبور فعلی یکسان باشد") + .required("رمز عبور جدید الزامی می‌باشد"), + password_confirmation: yup.string() + .oneOf([yup.ref('newPassword')], "رمز عبور جدید و تکرار آن باید یکسان باشند") + .required("تکرار رمز عبور الزامی می‌باشد"), +}); + +export const SendWithdrawSchema = yup.object({ + holderName: yup.string().required("وارد کردن اطلاعات صاحب کارت الزامی میباشد"), + card: yup.object(), +}) + +export const checkIMEISchema = yup.object({ + IMEI: yup.string().required("وارد کردن اطلاعات IMEI الزامی میباشد").min(15,"شناسه IMEI نمیتواند کمتر از 15 کاراکتر باشد").max(15,"شناسه IMEI نمیتواند بیشتر از 15 کاراکتر باشد"), }) \ No newline at end of file diff --git a/src/services/api/imei.ts b/src/services/api/imei.ts new file mode 100644 index 0000000..a53e17e --- /dev/null +++ b/src/services/api/imei.ts @@ -0,0 +1,5 @@ +import { CheckIMEI } from "../../types"; +import axiosInstance from "../axios"; + + +export const checkIMEI = (payload: CheckIMEI) => axiosInstance.post("gps/register-device", payload) \ No newline at end of file diff --git a/src/services/api/user.ts b/src/services/api/user.ts new file mode 100644 index 0000000..3a775ac --- /dev/null +++ b/src/services/api/user.ts @@ -0,0 +1,6 @@ +import { ResetPasswordUser } from "../../types"; +import axiosInstance from "../axios"; + +export const getUsersInformation = ({ }: any) => axiosInstance.get("/gps/user/me") +export const changeUserPassword = (payload: ResetPasswordUser) => axiosInstance.post("/gps/user/reset-pass", payload) +export const updateUSerInformations = (payload: any) => axiosInstance.put(`/gps/user/me/${payload?.id}`, payload?.formData) \ No newline at end of file diff --git a/src/services/api/wallet.ts b/src/services/api/wallet.ts index 5ddf5bf..8ceefc6 100644 --- a/src/services/api/wallet.ts +++ b/src/services/api/wallet.ts @@ -1,5 +1,6 @@ + import axiosInstance from "../axios"; import { OverviewResponse } from "./overview"; export const getWalletDetails = () => axiosInstance.get("/gps/overview/wallet") - +export const sendwithdraw = (payload: any) => axiosInstance.post("/gps/withdraw", payload) diff --git a/src/store/user.ts b/src/store/user.ts new file mode 100644 index 0000000..1100fb3 --- /dev/null +++ b/src/store/user.ts @@ -0,0 +1,56 @@ +import { create } from 'zustand'; +import { UserInterface } from '../types'; +import { persist } from "zustand/middleware"; + +type ZustandState = { + user: UserInterface; + updateUser: (userData: Partial) => void; + clearUser: () => void; +}; + +const defaultParams: UserInterface = { + cardBank: [], + city_name: "", + created_at: "", + dollarBalance: "", + first_name: "", + id: "", + last_name: "", + mobile_number: "", + national_code: "", + profilePic: "", + province_name: "", + score: 0, + shopName: "", + updated_at: "", + walletBalance: 0, + active: true, + _id: "", + address: "", + birthDate: "", + cell_number: "", +} +export const useUserStore = create()( + persist( + (set) => ({ + user: defaultParams, + updateUser: (userData: Partial) => + set((state) => ({ + ...state, + user: { + ...state.user, + ...userData, + }, + })), + clearUser: () => + set((state) => ({ + ...state, + user: defaultParams, + })), + }), + { + name: 'user-store', + getStorage: () => localStorage + } + ) +); \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 65ed87e..9359bc4 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -67,25 +67,21 @@ export interface StatusBoxItemInterface { detail?: boolean } -export interface MyAccountInterface { - phoneNumber: string, - marketName: string, - name: string, - family: string, - city: object, - capCity: object, - address: string, - phoneNumber1: string, - staticNumber: string, - date: string, - currentPassword: string, - newPassword: string, - repeatPassword: string +export interface UpdateUserInterface { + id?: any, + first_name: string + last_name: string + city_name: object | any + province_name: object | any + shopName: string + birthDate: string + address: string + cell_number: string, + national_code?: string, + mobile_number?: string, + image?: any } -type BankName = "saderat" | "mehr" | "pasargad" -type PersianBankName = "مهر" | "صادرات" | "پاسارگاد" - export interface CardBank { _id: string, holderName: string, @@ -207,15 +203,17 @@ export interface RegisterDeviceId { } export interface AwardsResponse { - _id: string, score: number, title: string, - desc: string, - expireDate: string, - created_at: string, - updated_at: string, - __v: number, - id: string + desc: string + req: { + userId: string, + desc: string, + metaData: null | any, + status: number + }, + status: number, + expireDate: string } interface TransactionsData { @@ -312,4 +310,47 @@ export type CitiesType = { export interface CitiesResponse { data: CitiesType[] | [], error: string | null | any +} + +export interface UserInterface { + cardBank: CardBank[] | [], + city_name: string, + created_at: string, + dollarBalance: string, + first_name: string, + id: string, + last_name: string, + mobile_number: string, + national_code: string, + profilePic: string, + province_name: string + score: number + shopName: string + updated_at: string + walletBalance: number + active: boolean, + _id: string, + address: string, + birthDate: string, + cell_number: string +} + +export interface ResetPasswordUser { + password: string, + newPassword: string, + password_confirmation: string +} + +export interface AwardStatus { + awards: number, + my_score: number +} + +export interface SendWithdraw { + holderName: string, + card: any +} + +export interface CheckIMEI { + IMEI: string } \ No newline at end of file diff --git a/src/utility/transactions.tsx b/src/utility/transactions.tsx index f3b591a..84d8f10 100644 --- a/src/utility/transactions.tsx +++ b/src/utility/transactions.tsx @@ -19,7 +19,7 @@ const columnConfigs: colConfig[] | [] = [
}, { accessor: "created_at", header: "تاریخ درخواست", cellRenderer: (info) => info.getValue() &&

{formatDateToPersian(info.getValue())}

}, - { accessor: "id", header: "شماره پیگیری" }, + { accessor: "trackingNumber", header: "شماره پیگیری" }, { accessor: "updated_at", header: "تاریخ واریز", cellRenderer: (info) => info.getValue() &&

{formatDateToPersian(info.getValue())}

}, ];