complete asan service gps
This commit is contained in:
Generated
+12
-8
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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",
|
||||
|
||||
+14
-38
@@ -1,43 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcBAMAAACAI8KnAAAAMFBMVEVHcEwHUJAHUpIGUpMGUpMNR4MHUpMGVJUIUZEIUZEIUZELTIoMTYwKT44GU5QHUpLtfyqAAAAAEHRSTlMAUJPA2BKu/4BebyM0QO6fto/c2wAAARNJREFUeAFF0QFEQ1EUh/FvtbdVe2uTCKDCCmSIBYgUgYYYUAFDMNvaf7MwYBoYEINSqIAHAhAwgAeMQFEglMiA5Lg9+4Cfc91zuWAtbO6sEOWVGxeDcqOIq3sK8Fh1irfx73shRy9Yb6QkKUsZG27jG/skbNyl5/jMAXjnsZbjKCwFJPLJVtqxksmTfOr+c1j1KiyhiOKVXGrC8Jut2Qk7t+zPyN2cHWp5EaNnDEdGO8xYqjGywzlfdWLSNQWFq7ZIH0zvEpctSgZrUgfmJHtG4iQp1YsUJHuk145Jat7ZqlIAG1zKUpM9YD6fduxnKgA1pkxfjIvY+B3/Zj3k8Ays8Q/AcQNXqvD5MPitBURdRZ/yB8Qed1faOTlWAAAAAElFTkSuQmCC" />
|
||||
<title>آسان سرویس</title>
|
||||
</head>
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
+21
@@ -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 (
|
||||
<TransitionPages>
|
||||
<div className="flex flex-col gap-y-[30px] w-full">
|
||||
|
||||
@@ -21,8 +21,8 @@ export const AwardNotComplete: FC<Props> = ({ award }) => {
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-normal text-base text-primary-text-color" style={{ fontFamily: "vazirmatn" }}>{award?.desc?.length >= 70 ? `${award?.desc?.slice(0, 70)} ...` : award?.desc}</p>
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<p className="font-normal text-sm text-secondary-text-color">{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}</p>
|
||||
<Calendar2 color="#777577" className="size-5" />
|
||||
<p className="font-normal text-sm text-secondary-text-color">{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<Props> = ({ award }) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-10 bg-auth-form p-[18px] rounded-[40px]">
|
||||
<div className="flex flex-col w-full h-full justify-between gap-10 bg-auth-form p-[18px] rounded-[40px]">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="font-normal text-lg text-secondary-text-color">۲۵۰ هزار تومان تخفیف برای اقامت در تمام ویلاها و کلبه های جاجیگا با حداقل رزرو ۲ میلیون تومان.</p>
|
||||
<p className="font-normal text-base text-secondary-text-color">1403/06/12</p>
|
||||
<p className="font-normal text-lg text-secondary-text-color">{award?.req?.desc}</p>
|
||||
<p className="font-normal text-base text-secondary-text-color">{formatDateToPersian(award?.expireDate, "jYYYY/jMM/jD")}</p>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row gap-x-[6px] items-center">
|
||||
<p className="font-normal text-lg text-secondary-text-color">75</p>
|
||||
<p className="font-normal text-lg text-secondary-text-color">{award?.score}</p>
|
||||
<Cup className="size-4" color="#777577" />
|
||||
</div>
|
||||
<div className="size-10 flex items-center justify-center bg-white rounded-full cursor-pointer">
|
||||
|
||||
@@ -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<AwardStatus>({
|
||||
awards: 0,
|
||||
my_score: 0
|
||||
});
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ["awards"],
|
||||
queryFn: getAwardsList
|
||||
})
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
handleError(error)
|
||||
}, [error])
|
||||
handleError(error);
|
||||
}, [error]);
|
||||
|
||||
if (isLoading) return <AwardSkeleton />
|
||||
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 <AwardSkeleton />;
|
||||
return (
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<p className="font-medium text-xl text-primary-text-color">جوایز (5)</p>
|
||||
<p className="font-normal text-base text-primary-text-color">امتیازهای من: 65</p>
|
||||
<p className="font-medium text-xl text-primary-text-color">جوایز ({status?.awards})</p>
|
||||
<p className="font-normal text-base text-primary-text-color">امتیازهای من: {status?.my_score}</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-6">
|
||||
{data?.data?.map((award: AwardsResponse) => <AwardNotComplete key={award.id} award={award} />)}
|
||||
{data?.data?.map((award: AwardsResponse, index: number) => award?.status === 0 ? <AwardNotComplete key={index} award={award} /> : <AwardComplete key={index} award={award} />)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ export const ComboBox: FC<Props> = ({ field, placeholder, className, data = [],
|
||||
<div className="relative w-full">
|
||||
<ComboboxInput
|
||||
placeholder={placeholder}
|
||||
className={`input-style min-w-full ${className}`}
|
||||
className={`input-style min-w-full ${className} ${placeholder && "placeholder:text-primary-text-color"}`}
|
||||
displayValue={(person: any) => person?.name}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
/>
|
||||
@@ -45,9 +45,9 @@ export const ComboBox: FC<Props> = ({ 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) => (
|
||||
<ComboboxOption
|
||||
key={value?.id}
|
||||
key={index}
|
||||
value={value}
|
||||
className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-secondary-text-color/10"
|
||||
>
|
||||
|
||||
@@ -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<Props> = ({ field }) => {
|
||||
export const DatepickerComponent: FC<Props> = ({ field, placeholder }) => {
|
||||
return (
|
||||
<div className='relative w-full min-w-max cursor-pointer mb-10 xl:mb-0'>
|
||||
<div className="absolute top-[18.5px] left-5 grid h-5 w-5 place-items-center !z-50">
|
||||
<div className="absolute top-[18.5px] left-5 grid h-5 w-5 place-items-center">
|
||||
<Calendar2 color='#777577' />
|
||||
</div>
|
||||
<DatePicker
|
||||
@@ -20,11 +21,12 @@ export const DatepickerComponent: FC<Props> = ({ 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`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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<Props> = ({ placeholder, type = "text", icon, field, className, value, onChange }) => {
|
||||
export const Input: FC<Props> = ({ placeholder, type = "text", icon, field, className, value, onChange, disabled = false }) => {
|
||||
const [hidden, setHidden] = useState<boolean>(false)
|
||||
const handleHidden = () => setHidden(prev => !prev)
|
||||
return (
|
||||
@@ -27,7 +28,7 @@ export const Input: FC<Props> = ({ placeholder, type = "text", icon, field, clas
|
||||
: icon}
|
||||
{placeholder === "شماره کارت" && field.value && findBankByPAN(field.value).icon}
|
||||
</div>
|
||||
<InputComponent ref={field?.ref} {...field} value={value} onChange={onChange || field?.onChange} type={type === "password" && hidden ? "text" : type} placeholder={placeholder} className={`input-style min-w-full ${className}`} />
|
||||
<InputComponent ref={field?.ref} {...field} value={value} onChange={onChange || field?.onChange} type={type === "password" && hidden ? "text" : type} placeholder={placeholder} className={`input-style min-w-full ${className}`} disabled={disabled} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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<Props> = ({ columns, data, tableStatus = false, tableScore = 0, isLoading = false, totalData = 0 }) => {
|
||||
const [aDSort, setADSort] = useState<ADSortType>({
|
||||
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<Props> = ({ columns, data, tableStatus = false, tableScor
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-center gap-2.5 md:gap-10">
|
||||
<div className="sm:max-w-[300px] xl:max-w-[450px] w-full">
|
||||
<Input placeholder="جستجو" value={params?.search} onChange={(e: any) => handleInputValue(e?.target?.value)} className="bg-auth-form placeholder:text-[#777577]" icon={<SearchNormal1 color="#777577" />} />
|
||||
<Input placeholder="جستجو" value={params?.search} onChange={(e: any) => {
|
||||
handleInputValue(e?.target?.value)
|
||||
}} className="bg-auth-form placeholder:text-[#777577]" icon={<SearchNormal1 color="#777577" />} />
|
||||
</div>
|
||||
<div className="w-full sm:max-w-[100px]">
|
||||
<ComboBox placeholder={params?.rows.toString()} className="bg-auth-form" data={tableRows} mode="table" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex justify-start items-end text-sm lg:text-lg text-primary-text-color font-normal -mb-7 pl-2">{tableStatus && `امتیاز من : ${tableScore}`}</div>
|
||||
<div className="block max-w-full overflow-x-auto overflow-y-hidden bg-auth-form rounded-[40px] shadow-sm p-4 pb-0 xl:p-9 xl:pb-0 relative">
|
||||
<div className="block max-w-full min-h-max overflow-x-auto overflow-y-hidden bg-auth-form rounded-[40px] shadow-sm p-4 pb-0 xl:p-9 xl:pb-0 relative">
|
||||
<table>
|
||||
<thead className="border-b border-b-secondary-color">
|
||||
{table.getHeaderGroups().map((headerGroup: any, index: number) => (
|
||||
@@ -92,7 +91,7 @@ export const Table: FC<Props> = ({ columns, data, tableStatus = false, tableScor
|
||||
}}>
|
||||
<div {...{ className: header.column.getCanSort() ? "text-secondary-text-color text-base font-normal flex flex-row items-center justify-center gap-1 px-4 py-2" : "", onClick: header.column.getToggleSortingHandler() }}>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{{ asc: <ArrowDown2 color="#777577" className="size-4" />, desc: <ArrowUp2 color="#777577" className="size-4" /> }[header.column.getIsSorted() as string] ?? null}
|
||||
{aDSort?.value === 1 && aDSort?.key === header.getContext()?.column?.id ? <ArrowDown2 color="#777577" className="size-4" /> : <ArrowUp2 color="#777577" className="size-4" />}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
|
||||
@@ -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<Props> = ({ 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 (
|
||||
<div className="flex flex-col items-left">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="hidden"
|
||||
id="imageUpload"
|
||||
/>
|
||||
<label htmlFor="imageUpload" className="cursor-pointer">
|
||||
{selectedImage ? (
|
||||
<div className="relative">
|
||||
<img src={URL.createObjectURL(selectedImage)} alt="Selected image" className="w-full min-w-max max-h-60 object-fill rounded-[40px]" />
|
||||
<Trash color='#fff' size={25} onClick={handleImageDelete} className='absolute bottom-2 right-4' />
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-white w-full min-w-max h-fit min-h-60 flex items-center justify-center text-secondary-text-color rounded-[40px]">
|
||||
عکس خود را انتخاب کنید
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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<LoginFormInterface>({
|
||||
resolver: yupResolver(loginSchema)
|
||||
})
|
||||
|
||||
const handleLoginSubmit: SubmitHandler<LoginFormInterface> = 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("/")
|
||||
|
||||
@@ -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<CheckIMEI>({
|
||||
resolver: yupResolver(checkIMEISchema)
|
||||
})
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: checkIMEI,
|
||||
mutationKey: ["IMEI"]
|
||||
})
|
||||
const handleCheckIMEI: SubmitHandler<CheckIMEI> = async (data) => {
|
||||
await mutate(data, {
|
||||
onSuccess: (res: any) => {
|
||||
toast.success("کد IMEI معتبر است")
|
||||
},
|
||||
onError: (err: any) => handleError(err)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className="w-full flex flex-col lg:flex-row bg-auth-form p-[30px] rounded-[40px] gap-8">
|
||||
<form onSubmit={handleSubmit(handleCheckIMEI)} className="w-full flex flex-col lg:flex-row bg-auth-form p-[30px] rounded-[40px] gap-8">
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<img src="/svgs/dashboard/binary.svg" alt="binary" className="size-full max-w-[500px] max-h-[500px]" />
|
||||
</div>
|
||||
<div className="flex flex-col w-full gap-10 justify-center lg:justify-between">
|
||||
<Input placeholder="کد IMEI" />
|
||||
<Controller control={control} name="IMEI" render={({ field }) => (
|
||||
<div className="flex flex-col !gap-0">
|
||||
<Input placeholder="کد IMEI" type="text" field={field} />
|
||||
<ErrorComponent show={errors?.IMEI} message={errors?.IMEI?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<p className="font-normal text-base text-primary-text-color text-justify lg:text-wrap">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز
|
||||
</p>
|
||||
<div className="w-full flex justify-end">
|
||||
<Button className="w-full min-w-full xl:min-w-min xl:max-w-60">ثبت کد</Button>
|
||||
<ButtonComponent title="ثبت کد" className="w-full min-w-full xl:min-w-min xl:max-w-60" pending={isPending} type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
+174
-122
@@ -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<MyAccountInterface>({
|
||||
resolver: yupResolver(MyAccountSchema)
|
||||
const [selectedImage, setSelectedImage] = useState<any>(null);
|
||||
const { user, updateUser } = useUserStore()
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: updateUSerInformations,
|
||||
mutationKey: ["update-user"]
|
||||
})
|
||||
const { handleSubmit, formState: { errors }, control, watch, reset } = useForm<UpdateUserInterface>({
|
||||
resolver: yupResolver(UpdateUserSchema),
|
||||
})
|
||||
const provinceName: any = watch("province_name");
|
||||
const [provinceData, setProvinceData] = useState<ComboBoxItems[] | []>([])
|
||||
const [citiesData, setCitiesData] = useState<ComboBoxItems[] | []>([])
|
||||
const { data: provinces } = useQuery({
|
||||
queryFn: getProvincesList,
|
||||
queryKey: ["province"]
|
||||
})
|
||||
const { data: cities } = useQuery({
|
||||
queryFn: getCitiesList,
|
||||
queryKey: ["cities"]
|
||||
})
|
||||
|
||||
const handleMyAccountForm: SubmitHandler<MyAccountInterface> = async (data) => {
|
||||
console.log("data =>", data)
|
||||
const handleUpdateUser: SubmitHandler<UpdateUserInterface> = 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 (
|
||||
<form onSubmit={handleSubmit(handleMyAccountForm)} className="w-full h-full flex flex-col gap-10 bg-auth-form rounded-[40px] p-[30px]">
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">اطلاعات اولیه</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
|
||||
<Controller name="phoneNumber" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="شماره تماس" type="number" field={field} />
|
||||
<ErrorComponent show={errors.phoneNumber} message={errors.phoneNumber?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="marketName" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="نام شرکت" field={field} />
|
||||
<ErrorComponent show={errors.marketName} message={errors.marketName?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="name" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="نام" field={field} />
|
||||
<ErrorComponent show={errors.name} message={errors.name?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="family" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="نام خانوادگی" field={field} />
|
||||
<ErrorComponent show={errors.family} message={errors.family?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="capCity" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<ComboBox field={field} />
|
||||
<ErrorComponent show={errors.capCity} message={errors.capCity?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="city" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<ComboBox field={field} />
|
||||
<ErrorComponent show={errors.city} message={errors.city?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<div className="w-full h-full flex flex-col gap-10 bg-auth-form rounded-[40px] p-[30px]">
|
||||
<form onSubmit={handleSubmit(handleUpdateUser)} className="w-full h-full flex flex-col gap-10 bg-auth-form rounded-[40px]">
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">اطلاعات اولیه</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
</div>
|
||||
<div className="divide-y-2 border" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">اطلاعات تکمیلی</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
|
||||
<Controller name="address" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="آدرس" field={field} />
|
||||
<ErrorComponent show={errors.address} message={errors.address?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="phoneNumber1" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="شماره تماس" type="number" field={field} />
|
||||
<ErrorComponent show={errors.phoneNumber1} message={errors.phoneNumber1?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="staticNumber" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="شماره ثابت" type="number" field={field} />
|
||||
<ErrorComponent show={errors.staticNumber} message={errors.staticNumber?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="date" control={control} render={({ field }) => (
|
||||
<div className="flex flex-col gap-y-9">
|
||||
<DatepickerComponent field={field} />
|
||||
<ErrorComponent show={errors.date} message={errors.date?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
|
||||
<Controller name="mobile_number" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.mobile_number ? user?.mobile_number : "شماره تماس"} type="number" disabled field={field} />
|
||||
<ErrorComponent show={errors.mobile_number} message={errors.mobile_number?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="shopName" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.shopName ? user?.shopName : "نام شرکت"} field={field} />
|
||||
<ErrorComponent show={errors.shopName} message={errors.shopName?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="first_name" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.first_name ? user?.first_name : "نام"} field={field} />
|
||||
<ErrorComponent show={errors.first_name} message={errors.first_name?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="last_name" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.last_name ? user?.last_name : "نام خانوادگی"} field={field} />
|
||||
<ErrorComponent show={errors.last_name} message={errors.last_name?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="province_name" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<ComboBox field={field} data={provinceData} placeholder={user?.province_name} />
|
||||
<ErrorComponent show={errors.province_name} message={errors.province_name?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="city_name" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<ComboBox field={field} data={citiesData} placeholder={user?.city_name} />
|
||||
<ErrorComponent show={errors.city_name} message={errors.city_name?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<ImageUpload setSelectedImage={setSelectedImage} selectedImage={selectedImage} />
|
||||
</div>
|
||||
<div className="divide-y-2 border" />
|
||||
</div>
|
||||
<div className="divide-y-2 border" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">تغییر رمز عبور</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">اطلاعات تکمیلی</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
|
||||
<Controller name="address" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.address ? user?.address : "آدرس"} field={field} />
|
||||
<ErrorComponent show={errors.address} message={errors.address?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="national_code" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.national_code} type="number" field={field} disabled />
|
||||
<ErrorComponent show={errors.national_code} message={errors.national_code?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="cell_number" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder={user?.cell_number} type="number" field={field} />
|
||||
<ErrorComponent show={errors.cell_number} message={errors.cell_number?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="birthDate" control={control} render={({ field }) => (
|
||||
<div className="flex flex-col gap-y-9">
|
||||
<DatepickerComponent field={field} placeholder={user?.birthDate} />
|
||||
<ErrorComponent show={errors.birthDate} message={errors.birthDate?.message} />
|
||||
</div>
|
||||
)} />
|
||||
</div>
|
||||
<div className="divide-y-2 border" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px] w-full">
|
||||
<Controller name="currentPassword" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="رمز عبور فعلی" type="password" field={field} />
|
||||
<ErrorComponent show={errors.currentPassword} message={errors.currentPassword?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<div />
|
||||
<Controller name="newPassword" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="رمز عبور جدید" type="password" field={field} />
|
||||
<ErrorComponent show={errors.newPassword} message={errors.newPassword?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="repeatPassword" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="تکرار رمز عبور" type="password" field={field} />
|
||||
<ErrorComponent show={errors.repeatPassword} message={errors.repeatPassword?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<div className="flex w-full min-w-max lg:justify-end mt-10">
|
||||
<ButtonComponent type="submit" title="ذخیره" pending={isPending} className="w-full lg:max-w-fit lg:min-w-72" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full xl:justify-end mt-10">
|
||||
<Button type="submit" className="w-full xl:max-w-fit xl:min-w-72">ذخیره</Button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
<ResetPasswordBox />
|
||||
</div >
|
||||
)
|
||||
}
|
||||
@@ -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<ResetPasswordUser>({
|
||||
resolver: yupResolver(ResetPasswordUserSchema)
|
||||
})
|
||||
|
||||
const handleResetUserPassword: SubmitHandler<ResetPasswordUser> = async (data) => {
|
||||
await mutate({
|
||||
...data
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success("تغییر رمز عبور با موفقیت انجام شد")
|
||||
},
|
||||
onError: (err: any) => {
|
||||
handleError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
return (
|
||||
<form className="w-full h-full flex flex-col gap-10" onSubmit={handleSubmit(handleResetUserPassword)}>
|
||||
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
|
||||
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
|
||||
<p className="font-medium text-xl text-primary-text-color">تغییر رمز عبور</p>
|
||||
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px] w-full">
|
||||
<Controller name="password" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="رمز عبور فعلی" type="password" field={field} />
|
||||
<ErrorComponent show={errors.password} message={errors.password?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<div />
|
||||
<Controller name="newPassword" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="رمز عبور جدید" type="password" field={field} />
|
||||
<ErrorComponent show={errors.newPassword} message={errors.newPassword?.message} />
|
||||
</div>
|
||||
)} />
|
||||
<Controller name="password_confirmation" control={control} render={({ field }) => (
|
||||
<div>
|
||||
<Input placeholder="تکرار رمز عبور" type="password" field={field} />
|
||||
<ErrorComponent show={errors.password_confirmation} message={errors.password_confirmation?.message} />
|
||||
</div>
|
||||
)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full lg:justify-end mt-10">
|
||||
<ButtonComponent type="submit" title="تغییر رمز عبور" pending={isPending} className="w-full lg:max-w-fit lg:min-w-72" />
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -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<Props> = ({ setShow }) => {
|
||||
const { user } = useUserStore()
|
||||
return (
|
||||
<header className="flex flex-row w-full justify-between items-center mb-10 lg:mb-[60px]">
|
||||
<div className="flex flex-row gap-x-5 lg:gap-x-3 items-center">
|
||||
<img src="/svgs/header/burger-menu.svg" alt="burger menu" className="lg:hidden size-[27px]" onClick={setShow} />
|
||||
<img src="/svgs/header/person.svg" alt="person" className="size-[60px]" />
|
||||
<p className="hidden font-normal text-sm lg:block lg:text-xl">علی مصلحی</p>
|
||||
{user?.profilePic !== "noPic" ? <img src={`https://asan-service.com/${user?.profilePic}`} alt="person" className="size-[60px] rounded-full" /> : <div className="size-[60px] flex flex-row justify-center items-center bg-secondary-color rounded-full">
|
||||
<Profile
|
||||
size="30"
|
||||
color="#777577"
|
||||
/></div>}
|
||||
<p className="hidden font-normal text-sm lg:block lg:text-xl">{user?.first_name} {user?.last_name}</p>
|
||||
</div>
|
||||
<div className="flex flex-row gap-x-3 items-center">
|
||||
<p className="font-normal text-sm lg:text-xl">021-45597000</p>
|
||||
<p className="font-normal text-sm lg:text-xl"><a href={`tel:${user?.mobile_number}`}>{user?.mobile_number}</a></p>
|
||||
<Call color="#11212D" variant="Bold" className="size-[27px]" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -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<Props> = ({ setShow, show }) => {
|
||||
const { clearUser } = useUserStore()
|
||||
const navigate = useNavigate()
|
||||
const handleCloseSidebar = () => setShow(false);
|
||||
const handleExit = () => {
|
||||
window.localStorage.clear()
|
||||
navigate("/auth/login")
|
||||
clearUser()
|
||||
}
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -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<Props> = ({ pending }) => {
|
||||
const [selectedCard, setSelectedCard] = useState<CardBank | null>(null)
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: sendwithdraw,
|
||||
mutationKey: ["withdraw"]
|
||||
})
|
||||
const { handleSubmit, formState: { errors }, control, watch } = useForm<any>({
|
||||
})
|
||||
const cardDetail: any = watch("card")
|
||||
const [userBanks, setUserBanks] = useState<any>([])
|
||||
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<SendWithdraw> = async () => {
|
||||
await mutate({
|
||||
card: {
|
||||
holderName: selectedCard?.holderName,
|
||||
PAN: selectedCard?.PAN,
|
||||
IBAN: selectedCard?.IBAN
|
||||
}
|
||||
}, {
|
||||
onSuccess: (res: any) => {
|
||||
toast.success("درخواست شما با موفقیت ثبت شد")
|
||||
},
|
||||
onError: (err: any) => handleError(err)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className="w-full flex flex-col lg:flex-row bg-auth-form p-[30px] rounded-[40px] gap-8">
|
||||
<form onSubmit={handleSubmit(handleSendWithdraw)} className="w-full flex flex-col lg:flex-row bg-auth-form p-[30px] rounded-[40px] gap-8">
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<img src="/svgs/dashboard/wallet.svg" alt="wallet" className="size-full max-w-[500px] max-h-[500px]" />
|
||||
</div>
|
||||
<div className="flex flex-col w-full gap-10 justify-center lg:justify-between">
|
||||
<div className="flex flex-col gap-10">
|
||||
<p className="text-2xl font-medium text-primary-text-color">ثبت درخواست برداشت از حساب</p>
|
||||
{!pending && <ComboBox placeholder="حساب بانکی مورد نظر خود را انتخاب کنید" />}
|
||||
|
||||
{!pending && <Controller control={control} name="card" render={({ field }) => (
|
||||
<>
|
||||
<ComboBox placeholder="حساب بانکی مورد نظر خود را انتخاب کنید" data={userBanks} field={field} />
|
||||
<ErrorComponent show={errors?.card} message={errors?.card?.message} />
|
||||
</>
|
||||
)} />}
|
||||
</div>
|
||||
{!pending && <p className="font-normal text-base text-primary-text-color text-justify lg:text-wrap">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز
|
||||
@@ -24,9 +75,9 @@ export const WalletAddress: FC<Props> = ({ pending }) => {
|
||||
{`همکار گرامی
|
||||
طبق درخواست قبلی، عملیات واریز مبلغ 765,975 تومان به حساب شما در حال اجراست. متاسفانه پیش از تکمیل درخواست، امکان شروع عملیات جدید برای شما وجود ندارد.`}</p>}
|
||||
<div className="w-full flex justify-end">
|
||||
<Button className="w-full min-w-full xl:min-w-min xl:max-w-60">{pending ? "لغو درخواست" : "ثبت درخواست"}</Button>
|
||||
<ButtonComponent className="w-full min-w-full xl:min-w-min xl:max-w-60" pending={isPending} title={pending ? "لغو درخواست" : "ثبت درخواست"} type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form >
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
+33
-25
@@ -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 کاراکتر باشد"),
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import { CheckIMEI } from "../../types";
|
||||
import axiosInstance from "../axios";
|
||||
|
||||
|
||||
export const checkIMEI = (payload: CheckIMEI) => axiosInstance.post<any>("gps/register-device", payload)
|
||||
@@ -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<any>("/gps/user/reset-pass", payload)
|
||||
export const updateUSerInformations = (payload: any) => axiosInstance.put<any>(`/gps/user/me/${payload?.id}`, payload?.formData)
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import axiosInstance from "../axios";
|
||||
import { OverviewResponse } from "./overview";
|
||||
|
||||
export const getWalletDetails = () => axiosInstance.get<OverviewResponse[] | OverviewResponse | [], any>("/gps/overview/wallet")
|
||||
|
||||
export const sendwithdraw = (payload: any) => axiosInstance.post("/gps/withdraw", payload)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { create } from 'zustand';
|
||||
import { UserInterface } from '../types';
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
type ZustandState = {
|
||||
user: UserInterface;
|
||||
updateUser: (userData: Partial<UserInterface>) => 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<ZustandState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
user: defaultParams,
|
||||
updateUser: (userData: Partial<UserInterface>) =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
user: {
|
||||
...state.user,
|
||||
...userData,
|
||||
},
|
||||
})),
|
||||
clearUser: () =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
user: defaultParams,
|
||||
})),
|
||||
}),
|
||||
{
|
||||
name: 'user-store',
|
||||
getStorage: () => localStorage
|
||||
}
|
||||
)
|
||||
);
|
||||
+65
-24
@@ -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
|
||||
}
|
||||
@@ -19,7 +19,7 @@ const columnConfigs: colConfig[] | [] = [
|
||||
</div>
|
||||
},
|
||||
{ accessor: "created_at", header: "تاریخ درخواست", cellRenderer: (info) => info.getValue() && <p style={{ direction: "ltr" }}>{formatDateToPersian(info.getValue())}</p> },
|
||||
{ accessor: "id", header: "شماره پیگیری" },
|
||||
{ accessor: "trackingNumber", header: "شماره پیگیری" },
|
||||
{ accessor: "updated_at", header: "تاریخ واریز", cellRenderer: (info) => info.getValue() && <p style={{ direction: "ltr" }}>{formatDateToPersian(info.getValue())}</p> },
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user