Merge pull request 'Advance_Payment_List :- Implementing advance Payment new API.' (#514) from Advance_Payment_List into Project_Branch_Management

Reviewed-on: #514
merged
This commit is contained in:
pramod.mahajan 2025-11-20 09:08:07 +00:00
commit bd43475d12
12 changed files with 229 additions and 63 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect, useMemo } from "react"; import React, { useEffect, useMemo } from "react";
import { useExpenseTransactions } from "../../hooks/useExpense"; import { useExpenseAllTransactionsList, useExpenseTransactions } from "../../hooks/useExpense";
import Error from "../common/Error"; import Error from "../common/Error";
import { formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Loader, { SpinnerLoader } from "../common/Loader"; import Loader, { SpinnerLoader } from "../common/Loader";
@ -11,11 +11,10 @@ import { employee } from "../../data/masters";
import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPage"; import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPage";
import { formatFigure } from "../../utils/appUtils"; import { formatFigure } from "../../utils/appUtils";
const AdvancePaymentList = ({ employeeId }) => { const AdvancePaymentList = ({ employeeId, searchString }) => {
const { setBalance } = useAdvancePaymentContext(); const { setBalance } = useAdvancePaymentContext();
const { data, isError, isLoading, error, isFetching } = const { data, isError, isLoading, error, isFetching } =
useExpenseTransactions(employeeId, { enabled: !!employeeId }); useExpenseTransactions(employeeId, { enabled: !!employeeId });
const records = Array.isArray(data) ? data : []; const records = Array.isArray(data) ? data : [];
let currentBalance = 0; let currentBalance = 0;

View File

@ -0,0 +1,100 @@
import React from 'react'
import Avatar from "../../components/common/Avatar"; // <-- ADD THIS
import { useExpenseAllTransactionsList } from '../../hooks/useExpense';
import { useNavigate } from 'react-router-dom';
import { formatFigure } from '../../utils/appUtils';
const AdvancePaymentList1 = ({ searchString }) => {
const { data, isError, isLoading, error } =
useExpenseAllTransactionsList(searchString);
const rows = data || [];
const navigate = useNavigate();
const columns = [
{
key: "employee",
label: "Employee Name",
align: "text-start",
customRender: (r) => (
<div className="d-flex align-items-center gap-2" onClick={() => navigate(`/advance-payment/${r.id}`)}
style={{ cursor: "pointer" }}>
<Avatar firstName={r.firstName} lastName={r.lastName} />
<span className="fw-medium">
{r.firstName} {r.lastName}
</span>
</div>
),
},
{
key: "jobRoleName",
label: "Job Role",
align: "text-start",
customRender: (r) => (
<span className="fw-semibold">
{r.jobRoleName}
</span>
),
},
{
key: "balanceAmount",
label: "Balance (₹)",
align: "text-end",
customRender: (r) => (
<span className="fw-semibold fs-6">
{formatFigure(r.balanceAmount, {
// type: "currency",
currency: "INR",
})}
</span>
),
},
];
if (isLoading) return <p className="text-center py-4">Loading...</p>;
if (isError) return <p className="text-center py-4 text-danger">{error.message}</p>;
return (
<div className="card-datatable" id="payment-request-table">
<div className="mx-2">
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
{columns.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{rows.length > 0 ? (
rows.map((row) => (
<tr key={row.id} className="align-middle" style={{ height: "40px" }}>
{columns.map((col) => (
<td key={col.key} className={`d-table-cell ${col.align} py-3`}>
{col.customRender
? col.customRender(row)
: col.getValue(row)}
</td>
))}
</tr>
))
) : (
<tr>
<td colSpan={columns.length} className="text-center border-0 py-3">
No Employees Found
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
export default AdvancePaymentList1;

View File

@ -50,8 +50,11 @@ const Header = () => {
const isRecurringExpense = /^\/recurring-payment$/.test(pathname); const isRecurringExpense = /^\/recurring-payment$/.test(pathname);
const isAdvancePayment = /^\/advance-payment$/.test(pathname); const isAdvancePayment = /^\/advance-payment$/.test(pathname);
const isServiceProjectPage = /^\/service-projects\/[0-9a-fA-F-]{36}$/.test(pathname); const isServiceProjectPage = /^\/service-projects\/[0-9a-fA-F-]{36}$/.test(pathname);
const isAdvancePayment1 =
/^\/advance-payment(\/[0-9a-fA-F-]{36})?$/.test(pathname);
return !(isDirectoryPath || isProfilePage || isExpensePage || isPaymentRequest || isRecurringExpense || isAdvancePayment ||isServiceProjectPage);
return !(isDirectoryPath || isProfilePage || isExpensePage || isPaymentRequest || isRecurringExpense || isAdvancePayment ||isServiceProjectPage || isAdvancePayment1);
}; };
const allowedProjectStatusIds = [ const allowedProjectStatusIds = [
"603e994b-a27f-4e5d-a251-f3d69b0498ba", "603e994b-a27f-4e5d-a251-f3d69b0498ba",

View File

@ -166,7 +166,7 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
} }
); );
}; };
console.log("Tanish",filteredData)
return ( return (
<> <>
{IsDeleteModalOpen && ( {IsDeleteModalOpen && (

View File

@ -40,7 +40,6 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
); );
const { setProcessedPayment, setAddPayment, setViewCollection } = const { setProcessedPayment, setAddPayment, setViewCollection } =
useCollectionContext(); useCollectionContext();
const paginate = (page) => { const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) { if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page); setCurrentPage(page);
@ -113,6 +112,16 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
), ),
align: "text-center", align: "text-center",
}, },
{
key: "status",
label: "Status",
getValue: (col) => (
<span className={`badge bg-label-${col?.isActive ? "primary" : "danger"}`}>
{col?.isActive ? "Active" : "Inactive"}
</span>
),
align: "text-center",
},
{ {
key: "amount", key: "amount",
label: "Total Amount", label: "Total Amount",
@ -129,6 +138,7 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
), ),
align: "text-end", align: "text-end",
}, },
{ {
key: "balance", key: "balance",
label: "Balance", label: "Balance",

View File

@ -25,7 +25,6 @@ const ViewCollection = ({ onClose }) => {
if (isLoading) return <CollectionDetailsSkeleton />; if (isLoading) return <CollectionDetailsSkeleton />;
if (isError) return <div>{error.message}</div>; if (isError) return <div>{error.message}</div>;
return ( return (
<div className="container p-3"> <div className="container p-3">
<p className="fs-5 fw-semibold">Collection Details</p> <p className="fs-5 fw-semibold">Collection Details</p>
@ -43,8 +42,7 @@ const ViewCollection = ({ onClose }) => {
<div> <div>
{" "} {" "}
<span <span
className={`badge bg-label-${ className={`badge bg-label-${data?.isActive ? "primary" : "danger"
data?.isActive ? "primary" : "danger"
}`} }`}
> >
{data?.isActive ? "Active" : "Inactive"} {data?.isActive ? "Active" : "Inactive"}
@ -214,8 +212,7 @@ const ViewCollection = ({ onClose }) => {
<ul className="nav nav-tabs" role="tablist"> <ul className="nav nav-tabs" role="tablist">
<li className="nav-item"> <li className="nav-item">
<button <button
className={`nav-link ${ className={`nav-link ${activeTab === "payments" ? "active" : ""
activeTab === "payments" ? "active" : ""
}`} }`}
onClick={() => setActiveTab("payments")} onClick={() => setActiveTab("payments")}
type="button" type="button"
@ -225,8 +222,7 @@ const ViewCollection = ({ onClose }) => {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<button <button
className={`nav-link ${ className={`nav-link ${activeTab === "details" ? "active" : ""
activeTab === "details" ? "active" : ""
}`} }`}
onClick={() => setActiveTab("details")} onClick={() => setActiveTab("details")}
type="button" type="button"

View File

@ -438,6 +438,15 @@ export const useExpenseTransactions = (employeeId)=>{
keepPreviousData:true, keepPreviousData:true,
}) })
} }
export const useExpenseAllTransactionsList = (searchString) => {
return useQuery({
queryKey: ["transaction", searchString],
queryFn: async () => {
const resp = await ExpenseRepository.getAllTranctionList(searchString);
return resp.data;
},
});
};
//#endregion //#endregion
// ---------------------------Put Post Recurring Expense--------------------------------------- // ---------------------------Put Post Recurring Expense---------------------------------------

View File

@ -13,6 +13,8 @@ import Label from "../../components/common/Label";
import AdvancePaymentList from "../../components/AdvancePayment/AdvancePaymentList"; import AdvancePaymentList from "../../components/AdvancePayment/AdvancePaymentList";
import { employee } from "../../data/masters"; import { employee } from "../../data/masters";
import { formatFigure } from "../../utils/appUtils"; import { formatFigure } from "../../utils/appUtils";
import { useParams } from "react-router-dom";
import { useExpenseTransactions } from "../../hooks/useExpense";
export const AdvancePaymentContext = createContext(); export const AdvancePaymentContext = createContext();
export const useAdvancePaymentContext = () => { export const useAdvancePaymentContext = () => {
@ -25,14 +27,32 @@ export const useAdvancePaymentContext = () => {
return context; return context;
}; };
const AdvancePaymentPage = () => { const AdvancePaymentPage = () => {
const { employeeId } = useParams();
const { data: transactionData } = useExpenseTransactions(employeeId, {
enabled: !!employeeId
});
const employeeName = useMemo(() => {
if (Array.isArray(transactionData) && transactionData.length > 0) {
const emp = transactionData[0].employee;
if (emp) return `${emp.firstName} ${emp.lastName}`;
}
return "";
}, [transactionData]);
const [balance, setBalance] = useState(null); const [balance, setBalance] = useState(null);
const { control, reset, watch } = useForm({ const { control, reset, watch } = useForm({
defaultValues: { defaultValues: {
employeeId: "", employeeId: employeeId || "",
searchString: "",
}, },
}); });
const selectedEmployeeId = watch("employeeId"); const selectedEmployeeId = employeeId || watch("employeeId");
const searchString = watch("searchString");
useEffect(() => { useEffect(() => {
const selectedEmpoyee = sessionStorage.getItem("transaction-empId"); const selectedEmpoyee = sessionStorage.getItem("transaction-empId");
reset({ reset({
@ -47,29 +67,19 @@ const AdvancePaymentPage = () => {
data={[ data={[
{ label: "Home", link: "/dashboard" }, { label: "Home", link: "/dashboard" },
{ label: "Finance", link: "/advance-payment" }, { label: "Finance", link: "/advance-payment" },
{ label: "Advance Payment" }, { label: "Advance Payment", link: "/advance-payment" },
]} employeeName && { label: employeeName, link: "" },
].filter(Boolean)}
/> />
<div className="card px-4 py-2 page-min-h "> <div className="card px-4 py-2 page-min-h ">
<div className="row py-1"> <div className="row py-1 justify-content-end">
<div className="col-12 col-md-4">
<div className="d-block text-start">
<EmployeeSearchInput
control={control}
name="employeeId"
projectId={null}
forAll={true}
placeholder={"Enter Employee Name"}
/>
</div>
</div>
<div className="col-md-8 d-flex align-items-center justify-content-end"> <div className="col-md-8 d-flex align-items-center justify-content-end">
{balance ? ( {balance ? (
<> <>
<label className="fs-5 fw-semibold">Current Balance : </label> <label className="fs-5 fw-semibold">Current Balance : </label>
<span <span
className={`${ className={`${balance > 0 ? "text-success" : "text-danger"
balance > 0 ? "text-success" : "text-danger"
} fs-5 fw-bold ms-1`} } fs-5 fw-bold ms-1`}
> >
{balance > 0 ? ( {balance > 0 ? (
@ -88,7 +98,7 @@ const AdvancePaymentPage = () => {
)} )}
</div> </div>
</div> </div>
<AdvancePaymentList employeeId={selectedEmployeeId} /> <AdvancePaymentList employeeId={selectedEmployeeId} searchString={searchString} />
</div> </div>
</div> </div>
</AdvancePaymentContext.Provider> </AdvancePaymentContext.Provider>

View File

@ -0,0 +1,34 @@
import React from 'react'
import Breadcrumb from '../../components/common/Breadcrumb'
import AdvancePaymentList1 from '../../components/AdvancePayment/AdvancePaymentList1'
import { useForm } from 'react-hook-form';
import EmployeeSearchInput from '../../components/common/EmployeeSearchInput';
const AdvancePaymentPage1 = () => {
const { control, reset, watch } = useForm({
defaultValues: {
searchString: "",
},
});
const searchString = watch("searchString");
return (
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Finance", link: "/advance-payment" },
{ label: "Advance Payment" },
]}
/>
<div className="card px-4 py-2 page-min-h">
<div className="row py-1">
<AdvancePaymentList1 searchString={searchString} />
</div>
</div>
</div>
)
}
export default AdvancePaymentPage1

View File

@ -118,32 +118,33 @@ const CollectionPage = () => {
<div className="card my-3 py-2 px-sm-4 px-2"> <div className="card my-3 py-2 px-sm-4 px-2">
<div className="row align-items-center mx-0"> <div className="row align-items-center mx-0">
{/* Left side: Date Picker + Show Pending (stacked on mobile) */} {/* Left side: Date Picker + Show Pending (stacked on mobile) */}
<div className="col-12 col-md-6 d-flex flex-column flex-md-row flex-wrap align-items-start align-md-items-center gap-2 gap-md-3 mb-3 mb-md-0"> <div className="col-12 col-md-6 d-flex flex-column flex-md-row flex-wrap align-items-start">
<FormProvider {...methods}> <div className="d-inline-flex border rounded-pill overflow-hidden shadow-none">
<DateRangePicker1 howManyDay={180} startField="fromDate" <button
endField="toDate" /> type="button"
</FormProvider> className={`btn px-2 py-1 rounded-0 text-tiny ${!showPending ? "btn-primary text-white" : ""
}`}
<div className="form-check form-switch d-flex align-items-center mt-1"> onClick={() => setShowPending(false)}
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
checked={showPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label
className="form-check-label ms-2"
htmlFor="inactiveEmployeesCheckbox"
> >
Show Completed Collections Show All
</label> </button>
<button
type="button"
className={`btn px-2 py-1 rounded-0 text-tiny ${showPending ? "btn-primary text-white" : ""
}`}
onClick={() => setShowPending(true)}
>
Pending
</button>
</div> </div>
</div> </div>
{/* Right side: Search + Add Button */} {/* Right side: Search + Add Button */}
<div className="col-12 col-sm-6 d-flex justify-content-end align-items-center gap-2"> <div className="col-12 col-sm-6 d-flex justify-content-end align-items-center gap-2">
<FormProvider {...methods}>
<DateRangePicker1 howManyDay={180} startField="fromDate"
endField="toDate" />
</FormProvider>
<input <input
type="search" type="search"
value={searchText} value={searchText}

View File

@ -70,6 +70,8 @@ const ExpenseRepository = {
//#region Advance Payment //#region Advance Payment
GetTranctionList: (employeeId) => GetTranctionList: (employeeId) =>
api.get(`/api/Expense/get/transactions/${employeeId}`), api.get(`/api/Expense/get/transactions/${employeeId}`),
getAllTranctionList: (searchString) =>
api.get(`/api/Expense/get/advance-payment/employee/list?searchString=${searchString}`),
//#endregion //#endregion

View File

@ -61,6 +61,7 @@ import RecurringExpensePage from "../pages/RecurringExpense/RecurringExpensePage
import AdvancePaymentPage from "../pages/AdvancePayment/AdvancePaymentPage"; import AdvancePaymentPage from "../pages/AdvancePayment/AdvancePaymentPage";
import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail"; import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail";
import ManageJob from "../components/ServiceProject/ServiceProjectJob/ManageJob"; import ManageJob from "../components/ServiceProject/ServiceProjectJob/ManageJob";
import AdvancePaymentPage1 from "../pages/AdvancePayment/AdvancePaymentPage1";
const router = createBrowserRouter( const router = createBrowserRouter(
[ [
{ {
@ -116,7 +117,8 @@ const router = createBrowserRouter(
{ path: "/expenses", element: <ExpensePage /> }, { path: "/expenses", element: <ExpensePage /> },
{ path: "/payment-request", element: <PaymentRequestPage /> }, { path: "/payment-request", element: <PaymentRequestPage /> },
{ path: "/recurring-payment", element: <RecurringExpensePage /> }, { path: "/recurring-payment", element: <RecurringExpensePage /> },
{ path: "/advance-payment", element: <AdvancePaymentPage /> }, { path: "/advance-payment", element: <AdvancePaymentPage1 /> },
{ path: "/advance-payment/:employeeId", element: <AdvancePaymentPage /> },
{ path: "/collection", element: <CollectionPage /> }, { path: "/collection", element: <CollectionPage /> },
{ path: "/masters", element: <MasterPage /> }, { path: "/masters", element: <MasterPage /> },
{ path: "/tenants", element: <TenantPage /> }, { path: "/tenants", element: <TenantPage /> },