sync bank card api and complete it

This commit is contained in:
Alihaghighattalab
2024-08-13 17:50:42 +03:30
parent 25b09cf7b1
commit 3bbf577093
20 changed files with 130 additions and 100 deletions
+2 -1
View File
@@ -6,7 +6,8 @@ type Props = {
title: string title: string
type?: "reset" | "button" | "submit" | undefined, type?: "reset" | "button" | "submit" | undefined,
pending?: boolean, pending?: boolean,
theme?: "primary" | "secondary" | "glass" | "light" theme?: "primary" | "secondary" | "glass" | "light",
onClick?: () => void
} }
export const ButtonComponent: FC<Props> = ({ title, type = "button", pending = false, theme = "primary" }) => { export const ButtonComponent: FC<Props> = ({ title, type = "button", pending = false, theme = "primary" }) => {
@@ -1,19 +1,17 @@
import { useQuery } from "@tanstack/react-query"
import { installationReportsCol } from "../../utility/reports" import { installationReportsCol } from "../../utility/reports"
import { Table } from "../common/table" import { Table } from "../common/table"
import { getReportsList } from "../../services/api/reports"
import { useState } from "react" import { useState } from "react"
import { SortingState } from "@tanstack/react-table" import { SortingState } from "@tanstack/react-table"
export const InstallationReports = () => { export const InstallationReports = () => {
const [sorting, setSorting] = useState<SortingState>([]) const [sorting, setSorting] = useState<SortingState>([])
const { data, isLoading } = useQuery({ // const { data, isLoading } = useQuery({
queryKey: ["installation-reports-list"], // queryKey: ["installation-reports-list"],
queryFn: getReportsList // queryFn: getReportsList
}) // })
if (isLoading) return <p>LODING .....</p> // if (isLoading) return <p>LODING .....</p>
return ( return (
<Table columns={installationReportsCol} data={data?.data} sorting={sorting} setSorting={setSorting} /> <Table columns={installationReportsCol} data={[]} sorting={sorting} setSorting={setSorting} />
) )
} }
@@ -2,34 +2,40 @@ import { CardAdd } from "iconsax-react"
import { DialogComponent } from "../common/dialog" import { DialogComponent } from "../common/dialog"
import { FC } from "react" import { FC } from "react"
import { Input } from "../common/input" import { Input } from "../common/input"
import { Button } from "@headlessui/react"
import { bankCarts, findBankByPAN } from "../../utility/cart"
import { Controller, SubmitHandler, useForm } from "react-hook-form" import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { AddNewCartInterface } from "../../types" import { AddNewCartInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup" import { yupResolver } from "@hookform/resolvers/yup"
import { AddNewCardSchema } from "../../schema" import { AddNewCardSchema } from "../../schema"
import { ErrorComponent } from "../common/error" import { ErrorComponent } from "../common/error"
import { useMutation } from "@tanstack/react-query"
import { createNewCardMutation } from "../../services/api/bank-card"
import toast from "react-hot-toast"
import { ButtonComponent } from "../common/button"
type Props = { type Props = {
isOpen: boolean, isOpen: boolean,
close: () => void close: () => void
} }
export const AddNewCardDialog: FC<Props> = ({ isOpen, close }) => { export const AddNewCardDialog: FC<Props> = ({ isOpen, close }) => {
const { mutate, isPending } = useMutation({
mutationFn: createNewCardMutation
})
const { handleSubmit, formState: { errors }, control } = useForm<AddNewCartInterface>({ const { handleSubmit, formState: { errors }, control } = useForm<AddNewCartInterface>({
resolver: yupResolver(AddNewCardSchema) resolver: yupResolver(AddNewCardSchema)
}) })
const createNewCard: SubmitHandler<AddNewCartInterface> = async (data) => { const createNewCard: SubmitHandler<AddNewCartInterface> = async (data) => {
const finalData: any = { mutate({
id: bankCarts?.length + 1, holderName: `${data?.name} ${data?.family}`,
bankName: findBankByPAN(data.cartNumber).name, PAN: data?.PAN,
bankPersianName: findBankByPAN(data.cartNumber).name_farsi, IBAN: data?.IBAN,
cartNumber: data?.cartNumber.toString(), }, {
cartOwner: `${data.name} ${data?.family}`, onSuccess: () => {
shabaNumber: data?.shabaNumber, toast.success("کارت با موفقیت اضافه شد")
},
onError: () => {
toast.error("کارت با موفقیت اضافه نشد")
} }
console.log("final-data =>", finalData) })
bankCarts.push(finalData)
close()
} }
return ( return (
<DialogComponent isOpen={isOpen} close={close} title="ایجاد حساب بانکی جدید" subTitle="لطفا اطلاعات را با دقت وارد نمایید" icon={<CardAdd color="#11212D" className="size-[50px]" />}> <DialogComponent isOpen={isOpen} close={close} title="ایجاد حساب بانکی جدید" subTitle="لطفا اطلاعات را با دقت وارد نمایید" icon={<CardAdd color="#11212D" className="size-[50px]" />}>
@@ -47,22 +53,22 @@ export const AddNewCardDialog: FC<Props> = ({ isOpen, close }) => {
<ErrorComponent show={errors.family} message={errors.family?.message} /> <ErrorComponent show={errors.family} message={errors.family?.message} />
</div> </div>
)} /> )} />
<Controller control={control} name="cartNumber" render={({ field }) => ( <Controller control={control} name="PAN" render={({ field }) => (
<div> <div>
<Input field={field} placeholder="شماره کارت" className="bg-auth-form" /> <Input field={field} placeholder="شماره کارت" className="bg-auth-form" />
<ErrorComponent show={errors.cartNumber} message={errors.cartNumber?.message} /> <ErrorComponent show={errors.PAN} message={errors.PAN?.message} />
</div> </div>
)} /> )} />
<Controller control={control} name="shabaNumber" render={({ field }) => ( <Controller control={control} name="IBAN" render={({ field }) => (
<div> <div>
<Input field={field} placeholder="شماره شبا" className="bg-auth-form" /> <Input field={field} placeholder="شماره شبا" className="bg-auth-form" />
<ErrorComponent show={errors.shabaNumber} message={errors.shabaNumber?.message} /> <ErrorComponent show={errors.IBAN} message={errors.IBAN?.message} />
</div> </div>
)} /> )} />
</div> </div>
<div className="flex flex-col gap-[6px]"> <div className="flex flex-col gap-[6px]">
<Button type="submit" className="w-full bg-primary-color text-white">ایجاد حساب</Button> <ButtonComponent title="افزودن کارت" pending={isPending} type="submit" />
<Button onClick={close} className="w-full bg-transparent text-primary-color">لغو</Button> <ButtonComponent title="لغو" pending={isPending} type="button" theme="glass" onClick={close} />
</div> </div>
</form> </form>
</DialogComponent> </DialogComponent>
@@ -5,10 +5,11 @@ import { findBankByPAN, formatCartNumber } from "../../utility/cart"
type Props = { type Props = {
item: BankCartInterface, item: BankCartInterface,
handleOpenDeleteDialog: () => void handleOpenDeleteDialog: () => void,
setSelectedCard: (value: string | number | null) => void,
} }
export const BankCart: FC<Props> = ({ item, handleOpenDeleteDialog }) => { export const BankCart: FC<Props> = ({ item, handleOpenDeleteDialog, setSelectedCard }) => {
return ( return (
<div className={`w-full flex flex-col justify-between rounded-[40px] bg-auth-form p-[18px] cursor-pointer bank-bg min-h-[226px]`} style={{ backgroundImage: `url('/svgs/banks/${item?.bankName}.svg')` }}> <div className={`w-full flex flex-col justify-between rounded-[40px] bg-auth-form p-[18px] cursor-pointer bank-bg min-h-[226px]`} style={{ backgroundImage: `url('/svgs/banks/${item?.bankName}.svg')` }}>
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
@@ -18,7 +19,7 @@ export const BankCart: FC<Props> = ({ item, handleOpenDeleteDialog }) => {
</div> </div>
<div className="flex flex-row gap-3 items-center"> <div className="flex flex-row gap-3 items-center">
<Trash color="#777577" onClick={handleOpenDeleteDialog} /> <Trash color="#777577" onClick={handleOpenDeleteDialog} />
<Edit color="#777577" /> <Edit color="#777577" onClick={() => setSelectedCard(item?.id)} />
</div> </div>
</div> </div>
<p className="font-medium text-2xl text-primary-text-color text-center" style={{ direction: "ltr" }}>{formatCartNumber(item?.cartNumber)}</p> <p className="font-medium text-2xl text-primary-text-color text-center" style={{ direction: "ltr" }}>{formatCartNumber(item?.cartNumber)}</p>
@@ -1,19 +1,34 @@
import { FC } from "react" import { FC } from "react"
import { DialogComponent } from "../common/dialog" import { DialogComponent } from "../common/dialog"
import { Trash } from "iconsax-react" import { Trash } from "iconsax-react"
import { Button } from "@headlessui/react" import { useMutation } from "@tanstack/react-query"
import { deleteCardMutation } from "../../services/api/bank-card"
import toast from "react-hot-toast"
import { ButtonComponent } from "../common/button"
type Props = { type Props = {
isOpen: boolean, isOpen: boolean,
close: () => void close: () => void,
id: string | number | null
} }
export const DeleteCardDialog: FC<Props> = ({ isOpen, close }) => { export const DeleteCardDialog: FC<Props> = ({ isOpen, close, id }) => {
const { mutate, isPending } = useMutation({
mutationFn: deleteCardMutation
})
const handleDeleteSelectedCard = async () => {
await mutate({
id
}, {
onSuccess: () => toast.success("کارت مورد نظر با موفقیت حذف شد"),
onError: () => toast.error("حذف کارت نا موفق")
})
}
return ( return (
<DialogComponent isOpen={isOpen} close={close} title="حذف حساب بانکی" subTitle="از حذف حساب بانکی مربوطه اطمینان دارید؟" icon={<Trash color="#11212D" className="size-[50px]" />}> <DialogComponent isOpen={isOpen} close={close} title="حذف حساب بانکی" subTitle="از حذف حساب بانکی مربوطه اطمینان دارید؟" icon={<Trash color="#11212D" className="size-[50px]" />}>
<div className="flex flex-col gap-[6px] mt-10"> <div className="flex flex-col gap-[6px] mt-10">
<Button className="w-full bg-primary-color text-white">حذف</Button> <ButtonComponent title="حذف" onClick={handleDeleteSelectedCard} pending={isPending} />
<Button onClick={close} className="w-full bg-transparent text-primary-color">لغو</Button> <ButtonComponent title="لغو" onClick={close} theme="glass" />
</div> </div>
</DialogComponent> </DialogComponent>
) )
@@ -1,6 +1,5 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { BankCartInterface } from "../../types" import { BankCartInterface } from "../../types"
import { bankCarts } from "../../utility/cart"
import { BankCart } from "./bank-cart" import { BankCart } from "./bank-cart"
import { CreateNewCart } from "./create-new-cart" import { CreateNewCart } from "./create-new-cart"
import { AddNewCardDialog } from "./add-new-cart-dialog" import { AddNewCardDialog } from "./add-new-cart-dialog"
@@ -9,6 +8,7 @@ import { useQuery } from "@tanstack/react-query"
import { getUserCardsList } from "../../services/api/bank-card" import { getUserCardsList } from "../../services/api/bank-card"
export const ManagementBankAccounts = () => { export const ManagementBankAccounts = () => {
const [selectedCard, setSelectedCard] = useState<string | number | null>(null)
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
queryKey: ["bank-card"], queryKey: ["bank-card"],
queryFn: getUserCardsList queryFn: getUserCardsList
@@ -29,10 +29,10 @@ export const ManagementBankAccounts = () => {
<> <>
<div className="w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 gap-6"> <div className="w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 gap-6">
<CreateNewCart handleOpenDialog={handleOpenDialog} /> <CreateNewCart handleOpenDialog={handleOpenDialog} />
{bankCarts?.map((item: BankCartInterface) => <BankCart key={item?.id} item={item} handleOpenDeleteDialog={handleOpenDeleteDialog} />)} {data?.data?.map((item: BankCartInterface) => <BankCart key={item?.id} item={item} handleOpenDeleteDialog={handleOpenDeleteDialog} setSelectedCard={setSelectedCard} />)}
</div> </div>
<AddNewCardDialog isOpen={showDialog} close={handleCloseDialog} /> <AddNewCardDialog isOpen={showDialog} close={handleCloseDialog} />
<DeleteCardDialog isOpen={showDeleteDialog} close={handleCloseDeleteDialog} /> <DeleteCardDialog isOpen={showDeleteDialog} close={handleCloseDeleteDialog} id={selectedCard} />
</> </>
) )
} }
+6 -8
View File
@@ -2,19 +2,17 @@ import { useState } from "react"
import { Table } from "../common/table" import { Table } from "../common/table"
import { SortingState } from "@tanstack/react-table" import { SortingState } from "@tanstack/react-table"
import { scoresCol } from "../../utility/score" import { scoresCol } from "../../utility/score"
import { useQuery } from "@tanstack/react-query"
import { getScoreList } from "../../services/api/score"
export const Score = () => { export const Score = () => {
const [sorting, setSorting] = useState<SortingState>([]) const [sorting, setSorting] = useState<SortingState>([])
const { data, isLoading } = useQuery({ // const { data, isLoading } = useQuery({
queryKey: ["score-list"], // queryKey: ["score-list"],
queryFn: getScoreList // queryFn: getScoreList
}) // })
if (isLoading) return <p>LOADING ...</p> // if (isLoading) return <p>LOADING ...</p>
return ( return (
<Table sorting={sorting} setSorting={setSorting} columns={scoresCol} data={data?.data} tableStatus={true} /> <Table sorting={sorting} setSorting={setSorting} columns={scoresCol} data={[]} tableStatus={true} />
) )
} }
+6 -8
View File
@@ -2,17 +2,15 @@ import { useState } from "react"
import { Table } from "../common/table" import { Table } from "../common/table"
import { SortingState } from "@tanstack/react-table" import { SortingState } from "@tanstack/react-table"
import { subscriptionsCol } from "../../utility/subscription" import { subscriptionsCol } from "../../utility/subscription"
import { useQuery } from "@tanstack/react-query"
import { getSubscriptionsList } from "../../services/api/subscription"
export const SubscriptionReport = () => { export const SubscriptionReport = () => {
const [sorting, setSorting] = useState<SortingState>([]) const [sorting, setSorting] = useState<SortingState>([])
const { data, isLoading } = useQuery({ // const { data, isLoading } = useQuery({
queryKey: ["suscriptions-list"], // queryKey: ["suscriptions-list"],
queryFn: getSubscriptionsList // queryFn: getSubscriptionsList
}) // })
if (isLoading) return <p>LOADIN...</p> // if (isLoading) return <p>LOADIN...</p>
return ( return (
<Table sorting={sorting} setSorting={setSorting} columns={subscriptionsCol} data={data?.data} /> <Table sorting={sorting} setSorting={setSorting} columns={subscriptionsCol} data={[]} />
) )
} }
+6 -8
View File
@@ -2,19 +2,17 @@ import { useState } from "react"
import { Table } from "../common/table" import { Table } from "../common/table"
import { SortingState } from "@tanstack/react-table" import { SortingState } from "@tanstack/react-table"
import { transactionsCol } from "../../utility/transactions" import { transactionsCol } from "../../utility/transactions"
import { useQuery } from "@tanstack/react-query"
import { getTransactionsList } from "../../services/api/transactions"
export const Transactions = () => { export const Transactions = () => {
const [sorting, setSorting] = useState<SortingState>([]) const [sorting, setSorting] = useState<SortingState>([])
const { data, isLoading } = useQuery({ // const { data, isLoading } = useQuery({
queryKey: ["transactions-list"], // queryKey: ["transactions-list"],
queryFn: getTransactionsList // queryFn: getTransactionsList
}) // })
if (isLoading) return <p>LOADING ....</p> // if (isLoading) return <p>LOADING ....</p>
return ( return (
<Table sorting={sorting} setSorting={setSorting} columns={transactionsCol} data={data?.data} /> <Table sorting={sorting} setSorting={setSorting} columns={transactionsCol} data={[]} />
) )
} }
+5 -5
View File
@@ -100,14 +100,14 @@ export const MyAccountSchema = yup.object({
export const AddNewCardSchema = yup.object({ export const AddNewCardSchema = yup.object({
name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"), name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"),
family: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"), family: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"),
cartNumber: yup.string() PAN: yup.string()
.required("وارد کردن شماره کارت الزامی میباشد") .required("وارد کردن شماره کارت الزامی میباشد")
.min(16, "شماره کارت نمیتوند کمتر از 16 رقم باشد") .min(16, "شماره کارت نمیتوند کمتر از 16 رقم باشد")
.max(16, " شماره کارت نمیتواند بیشتر از 16 رقم باشد") .max(16, " شماره کارت نمیتواند بیشتر از 16 رقم باشد")
.matches(/^[0-9]{16}$/, "شماره کارت نامعتبر"), .matches(/^[0-9]{16}$/, "شماره کارت نامعتبر"),
shabaNumber: yup.string() IBAN: yup.string()
.required("وارد کردن شماره شبا الزامی میباشد") .required("وارد کردن شماره شبا الزامی میباشد")
.min(24, "شماره شبا نمیتوند کمتر از 24 رقم باشد") .min(26, "شماره شبا نمیتوند کمتر از 26 رقم باشد")
.max(24, " شماره شبا نمیتواند بیشتر از 24 رقم باشد") .max(26, " شماره شبا نمیتواند بیشتر از 26 رقم باشد")
.matches(/^[0-9]{24}$/, "شماره شبا نامعتبر"), .matches(/^IR[0-9]{24}$/, "شماره شبا نامعتبر"),
}) })
+1 -1
View File
@@ -1,3 +1,3 @@
import { axiosInstance } from "../axios"; import axiosInstance from "../axios";
export const loginMutation = (payload: any) => axiosInstance.post<any>("/auth/gps/login", payload) export const loginMutation = (payload: any) => axiosInstance.post<any>("/auth/gps/login", payload)
+14 -3
View File
@@ -1,4 +1,15 @@
import { LoginFormInterface } from "../../types"; import axiosInstance from "../axios";
import { axiosInstance } from "../axios";
export const getUserCardsList = () => axiosInstance.get<LoginFormInterface>("/gps/bank-card") type CreateNewCard = {
holderName: string,
PAN: string,
IBAN: string
}
type DeleteCard = {
id: string | number | null
}
export const createNewCardMutation = (payload: CreateNewCard) => axiosInstance.post<any>("/gps/bank-card", payload)
export const deleteCardMutation = (payload: DeleteCard) => axiosInstance.delete<any>(`/gps/bank-card/:${payload?.id}`)
export const getUserCardsList = () => axiosInstance.get<any>("/gps/bank-card")
-4
View File
@@ -1,4 +0,0 @@
import { installationReportsInterface } from "../../types";
import { axiosInstance } from "../axios";
export const getReportsList = () => axiosInstance.get<installationReportsInterface>("/reports")
-4
View File
@@ -1,4 +0,0 @@
import { ScoreInterface } from "../../types";
import { axiosInstance } from "../axios";
export const getScoreList = () => axiosInstance.get<ScoreInterface>("/score")
-4
View File
@@ -1,4 +0,0 @@
import { SubscriptionReportInterface } from "../../types";
import { axiosInstance } from "../axios";
export const getSubscriptionsList = () => axiosInstance.get<SubscriptionReportInterface>("/subscriptions")
-4
View File
@@ -1,4 +0,0 @@
import { TransactionsInterface } from "../../types";
import { axiosInstance } from "../axios";
export const getTransactionsList = () => axiosInstance.get<TransactionsInterface>("/transactions")
-10
View File
@@ -1,10 +0,0 @@
import axios, { AxiosRequestConfig } from "axios";
const axiosParam: AxiosRequestConfig = {
baseURL: "https://asan-service.com/api/",
headers: {
Authorization: `Bearer ${window.localStorage.getItem("token") && window.localStorage.getItem("token")}`,
}
}
export const axiosInstance = axios.create(axiosParam)
+16
View File
@@ -0,0 +1,16 @@
import axios from "axios";
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_Address,
});
axiosInstance.interceptors.request.use(async function (config) {
const search = window.location.search;
const params = new URLSearchParams(search)
const token = localStorage.getItem("token") ? localStorage.getItem("token") : params.get('token')
config.headers.Authorization = 'Bearer ' + token
return config;
});
export default axiosInstance;
+14
View File
@@ -0,0 +1,14 @@
import { create } from "zustand";
type UserType = {
token: string | null
}
const useUserStore = create<UserType>((set) => ({
token: '',
setToken: (token: string | null) => {
set({ token: token });
},
}));
export default useUserStore;
+2 -2
View File
@@ -101,8 +101,8 @@ export interface BanksInterface {
export interface AddNewCartInterface { export interface AddNewCartInterface {
name: string name: string
family: string family: string
cartNumber: string PAN: string
shabaNumber: string IBAN: string
} }
export interface installationReportsInterface { export interface installationReportsInterface {