adding api for get list for payment request.

This commit is contained in:
Kartik Sharma 2025-11-03 12:50:06 +05:30
parent e0c0a14777
commit f3cfb3cf24
7 changed files with 250 additions and 97 deletions

View File

@ -4,7 +4,7 @@ import Label from '../common/Label';
import { useForm } from 'react-hook-form';
import { useExpenseType } from '../../hooks/masterHook/useMaster';
import DatePicker from '../common/DatePicker';
import { useCreatePaymentRequest } from '../../hooks/usePaymentRequest';
import { useCreatePaymentRequest, useUpdatePaymentRequest } from '../../hooks/useExpense';
import { defaultPaymentRequest, PaymentRequestSchema } from '../../pages/PaymentRequest/PaymentRequestSchema';
import { zodResolver } from '@hookform/resolvers/zod';
import { formatFileSize, localToUtc } from '../../utils/appUtils';
@ -103,6 +103,9 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
handleClose();
}
);
const { mutate: PaymentRequestUpdate, isPending } = useUpdatePaymentRequest(() =>
handleClose()
);
useEffect(() => {
if (expenseToEdit && data) {
@ -140,13 +143,13 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
};
if (expenseToEdit) {
const editPayload = { ...payload, id: data.id };
ExpenseUpdate({ id: data.id, payload: editPayload });
PaymentRequestUpdate({ id: data.id, payload: editPayload });
} else {
CreatePaymentRequest(payload);
}
};
return (
<div className="container p-3">
<h5 className="m-0">

View File

@ -262,3 +262,64 @@ export const useHasAnyPermission = (permissionIdsInput) => {
return permissionIds.some((id) => permissions.includes(id));
};
// ---------------------------Payment Request---------------------------------------------
export const usePaymentRequestList = (
pageSize,
pageNumber,
filter,
isActive,
searchString = "",
) => {
return useQuery({
queryKey: ["PaymentRequest",pageSize,pageNumber,filter,isActive,searchString],
queryFn: async()=>{
const resp = await ExpenseRepository.GetPaymentRequestList(pageSize,pageNumber,filter,isActive,searchString);
return resp.data;
},
keepPreviousData: true,
});
};
// CREATE PAYMENT REQUEST
export const useCreatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (payload) => {
await ExpenseRepository.CreatePaymentRequest(payload);
},
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ["PaymentRequest"] });
showToast("Payment Created Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.message || "Something went wrong please try again !",
"error"
);
},
});
};
export const useUpdatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, payload }) => {
const response = await ExpenseRepository.UpdatePaymentRequest(id, payload);
return response.data;
},
onSuccess: (updatedExpense, variables) => {
queryClient.removeQueries({ queryKey: ["PaymentRequest", variables.id] });
queryClient.invalidateQueries({ queryKey: ["PaymentRequest"] });
showToast("PaymentRequest updated Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast("Something went wrong.Please try again later.", "error");
},
});
};

View File

@ -1,25 +0,0 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import PaymentRequestRepository from "../repositories/PaymentRequestRepository";
import showToast from "../services/toastService";
export const useCreatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (payload) => {
await PaymentRequestRepository.CreatePaymentRequest(payload);
},
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ["PaymentRequest"] });
showToast("Payment Created Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.message || "Something went wrong please try again !",
"error"
);
},
});
};

View File

@ -1,41 +1,150 @@
import React from "react";
import React, { useState } from "react";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import { useDebounce } from "../../utils/appUtils";
import { usePaymentRequestList } from "../../hooks/useExpense";
import { formatDate } from "../../utils/dateUtils";
const PaymentRequestList = () => {
const paymentRequestColumns = [
{ key: "requestId", label: "Request Id" },
{ key: "RequestType", label: "Request Type" },
{ key: "submittedBy", label: "Submitted By" },
{ key: "submittedOn", label: "Submitted On" },
{ key: "amount", label: "Amount" },
{ key: "status", label: "Status" },
];
const PaymentRequestList = ({ setManagePaymentRequestModal, search }) => {
const paymentRequestColumns = [
{ key: "paymentRequestUID", label: "Request ID", align: "text-start mx-2" },
{ key: "title", label: "Request Title", align: "text-start" },
{ key: "payee", label: "Payee", align: "text-start" },
{ key: "createdAt", label: "Submitted On", align: "text-start" },
{ key: "amount", label: "Amount", align: "text-start" },
{ key: "expenseStatus", label: "Status", align: "text-start" },
];
return (
<div className="card page-min-h table-responsive px-sm-4">
<div className="card-datatable " id="payment-request-table">
<table className="table border-top dataTable text-nowrap">
<thead>
<tr>
{paymentRequestColumns.map((col) => (
<th key={col.key} className="sorting text-start">
{col.label}
</th>
))}
<th className="text-center">Action</th>
</tr>
</thead>
<tbody>
{/* Data rows will be added here later */}
<tr>
<td colSpan={paymentRequestColumns.length + 1} className="text-center py-4">
No Payment Request Found
</td>
</tr>
</tbody>
</table>
</div>
</div>
);
const [currentPage, setCurrentPage] = useState(1);
const debouncedSearch = useDebounce(search, 500);
const { data, isLoading, isError, error, isFetching } = usePaymentRequestList(
ITEMS_PER_PAGE,
currentPage,
{},
true,
debouncedSearch
);
const paymentRequestData = data?.data || [];
const totalPages = data?.data?.totalPages || 1;
if (isError) {
return (
<div className="text-center text-danger py-5">
<p>Failed to load payment requests.</p>
<small>{error?.message || "Something went wrong."}</small>
</div>
);
}
return (
<div className="card page-min-h table-responsive px-sm-4">
<div className="card-datatable" id="payment-request-table">
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
{paymentRequestColumns.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
<th className="text-center">Action</th>
</tr>
</thead>
<tbody>
{isLoading || isFetching ? (
<tr>
<td
colSpan={paymentRequestColumns.length + 1}
className="text-center py-4"
>
Loading Payment Requests...
</td>
</tr>
) : paymentRequestData.length > 0 ? (
paymentRequestData.map((row) => (
<tr key={row.id}>
<td>{row.paymentRequestUID}</td>
<td>{row.title}</td>
<td>{row.payee}</td>
<td>{formatDate(row.createdAt)}</td>
<td>
{row.currency?.symbol}
{row.amount}
</td>
<td>
<span
className="badge"
style={{
backgroundColor: row.expenseStatus?.color || "#6c757d",
}}
>
{row.expenseStatus?.displayName || "Unknown"}
</span>
</td>
<td className="text-center">
<div className="d-flex justify-content-center gap-2">
<i
className="bx bx-show text-primary cursor-pointer"
title="View"
onClick={() =>
console.log("View clicked for:", row.paymentRequestUID)
}
></i>
<i
className="bx bx-edit text-secondary cursor-pointer"
title="Edit"
onClick={() =>
setManagePaymentRequestModal({
IsOpen: true,
expenseId: row.id, // Pass ID for editing
})
}
></i>
</div>
</td>
</tr>
))
) : (
<tr>
<td
colSpan={paymentRequestColumns.length + 1}
className="text-center py-4"
>
No Payment Requests Found
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* Pagination */}
{totalPages > 1 && (
<div className="d-flex justify-content-end py-3 pe-3">
<nav>
<ul className="pagination mb-0">
{[...Array(totalPages)].map((_, index) => (
<li
key={index}
className={`page-item ${currentPage === index + 1 ? "active" : ""}`}
>
<button
className="page-link"
onClick={() => setCurrentPage(index + 1)}
>
{index + 1}
</button>
</li>
))}
</ul>
</nav>
</div>
)}
</div>
);
};
export default PaymentRequestList;

View File

@ -3,16 +3,25 @@ import Breadcrumb from "../../components/common/Breadcrumb";
import GlobalModel from "../../components/common/GlobalModel";
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
import PaymentRequestList from "./PaymentRequestList";
const PaymentRequestPage = () => {
const [ManagePaymentRequestModal, setManagePaymentRequestModal] = useState({
IsOpen: null,
expenseId: null,
});
const [search, setSearch] = useState("");
return (
<div className="container-fluid">
{/* Breadcrumb */}
<Breadcrumb data={[{ label: "Home", link: "/" }, { label: "Finance", link: "/Payment Request" }, { label: "Payment Request" }]} />
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Finance", link: "/Payment Request" },
{ label: "Payment Request" },
]}
/>
{/* Top Bar */}
<div className="card my-3 px-sm-4 px-0">
@ -23,6 +32,8 @@ const PaymentRequestPage = () => {
type="search"
className="form-control form-control-sm w-auto"
placeholder="Search Payment Req.."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
@ -38,13 +49,16 @@ const PaymentRequestPage = () => {
}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">Add Payment Request</span>
<span className="d-none d-md-inline-block">
Add Payment Request
</span>
</button>
</div>
</div>
</div>
</div>
{/* Add/Edit Modal */}
{ManagePaymentRequestModal.IsOpen && (
<GlobalModel
isOpen
@ -63,15 +77,13 @@ const PaymentRequestPage = () => {
</GlobalModel>
)}
{/* Expense List Placeholder */}
{/* Payment Request List */}
<div className="card">
{/* <div className="card-body text-center text-muted py-5">
<p>No Expense Data found</p>
</div> */}
<PaymentRequestList/>
<PaymentRequestList
search={search}
setManagePaymentRequestModal={setManagePaymentRequestModal}
/>
</div>
</div>
);
};

View File

@ -2,22 +2,27 @@ import { api } from "../utils/axiosClient";
const ExpenseRepository = {
GetExpenseList: ( pageSize, pageNumber, filter,searchString ) => {
GetExpenseList: (pageSize, pageNumber, filter, searchString) => {
const payloadJsonString = JSON.stringify(filter);
return api.get(`/api/expense/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`);
},
GetExpenseDetails:(id)=>api.get(`/api/Expense/details/${id}`),
CreateExpense:(data)=>api.post("/api/Expense/create",data),
UpdateExpense:(id,data)=>api.put(`/api/Expense/edit/${id}`,data),
DeleteExpense:(id)=>api.delete(`/api/Expense/delete/${id}`),
ActionOnExpense:(data)=>api.post('/api/expense/action',data),
GetExpenseFilter:()=>api.get('/api/Expense/filter')
GetExpenseDetails: (id) => api.get(`/api/Expense/details/${id}`),
CreateExpense: (data) => api.post("/api/Expense/create", data),
UpdateExpense: (id, data) => api.put(`/api/Expense/edit/${id}`, data),
DeleteExpense: (id) => api.delete(`/api/Expense/delete/${id}`),
ActionOnExpense: (data) => api.post('/api/expense/action', data),
GetExpenseFilter: () => api.get('/api/Expense/filter'),
GetPaymentRequestList: (pageSize, pageNumber, filter, isActive, searchString) => {
const payloadJsonString = JSON.stringify(filter);
return api.get(`/api/Expense/get/payment-requests/list?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`);
},
CreatePaymentRequest: (data) => api.post("/api/expense/payment-request/create", data),
UpdatePaymentRequest: (id, data) => api.put(`/api/Expense/payment-request/edit/${id}`, data),
}

View File

@ -1,12 +0,0 @@
import { api } from "../utils/axiosClient";
const PaymentRequestRepository = {
CreatePaymentRequest:(data)=>api.post("/api/expense/payment-request/create",data),
}
export default PaymentRequestRepository;