invoice payments
This commit is contained in:
@@ -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<string | null>(null)
|
||||
const invoice = data?.data
|
||||
@@ -241,6 +245,8 @@ const InvoiceDetail: FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
{/* Notes Section */}
|
||||
<div className='mt-8 pt-6 border-t border-[#EBEDF5]'>
|
||||
<div className='text-xs text-[#8C90A3] leading-6 space-y-2'>
|
||||
@@ -250,6 +256,11 @@ const InvoiceDetail: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* لیست پرداختها */}
|
||||
<div className="mt-8">
|
||||
<InvoicePaymentList invoiceId={id!} />
|
||||
</div>
|
||||
|
||||
<ModalConfirm
|
||||
open={!!confirmingItemId}
|
||||
onClose={() => setConfirmingItemId(null)}
|
||||
|
||||
@@ -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<string, string> = {
|
||||
[PaymentStatusEnum.Pending]: 'در انتظار',
|
||||
[PaymentStatusEnum.Paid]: 'پرداخت شده',
|
||||
[PaymentStatusEnum.Failed]: 'ناموفق',
|
||||
[PaymentStatusEnum.Refunded]: 'مسترد شده',
|
||||
}
|
||||
|
||||
const methodLabels: Record<string, string> = {
|
||||
[PaymentMethodEnum.Online]: 'آنلاین',
|
||||
[PaymentMethodEnum.Cash]: 'نقد',
|
||||
[PaymentMethodEnum.Credit]: 'اعتباری',
|
||||
}
|
||||
|
||||
const gatewayLabels: Record<string, string> = {
|
||||
[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<InvoicePaymentListProps> = ({ 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 (
|
||||
<div className="bg-white rounded-2xl p-8">
|
||||
<div className="text-sm mb-6">لیست پرداختها</div>
|
||||
<Table<PaymentRowType>
|
||||
columns={columns}
|
||||
data={rows}
|
||||
showHeader={true}
|
||||
isLoading={isPending}
|
||||
noDataMessage="پرداختی برای این فاکتور ثبت نشده است."
|
||||
pagination={{
|
||||
currentPage: page,
|
||||
totalPages,
|
||||
onPageChange: setPage,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InvoicePaymentList
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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<GetPaymentInvoiceResponseType> => {
|
||||
const { data } = await axios.get<GetPaymentInvoiceResponseType>(
|
||||
`/public/payments`,
|
||||
{
|
||||
params: {
|
||||
invoiceId,
|
||||
page,
|
||||
limit,
|
||||
},
|
||||
},
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -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<PaymentItemType[]>;
|
||||
|
||||
Reference in New Issue
Block a user