diff --git a/src/config/Paths.tsx b/src/config/Paths.tsx index aadcda4..2a0aba5 100644 --- a/src/config/Paths.tsx +++ b/src/config/Paths.tsx @@ -30,4 +30,7 @@ export const Paths = { auth: { login: '/login', }, + payment: { + payInvoice: '/payments/invoice/', + }, } \ No newline at end of file diff --git a/src/pages/invoice/Detail.tsx b/src/pages/invoice/Detail.tsx index 1641902..2418a70 100644 --- a/src/pages/invoice/Detail.tsx +++ b/src/pages/invoice/Detail.tsx @@ -4,12 +4,13 @@ import ModalConfirm from '@/components/ModalConfirm' import Button from '@/components/Button' import Table from '@/components/Table' import type { RowDataType } from '@/components/types/TableTypes' -import { useParams } from 'react-router-dom' +import { Link, useParams } from 'react-router-dom' import { useQueryClient } from '@tanstack/react-query' import { useConfirmInvoiceItem, useGetInvoiceDetail } from './hooks/useInvoiceData' import { NumberFormat } from '@/config/func' import moment from 'moment-jalaali' import type { InvoiceItem as ApiInvoiceItem } from './types/InvoiceTypes' +import { Paths } from '@/config/Paths' interface TableInvoiceItem extends RowDataType { id: string @@ -135,7 +136,7 @@ const InvoiceDetail: FC = () => { ) } - const isPaid = invoice && invoice.paidAmount >= invoice.total && invoice.total > 0 + // const isPaid = invoice && invoice.paidAmount >= invoice.total && invoice.total > 0 return (
@@ -152,15 +153,13 @@ const InvoiceDetail: FC = () => { چاپ - + + +
diff --git a/src/pages/payment/PayInvoice.tsx b/src/pages/payment/PayInvoice.tsx new file mode 100644 index 0000000..184e323 --- /dev/null +++ b/src/pages/payment/PayInvoice.tsx @@ -0,0 +1,246 @@ +import { type FC, useState } from 'react' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { useParams } from 'react-router-dom' +import Input from '@/components/Input' +import Select from '@/components/Select' +import type { ItemsSelectType } from '@/components/Select' +import Textarea from '@/components/Textarea' +import UploadBox from '@/components/UploadBox' +import Button from '@/components/Button' +import { TickCircle } from 'iconsax-react' +import { useGetInvoiceDetail } from '@/pages/invoice/hooks/useInvoiceData' +import { usePayInvoice } from './hooks/usePaymentData' +import { useMultiUpload } from '@/pages/uploader/hooks/useUploader' +import { NumberFormat } from '@/config/func' +import { toast } from '@/shared/toast' +import type { ErrorType } from '@/helpers/types' +import type { PayInvoiceParamsType } from './types/Types' +import { PaymentGatewayEnum, PaymentMethodEnum } from './enum/Enum' +const methodItems: ItemsSelectType[] = [ + { value: PaymentMethodEnum.Online, label: 'آنلاین' }, + { value: PaymentMethodEnum.Cash, label: 'نقد' }, + { value: PaymentMethodEnum.Credit, label: 'اعتباری' }, +] + +const gatewayItems: ItemsSelectType[] = [ + { value: PaymentGatewayEnum.ZarinPal, label: 'زرین‌پال' }, +] + +const formatPrice = (num: number) => `${NumberFormat(num)} تومان` + +const PayInvoice: FC = () => { + const { id } = useParams() + const { data, isPending } = useGetInvoiceDetail(id ?? '') + const { mutate: payInvoice, isPending: isPaying } = usePayInvoice() + const multiUpload = useMultiUpload() + const [attachmentFiles, setAttachmentFiles] = useState([]) + + const invoice = data?.data + + const formik = useFormik({ + initialValues: { + amount: invoice?.balance ?? 0, + method: PaymentMethodEnum.Online, + gateway: PaymentGatewayEnum.ZarinPal, + description: '', + attachments: [], + }, + validationSchema: Yup.object({ + amount: Yup.number() + .required('مبلغ اجباری است') + .min(1, 'مبلغ باید بیشتر از صفر باشد'), + method: Yup.string().required('روش پرداخت اجباری است'), + gateway: Yup.string().when('method', { + is: PaymentMethodEnum.Online, + then: (schema) => schema.required('درگاه پرداخت اجباری است'), + }), + description: Yup.string(), + attachments: Yup.array().of(Yup.string()), + }), + enableReinitialize: true, + onSubmit: async (values) => { + if (!id) return + + let attachments: string[] = values.attachments ?? [] + + if (attachmentFiles.length > 0) { + try { + const uploadResult = await multiUpload.mutateAsync(attachmentFiles) + attachments = (uploadResult?.data ?? []).map( + (item: { url?: string }) => item?.url ?? '' + ).filter(Boolean) + } catch (error) { + toast((error as ErrorType)?.response?.data?.error?.message?.[0] ?? 'خطا در آپلود فایل‌ها', 'error') + return + } + } + + payInvoice( + { + invoiceId: id, + params: { + ...values, + attachments, + }, + }, + { + onSuccess: (res) => { + const paymentUrl = res?.data?.paymentUrl + if (paymentUrl) { + toast('در حال انتقال به درگاه پرداخت', 'success') + window.location.href = paymentUrl + return + } + toast('پرداخت با موفقیت ثبت شد', 'success') + }, + onError: (error: ErrorType) => { + toast( + error?.response?.data?.error?.message?.[0] ?? 'خطا در ثبت پرداخت', + 'error' + ) + }, + } + ) + }, + }) + + if (isPending && !invoice) { + return ( +
+ در حال بارگذاری... +
+ ) + } + + if (!id || !invoice) { + return ( +
+ صورت حساب یافت نشد +
+ ) + } + + return ( +
+
پرداخت صورت حساب
+ + {/* خلاصه صورت حساب */} +
+
اطلاعات صورت حساب
+
+
+ شماره صورت حساب: + {invoice.invoiceNumber} +
+
+ مبلغ کل: + {formatPrice(invoice.total)} +
+
+ پرداخت شده: + {formatPrice(invoice.paidAmount)} +
+
+ مانده: + {formatPrice(invoice.balance)} +
+
+
+ + {/* فرم پرداخت */} +
+
ثبت پرداخت
+ +
+ + formik.setFieldValue( + 'amount', + Number((e.target.value ?? '').replace(/,/g, '')) || 0 + ) + } + onBlur={formik.handleBlur} + seprator + error_text={ + formik.touched.amount && formik.errors.amount + ? formik.errors.amount + : '' + } + /> + + + )} + + {formik.values.method !== PaymentMethodEnum.Online && ( + <> +