added api Get Advance Payment

This commit is contained in:
pramod.mahajan 2025-11-03 14:48:42 +05:30
parent e80223beba
commit 871daf6036
11 changed files with 214 additions and 181 deletions

View File

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import { useExpenseTransactions } from "../../hooks/useExpense";
const AdvancePaymentList = ({ employeeId }) => { const AdvancePaymentList = ({ employeeId }) => {
const {data,isError, isLoading,isFetching,error} = useExpenseTransactions(employeeId)
const record = { const record = {
openingBalance: 25000.0, openingBalance: 25000.0,
rows: [ rows: [
@ -71,7 +73,6 @@ const AdvancePaymentList = ({ employeeId }) => {
{ id: 20, description: "Closing Balance", credit: 0.0, debit: 0.0 }, { id: 20, description: "Closing Balance", credit: 0.0, debit: 0.0 },
], ],
}; };
let currentBalance = record.openingBalance; let currentBalance = record.openingBalance;
const rowsWithBalance = record.rows.map((r) => { const rowsWithBalance = record.rows.map((r) => {
currentBalance += r.credit - r.debit; currentBalance += r.credit - r.debit;
@ -134,6 +135,7 @@ const AdvancePaymentList = ({ employeeId }) => {
currency: "INR", currency: "INR",
}) })
: (<div className="d-flex flex-column"> : (<div className="d-flex flex-column">
<small>{new Date().toISOString()}</small>
<small>Projec Name</small> <small>Projec Name</small>
<small>{row[col.key]}</small> <small>{row[col.key]}</small>
</div>)} </div>)}

View File

@ -184,8 +184,8 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
), ),
}, },
]; ];
const headers = ["Expense Type","Payment Mode","Submitted By","Submitted","Amount","Status","Action"]
if (isInitialLoading && !data) return <ExpenseTableSkeleton />; if (isInitialLoading && !data) return <ExpenseTableSkeleton headers={headers} />;
if (isError) return <div>{error?.message}</div>; if (isError) return <div>{error?.message}</div>;
const grouped = groupBy const grouped = groupBy

View File

@ -143,7 +143,7 @@ const SkeletonCell = ({
/> />
); );
export const ExpenseTableSkeleton = ({ groups = 3, rowsPerGroup = 3 }) => { export const ExpenseTableSkeleton = ({ groups = 3, rowsPerGroup = 3, headers }) => {
return ( return (
<div className="card px-2"> <div className="card px-2">
<table <table
@ -153,17 +153,12 @@ export const ExpenseTableSkeleton = ({ groups = 3, rowsPerGroup = 3 }) => {
> >
<thead> <thead>
<tr> <tr>
{headers.map((header)=>(
<th className="d-none d-sm-table-cell"> <th className="d-none d-sm-table-cell">
<div className="text-start ms-5">Expense Type</div> <div className="text-start ms-5">{header}</div>
</th> </th>
<th className="d-none d-sm-table-cell"> ))}
<div className="text-start ms-5">Payment Mode</div>
</th>
<th className="d-none d-sm-table-cell">Submitted By</th>
<th className="d-none d-sm-table-cell">Submitted</th>
<th className="d-none d-md-table-cell">Amount</th>
<th>Status</th>
<th>Action</th>
</tr> </tr>
</thead> </thead>

View File

@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form';
import { useExpenseType } from '../../hooks/masterHook/useMaster'; import { useExpenseType } from '../../hooks/masterHook/useMaster';
import DatePicker from '../common/DatePicker'; import DatePicker from '../common/DatePicker';
import { useCreatePaymentRequest, useUpdatePaymentRequest } from '../../hooks/useExpense'; import { useCreatePaymentRequest, useUpdatePaymentRequest } from '../../hooks/useExpense';
import { defaultPaymentRequest, PaymentRequestSchema } from '../../pages/PaymentRequest/PaymentRequestSchema'; import { defaultPaymentRequest, PaymentRequestSchema } from './PaymentRequestSchema';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { formatFileSize, localToUtc } from '../../utils/appUtils'; import { formatFileSize, localToUtc } from '../../utils/appUtils';

View File

@ -0,0 +1,168 @@
import React, { useState } from "react";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import { useDebounce } from "../../utils/appUtils";
import { formatDate } from "../../utils/dateUtils";
import { usePaymentRequestList } from "../../hooks/useExpense";
import ExpenseSkeleton, {
ExpenseTableSkeleton,
} from "../Expenses/ExpenseSkeleton";
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" },
];
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>
);
}
const header = [
"Expense ID",
"Expense Category",
"Payment Mode",
"Sumitted By",
"Submitted",
"Amount",
"Status",
"Action",
];
if (isLoading) return <ExpenseTableSkeleton headers={header} />;
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

@ -263,7 +263,8 @@ export const useHasAnyPermission = (permissionIdsInput) => {
return permissionIds.some((id) => permissions.includes(id)); return permissionIds.some((id) => permissions.includes(id));
}; };
// ---------------------------Payment Request--------------------------------------------- //#region Payment Request
// ---------------------------Get Payment Request---------------------------------------------
export const usePaymentRequestList = ( export const usePaymentRequestList = (
pageSize, pageSize,
pageNumber, pageNumber,
@ -281,10 +282,9 @@ export const usePaymentRequestList = (
}); });
}; };
// CREATE PAYMENT REQUEST // ---------------------------Put Post Payment Request---------------------------------------
export const useCreatePaymentRequest = (onSuccessCallBack) => { export const useCreatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (payload) => { mutationFn: async (payload) => {
await ExpenseRepository.CreatePaymentRequest(payload); await ExpenseRepository.CreatePaymentRequest(payload);
@ -303,7 +303,6 @@ export const useCreatePaymentRequest = (onSuccessCallBack) => {
}, },
}); });
}; };
export const useUpdatePaymentRequest = (onSuccessCallBack) => { export const useUpdatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
@ -323,3 +322,18 @@ export const useUpdatePaymentRequest = (onSuccessCallBack) => {
}, },
}); });
}; };
//#endregion
//#region Advance Payment
export const useExpenseTransactions = (employeeId)=>{
return useQuery({
queryKey:["transaction",employeeId],
queryFn:async()=> {
debugger
const resp = await ExpenseRepository.GetTranctionList(employeeId);
return resp.data
},
enabled:!!employeeId
})
}
//#endregion

View File

@ -30,14 +30,14 @@ const AdvancePaymentPage = () => {
<Label>Search Employee</Label> <Label>Search Employee</Label>
<EmployeeSearchInput <EmployeeSearchInput
control={control} control={control}
name="paidById" name="employeeId"
projectId={null} projectId={null}
forAll={true} forAll={true}
/> />
</div> </div>
</div> </div>
</div> </div>
<AdvancePaymentList/> <AdvancePaymentList employeeId={selectedEmployeeId}/>
</div> </div>
</div> </div>
); );

View File

@ -1,150 +0,0 @@
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 = ({ 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" },
];
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

@ -1,8 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Breadcrumb from "../../components/common/Breadcrumb"; import Breadcrumb from "../../components/common/Breadcrumb";
import GlobalModel from "../../components/common/GlobalModel"; import GlobalModel from "../../components/common/GlobalModel";
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest"; import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
import PaymentRequestList from "./PaymentRequestList";
const PaymentRequestPage = () => { const PaymentRequestPage = () => {
const [ManagePaymentRequestModal, setManagePaymentRequestModal] = useState({ const [ManagePaymentRequestModal, setManagePaymentRequestModal] = useState({
@ -67,7 +66,7 @@ const PaymentRequestPage = () => {
setManagePaymentRequestModal({ IsOpen: null, expenseId: null }) setManagePaymentRequestModal({ IsOpen: null, expenseId: null })
} }
> >
<ManagePaymentRequest <ManagePaymentRequestModal
key={ManagePaymentRequestModal.expenseId ?? "new"} key={ManagePaymentRequestModal.expenseId ?? "new"}
expenseToEdit={ManagePaymentRequestModal.expenseId} expenseToEdit={ManagePaymentRequestModal.expenseId}
closeModal={() => closeModal={() =>

View File

@ -2,28 +2,33 @@ import { api } from "../utils/axiosClient";
const ExpenseRepository = { const ExpenseRepository = {
//#region Expense
GetExpenseList: (pageSize, pageNumber, filter, searchString) => { GetExpenseList: (pageSize, pageNumber, filter, searchString) => {
const payloadJsonString = JSON.stringify(filter); const payloadJsonString = JSON.stringify(filter);
return api.get(`/api/expense/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`); return api.get(`/api/expense/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`);
}, },
GetExpenseDetails: (id) => api.get(`/api/Expense/details/${id}`), GetExpenseDetails: (id) => api.get(`/api/Expense/details/${id}`),
CreateExpense: (data) => api.post("/api/Expense/create", data), CreateExpense: (data) => api.post("/api/Expense/create", data),
UpdateExpense: (id, data) => api.put(`/api/Expense/edit/${id}`, data), UpdateExpense: (id, data) => api.put(`/api/Expense/edit/${id}`, data),
DeleteExpense: (id) => api.delete(`/api/Expense/delete/${id}`), DeleteExpense: (id) => api.delete(`/api/Expense/delete/${id}`),
ActionOnExpense: (data) => api.post('/api/expense/action', data), ActionOnExpense: (data) => api.post('/api/expense/action', data),
GetExpenseFilter: () => api.get('/api/Expense/filter'), GetExpenseFilter: () => api.get('/api/Expense/filter'),
//#endregion
//#region Payment Request
GetPaymentRequestList: (pageSize, pageNumber, filter, isActive, searchString) => { GetPaymentRequestList: (pageSize, pageNumber, filter, isActive, searchString) => {
const payloadJsonString = JSON.stringify(filter); const payloadJsonString = JSON.stringify(filter);
return api.get(`/api/Expense/get/payment-requests/list?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`); 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), CreatePaymentRequest: (data) => api.post("/api/expense/payment-request/create", data),
UpdatePaymentRequest: (id, data) => api.put(`/api/Expense/payment-request/edit/${id}`, data), UpdatePaymentRequest: (id, data) => api.put(`/api/Expense/payment-request/edit/${id}`, data),
//#endregion
//#region Advance Payment
GetTranctionList:()=>api.get(`/get/transactions/${employeeId}`)
//#endregion
} }
export default ExpenseRepository; export default ExpenseRepository;