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

View File

@ -184,8 +184,8 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
),
},
];
if (isInitialLoading && !data) return <ExpenseTableSkeleton />;
const headers = ["Expense Type","Payment Mode","Submitted By","Submitted","Amount","Status","Action"]
if (isInitialLoading && !data) return <ExpenseTableSkeleton headers={headers} />;
if (isError) return <div>{error?.message}</div>;
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 (
<div className="card px-2">
<table
@ -153,17 +153,12 @@ export const ExpenseTableSkeleton = ({ groups = 3, rowsPerGroup = 3 }) => {
>
<thead>
<tr>
<th className="d-none d-sm-table-cell">
<div className="text-start ms-5">Expense Type</div>
{headers.map((header)=>(
<th className="d-none d-sm-table-cell">
<div className="text-start ms-5">{header}</div>
</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>
</thead>

View File

@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form';
import { useExpenseType } from '../../hooks/masterHook/useMaster';
import DatePicker from '../common/DatePicker';
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 { 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));
};
// ---------------------------Payment Request---------------------------------------------
//#region Payment Request
// ---------------------------Get Payment Request---------------------------------------------
export const usePaymentRequestList = (
pageSize,
pageNumber,
@ -281,10 +282,9 @@ export const usePaymentRequestList = (
});
};
// CREATE PAYMENT REQUEST
// ---------------------------Put Post Payment Request---------------------------------------
export const useCreatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (payload) => {
await ExpenseRepository.CreatePaymentRequest(payload);
@ -303,7 +303,6 @@ export const useCreatePaymentRequest = (onSuccessCallBack) => {
},
});
};
export const useUpdatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient();
return useMutation({
@ -322,4 +321,19 @@ export const useUpdatePaymentRequest = (onSuccessCallBack) => {
showToast("Something went wrong.Please try again later.", "error");
},
});
};
};
//#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>
<EmployeeSearchInput
control={control}
name="paidById"
name="employeeId"
projectId={null}
forAll={true}
/>
</div>
</div>
</div>
<AdvancePaymentList/>
<AdvancePaymentList employeeId={selectedEmployeeId}/>
</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 Breadcrumb from "../../components/common/Breadcrumb";
import GlobalModel from "../../components/common/GlobalModel";
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
import PaymentRequestList from "./PaymentRequestList";
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
const PaymentRequestPage = () => {
const [ManagePaymentRequestModal, setManagePaymentRequestModal] = useState({
@ -33,7 +32,7 @@ const PaymentRequestPage = () => {
className="form-control form-control-sm w-auto"
placeholder="Search Payment Req.."
value={search}
onChange={(e) => setSearch(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
@ -67,7 +66,7 @@ const PaymentRequestPage = () => {
setManagePaymentRequestModal({ IsOpen: null, expenseId: null })
}
>
<ManagePaymentRequest
<ManagePaymentRequestModal
key={ManagePaymentRequestModal.expenseId ?? "new"}
expenseToEdit={ManagePaymentRequestModal.expenseId}
closeModal={() =>
@ -80,7 +79,7 @@ const PaymentRequestPage = () => {
{/* Payment Request List */}
<div className="card">
<PaymentRequestList
search={search}
search={search}
setManagePaymentRequestModal={setManagePaymentRequestModal}
/>
</div>

View File

@ -2,28 +2,33 @@ import { api } from "../utils/axiosClient";
const ExpenseRepository = {
//#region Expense
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'),
//#endregion
//#region Payment Request
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),
//#endregion
//#region Advance Payment
GetTranctionList:()=>api.get(`/get/transactions/${employeeId}`)
//#endregion
}
export default ExpenseRepository;