diff --git a/src/pages/invoice/Detail.tsx b/src/pages/invoice/Detail.tsx index 2418a70..a9ae624 100644 --- a/src/pages/invoice/Detail.tsx +++ b/src/pages/invoice/Detail.tsx @@ -11,6 +11,8 @@ import { NumberFormat } from '@/config/func' import moment from 'moment-jalaali' import type { InvoiceItem as ApiInvoiceItem } from './types/InvoiceTypes' import { Paths } from '@/config/Paths' +import { useGetPaymentInvoice } from '../payment/hooks/usePaymentData' +import InvoicePaymentList from './InvoicePaymentList' interface TableInvoiceItem extends RowDataType { id: string @@ -26,9 +28,11 @@ interface TableInvoiceItem extends RowDataType { const formatPrice = (num: number) => `${NumberFormat(num)} تومان` const InvoiceDetail: FC = () => { + const { id } = useParams() const queryClient = useQueryClient() const { data, isPending } = useGetInvoiceDetail(id!) + useGetPaymentInvoice(id) const { mutate: confirmInvoiceItem, isPending: isConfirming } = useConfirmInvoiceItem() const [confirmingItemId, setConfirmingItemId] = useState(null) const invoice = data?.data @@ -241,6 +245,8 @@ const InvoiceDetail: FC = () => { )} + + {/* Notes Section */}
@@ -250,6 +256,11 @@ const InvoiceDetail: FC = () => {
+ {/* لیست پرداخت‌ها */} +
+ +
+ setConfirmingItemId(null)} diff --git a/src/pages/invoice/InvoicePaymentList.tsx b/src/pages/invoice/InvoicePaymentList.tsx new file mode 100644 index 0000000..66f2935 --- /dev/null +++ b/src/pages/invoice/InvoicePaymentList.tsx @@ -0,0 +1,98 @@ +import { type FC, useState } from 'react' +import Table from '@/components/Table' +import type { RowDataType } from '@/components/types/TableTypes' +import { NumberFormat } from '@/config/func' +import moment from 'moment-jalaali' +import { useGetPaymentInvoice } from '../payment/hooks/usePaymentData' +import type { PaymentItemType } from '../payment/types/Types' +import { + PaymentMethodEnum, + PaymentStatusEnum, + PaymentGatewayEnum, +} from '../payment/enum/Enum' + +interface PaymentRowType extends RowDataType { + id: string + amount: string + method: string + gateway: string + status: string + date: string + transactionId: string + description: string +} + +const formatPrice = (num: number) => `${NumberFormat(num)} تومان` + +const statusLabels: Record = { + [PaymentStatusEnum.Pending]: 'در انتظار', + [PaymentStatusEnum.Paid]: 'پرداخت شده', + [PaymentStatusEnum.Failed]: 'ناموفق', + [PaymentStatusEnum.Refunded]: 'مسترد شده', +} + +const methodLabels: Record = { + [PaymentMethodEnum.Online]: 'آنلاین', + [PaymentMethodEnum.Cash]: 'نقد', + [PaymentMethodEnum.Credit]: 'اعتباری', +} + +const gatewayLabels: Record = { + [PaymentGatewayEnum.ZarinPal]: 'زرین‌پال', +} + +const mapPaymentToRow = (p: PaymentItemType): PaymentRowType => { + const date = p.paidAt || p.failedAt || p.createdAt + return { + id: p.id, + amount: formatPrice(p.amount), + method: methodLabels[p.method as keyof typeof methodLabels] ?? p.method, + gateway: p.gateway ? (gatewayLabels[p.gateway as keyof typeof gatewayLabels] ?? p.gateway) : '-', + status: statusLabels[p.status as keyof typeof statusLabels] ?? p.status, + date: date ? moment(date).format('jYYYY/jMM/jDD - HH:mm') : '-', + transactionId: p.transactionId ?? '-', + description: p.description ?? '-', + } +} + +interface InvoicePaymentListProps { + invoiceId: string +} + +const InvoicePaymentList: FC = ({ invoiceId }) => { + const [page, setPage] = useState(1) + const { data, isPending } = useGetPaymentInvoice(invoiceId, page) + const payments = data?.data ?? [] + const rows: PaymentRowType[] = payments.map(mapPaymentToRow) + const totalPages = data?.meta?.totalPages ?? 1 + + const columns = [ + { key: 'amount', title: 'مبلغ' }, + { key: 'method', title: 'روش پرداخت' }, + { key: 'gateway', title: 'درگاه' }, + { key: 'status', title: 'وضعیت' }, + { key: 'date', title: 'تاریخ' }, + { key: 'transactionId', title: 'شناسه تراکنش' }, + { key: 'description', title: 'توضیحات' }, + ] + + return ( +
+
لیست پرداخت‌ها
+ + columns={columns} + data={rows} + showHeader={true} + isLoading={isPending} + noDataMessage="پرداختی برای این فاکتور ثبت نشده است." + pagination={{ + currentPage: page, + totalPages, + onPageChange: setPage, + }} + /> +
+ ) +} + +export default InvoicePaymentList diff --git a/src/pages/payment/hooks/usePaymentData.ts b/src/pages/payment/hooks/usePaymentData.ts index 6f7a918..a6166ba 100644 --- a/src/pages/payment/hooks/usePaymentData.ts +++ b/src/pages/payment/hooks/usePaymentData.ts @@ -1,4 +1,4 @@ -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import * as api from "../service/PaymentService"; import type { PayInvoiceParamsType } from "../types/Types"; @@ -13,3 +13,14 @@ export const usePayInvoice = () => { }) => api.payInvoice(invoiceId, params), }); }; + +export const useGetPaymentInvoice = ( + invoiceId?: string, + page: number = 1, +) => { + return useQuery({ + queryKey: ["payment", invoiceId, page], + queryFn: () => api.getPaymentInvoice(invoiceId, page), + enabled: !!invoiceId, + }); +}; diff --git a/src/pages/payment/service/PaymentService.ts b/src/pages/payment/service/PaymentService.ts index 97c08a6..43cf2b6 100644 --- a/src/pages/payment/service/PaymentService.ts +++ b/src/pages/payment/service/PaymentService.ts @@ -1,5 +1,8 @@ import axios from "@/config/axios"; -import type { PayInvoiceParamsType } from "../types/Types"; +import type { + GetPaymentInvoiceResponseType, + PayInvoiceParamsType, +} from "../types/Types"; export const payInvoice = async ( invoiceId: string, @@ -11,3 +14,23 @@ export const payInvoice = async ( ); return data; }; + +const DEFAULT_PAGE_SIZE = 10; + +export const getPaymentInvoice = async ( + invoiceId?: string, + page: number = 1, + limit: number = DEFAULT_PAGE_SIZE, +): Promise => { + const { data } = await axios.get( + `/public/payments`, + { + params: { + invoiceId, + page, + limit, + }, + }, + ); + return data; +}; diff --git a/src/pages/payment/types/Types.ts b/src/pages/payment/types/Types.ts index 9e8befd..860d648 100644 --- a/src/pages/payment/types/Types.ts +++ b/src/pages/payment/types/Types.ts @@ -1,4 +1,9 @@ -import type { PaymentGatewayEnum, PaymentMethodEnum } from "../enum/Enum"; +import type { BaseResponse } from "@/shared/types/Types"; +import type { + PaymentGatewayEnum, + PaymentMethodEnum, + PaymentStatusEnum, +} from "../enum/Enum"; export type PayInvoiceParamsType = { amount: number; @@ -7,3 +12,24 @@ export type PayInvoiceParamsType = { description: string; attachments: string[]; }; + +export type PaymentItemType = { + id: string; + createdAt: string; + deletedAt: string | null; + invoice: string; + creator: string | null; + amount: number; + token: string | null; + method: PaymentMethodEnum | string; + gateway: PaymentGatewayEnum | string | null; + transactionId: string | null; + status: PaymentStatusEnum | string; + verifyResponse: unknown; + paidAt: string | null; + failedAt: string | null; + attachments: string[] | null; + description: string | null; +}; + +export type GetPaymentInvoiceResponseType = BaseResponse;