Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment

This commit is contained in:
pramod.mahajan 2025-11-17 20:26:33 +05:30
commit 8de1230f79
28 changed files with 602 additions and 414 deletions

View File

@ -100,9 +100,9 @@ const AttendanceOverview = () => {
};
return (
<div className="bg-white p-4 rounded shadow d-flex flex-column h-100">
<div className="bg-white px-4 rounded shadow d-flex flex-column h-100">
{/* Header */}
<div className="d-flex justify-content-between align-items-center mb-3">
<div className="d-flex mt-2 justify-content-between align-items-center mb-3">
<div className="card-title mb-0 text-start">
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
<p className="card-subtitle">Role-wise present count</p>

View File

@ -12,11 +12,11 @@ import Teams from "./Teams";
import TasksCard from "./Tasks";
import ProjectCompletionChart from "./ProjectCompletionChart";
import ProjectProgressChart from "./ProjectProgressChart";
import ProjectOverview from "../Project/ProjectOverview";
import AttendanceOverview from "./AttendanceChart";
import AttendanceOverview from "./AttendanceOverview";
import ExpenseAnalysis from "./ExpenseAnalysis";
import ExpenseStatus from "./ExpenseStatus";
import ExpenseByProject from "./ExpenseByProject";
import ProjectStatistics from "../Project/ProjectStatistics";
const Dashboard = () => {
@ -29,16 +29,16 @@ const Dashboard = () => {
<div className="row gy-4">
{isAllProjectsSelected && (
<div className="col-sm-6 col-lg-4">
<Projects />
<Projects />
</div>
)}
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<Teams />
<Teams />
</div>
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<TasksCard/>
<TasksCard />
</div>
{isAllProjectsSelected && (
@ -46,32 +46,31 @@ const Dashboard = () => {
<ProjectCompletionChart />
</div>
)}
{!isAllProjectsSelected && (
<div className="col-xxl-6 col-lg-6">
<ProjectOverview />
</div>
)}
<div className="col-xxl-6 col-lg-6">
<ProjectProgressChart />
</div>
<div className="col-12 col-xl-8">
<div className="card h-100">
<ExpenseAnalysis />
</div>
</div>
<div className="col-12 col-xl-4 col-md-6">
<div className="card h-100">
<ExpenseStatus />
</div>
</div>
{!isAllProjectsSelected && (
<div className="col-12 col-md-6 mb-sm-0 mb-4">
<AttendanceOverview />
</div>
)}
{!isAllProjectsSelected && (
<div className="col-xxl-4 col-lg-4">
<ProjectStatistics />
</div>
)}
<div className="col-12 col-xl-4 col-md-6">
<div className="card ">
<ExpenseStatus />
</div>
</div>
<div className="col-12 col-xl-8">
<div className="card h-100">
<ExpenseAnalysis />
</div>
</div>
<div className="col-12 col-md-6">
<ExpenseByProject />
</div>

View File

@ -7,11 +7,12 @@ import { FormProvider, useForm } from "react-hook-form";
import { formatCurrency, localToUtc } from "../../utils/appUtils";
import { useProjectName } from "../../hooks/useProjects";
import { SpinnerLoader } from "../common/Loader";
import flatColors from "../Charts/flatColor";
const ExpenseAnalysis = () => {
const projectId = useSelectedProject();
const [projectName, setProjectName] = useState("All Project");
const { projectNames, loading } = useProjectName();
const { projectNames } = useProjectName();
const methods = useForm({
defaultValues: { startDate: "", endDate: "" },
@ -50,7 +51,7 @@ const ExpenseAnalysis = () => {
labels,
legend: { show: false },
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
colors: flatColors,
plotOptions: {
pie: {
donut: {
@ -79,22 +80,19 @@ const ExpenseAnalysis = () => {
return (
<>
<div className="card-header d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-2">
<div className="text-start ">
<div className="text-start">
<h5 className="mb-1 card-title">Expense Breakdown</h5>
{/* <p className="card-subtitle mb-0">Category Wise Expense Breakdown</p> */}
<p className="card-subtitle m-0">{projectName}</p>
</div>
<div className="text-end text-sm-end ">
<div className="text-end text-sm-end">
<FormProvider {...methods}>
<DateRangePicker1 />
</FormProvider>
</div>
</div>
{/* Card body */}
<div className="card-body position-relative">
{isLoading && (
<div
@ -114,7 +112,6 @@ const ExpenseAnalysis = () => {
</div>
)}
{!isLoading && report.length > 0 && (
<>
{isFetching && (
@ -123,50 +120,59 @@ const ExpenseAnalysis = () => {
</div>
)}
<div className="d-flex justify-content-center mb-3">
<Chart
options={donutOptions}
series={series}
type="donut"
width="100%"
height={320}
/>
</div>
<div className="row">
{/* Chart Column */}
<div className="col-12 col-lg-6 d-flex justify-content-center mt-5 mb-3 mb-lg-0">
<Chart
options={donutOptions}
series={series}
type="donut"
width="70%"
height={320}
/>
</div>
<div className="mb-2 w-100">
<div className="row g-2">
{report.map((item, idx) => (
<div
className="col-12 col-sm-6 d-flex align-items-start"
key={idx}
>
<div className="avatar me-2">
<span
className="avatar-initial rounded-2"
style={{
backgroundColor:
donutOptions.colors[idx % donutOptions.colors.length],
}}
>
<i className="bx bx-receipt fs-4"></i>
</span>
{/* Data/Legend Column */}
<div className="col-12 mt-6 col-lg-6">
<div className="row g-4">
{report.map((item, idx) => (
<div
className="col-6"
key={idx}
style={{
borderLeft: `3px solid ${flatColors[idx % flatColors.length]}`,
}}
>
<div className="d-flex flex-column text-start">
<small
className="fw-semibold text-wrap text-dark"
style={{
fontSize: "0.8rem",
whiteSpace: "normal",
wordBreak: "break-word",
lineHeight: "1.2",
}}
>
{item.projectName}
</small>
<span
className="fw-semibold text-muted"
style={{
fontSize: "0.75rem",
}}
>
{formatCurrency(item.totalApprovedAmount)}
</span>
</div>
</div>
<div className="d-flex flex-column gap-1 text-start">
<small className="fw-semibold">{item.projectName}</small>
<span className="fw-semibold text-muted ms-1">
{formatCurrency(item.totalApprovedAmount)}
</span>
</div>
</div>
))}
))}
</div>
</div>
</div>
</>
)}
</div>
{/* Header */}
</>
);
};

View File

@ -92,7 +92,7 @@ const ExpenseByProject = () => {
<div className="card shadow-sm h-100 rounded ">
{/* Header */}
<div className="card-header">
<div className="d-flex justify-content-start align-items-center mb-1 mt-1">
<div className="d-flex justify-content-between align-items-center mb-1 mt-1">
<div className="text-start">
<h5 className="mb-1 me-6 card-title">Monthly Expense -</h5>
<p className="card-subtitle m-0">{projectName}</p>

View File

@ -103,7 +103,7 @@ const ExpenseStatus = () => {
</div>
<div>
<small
className={`text-royalblue ${countDigit(item?.count || 0) >= 3 ? "text-xl" : "text-2xl"
className={`text-royalblue ${countDigit(item?.count || 0) >= 3 ? "text-xl" : "text-xl"
} text-gray-500`}
>
{item?.count || 0}
@ -122,7 +122,7 @@ const ExpenseStatus = () => {
{isManageExpense && (
<div
className="d-flex justify-content-between align-items-center cursor-pointer"
onClick={() => handleNavigate(EXPENSE_STATUS.process_pending)}
onClick={() => handleNavigate(EXPENSE_STATUS.payment_processed)}
>
<div className="d-block">
<span
@ -137,7 +137,7 @@ const ExpenseStatus = () => {
</div>
<div className="d-flex align-items-center gap-2">
<span
className={`text-end text-royalblue ${countDigit(data?.totalAmount || 0) > 3 ? "text-" : "text-3xl"
className={`text-end text-royalblue ${countDigit(data?.totalAmount || 0) > 3 ? "text-xl" : "text-3xl"
} text-md`}
>
{formatCurrency(data?.totalAmount || 0)}

View File

@ -1,11 +1,14 @@
import React from "react";
import React, { useState } from "react";
import HorizontalBarChart from "../Charts/HorizontalBarChart";
import { useProjects } from "../../hooks/useProjects";
import { ITEMS_PER_PAGE } from "../../utils/constants";
const ProjectCompletionChart = () => {
const { data: projects = [], isLoading: loading, isError, error } = useProjects();
const [currentPage, setCurrentPage] = useState(1);
const { data: projects, isLoading: loading, isError, error } = useProjects(currentPage, ITEMS_PER_PAGE);
console.log("Kartik", projects)
// Bar chart logic
const projectNames = projects?.map((p) => p.name) || [];
const projectProgress =

View File

@ -99,7 +99,7 @@ const MenuItem = (item) => {
className={`menu-link ${hasSubmenu ? "menu-toggle" : ""}`}
target={item.link?.includes("http") ? "_blank" : undefined}
>
<i className={`menu-icon tf-icons ${item.icon}`}></i>
{item.icon && <i className={`menu-icon tf-icons ${item.icon}`}></i>}
<div>{item.name}</div>
{item.available === false && (
<div className="badge bg-label-primary fs-tiny rounded-pill ms-auto">

View File

@ -40,6 +40,8 @@ const ActionPaymentRequest = ({ requestId }) => {
error: PaymentModeError,
} = usePaymentMode();
console.log("Kartik", data)
const IsReview = useHasUserPermission(REVIEW_EXPENSE);
const [imageLoaded, setImageLoaded] = useState({});
@ -175,6 +177,16 @@ const ActionPaymentRequest = ({ requestId }) => {
const newFiles = files.filter((_, i) => i !== index);
setValue("billAttachments", newFiles, { shouldValidate: true });
};
const filteredPaymentModes = useMemo(() => {
return PaymentModes?.filter((mode) => {
if (mode.name === "Advance Payment" && data?.isAdvancePayment === false) {
return false;
}
return true;
}) || [];
}, [PaymentModes, data]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
@ -219,7 +231,7 @@ const ActionPaymentRequest = ({ requestId }) => {
{PaymentModeLoading ? (
<option disabled>Loading...</option>
) : (
PaymentModes?.map((payment) => (
filteredPaymentModes?.map((payment) => (
<option key={payment.id} value={payment.id}>
{payment.name}
</option>

View File

@ -0,0 +1,60 @@
import React, { useMemo } from "react";
const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clearFilter }) => {
const data = filterData?.data || filterData || {};
const filterChips = useMemo(() => {
const chips = [];
const addGroup = (ids, list, label, key) => {
if (!ids?.length) return;
const items = ids.map((id) => ({
id,
name: list?.find((i) => i.id === id)?.name || id,
}));
chips.push({ key, label, items });
};
addGroup(filters.createdByIds, data.createdBy, "Created By", "createdByIds");
addGroup(filters.currencyIds, data.currency, "Currency", "currencyIds");
addGroup(filters.expenseCategoryIds, data.expenseCategory, "Expense Category", "expenseCategoryIds");
addGroup(filters.payees, data.payees, "Payees", "payees");
addGroup(filters.projectIds, data.projects, "Projects", "projectIds");
addGroup(filters.statusIds, data.status, "Status", "statusIds");
return chips;
}, [filters, filterData]);
if (!filterChips.length) return null;
return (
<div className="d-flex flex-wrap align-items-center gap-2">
{filterChips.map((chipGroup) => (
<div key={chipGroup.key} className="d-flex align-items-center flex-wrap">
<span className="fw-semibold me-2">{chipGroup.label}:</span>
{chipGroup.items.map((item) => (
<span
key={item.id}
className="d-flex align-items-center bg-light rounded px-2 py-1 me-1"
>
<span>{item.name}</span>
<button
type="button"
className="btn-close btn-close-white btn-sm ms-2"
style={{
filter: "invert(1) grayscale(1)",
opacity: 0.7,
fontSize: "0.6rem",
}}
onClick={() => removeFilterChip(chipGroup.key, item.id)}
/>
</span>
))}
</div>
))}
</div>
);
};
export default PaymentRequestFilterChips;

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState, useMemo } from "react";
import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle } from "react";
import { FormProvider, useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "./PaymentRequestSchema";
import { defaultPaymentRequestFilter, SearchPaymentRequestSchema } from "./PaymentRequestSchema";
import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker";
import SelectMultiple from "../common/SelectMultiple";
@ -13,7 +13,7 @@ import moment from "moment";
import { usePaymentRequestFilter } from "../../hooks/useExpense";
import { useLocation, useNavigate, useParams } from "react-router-dom";
const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata, clearFilter }, ref) => {
const { status } = useParams();
const navigate = useNavigate();
const selectedProjectId = useSelector(
@ -38,10 +38,23 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
const [resetKey, setResetKey] = useState(0);
const dynamicDefaultFilter = useMemo(() => {
return {
...defaultPaymentRequestFilter,
projectIds: defaultPaymentRequestFilter.projectIds || [],
statusIds: status ? [status] : defaultPaymentRequestFilter.statusIds || [],
createdByIds: defaultPaymentRequestFilter.createdByIds || [],
currencyIds: defaultPaymentRequestFilter.currencyIds || [],
expenseCategoryIds: defaultPaymentRequestFilter.expenseCategoryIds || [],
payees: defaultPaymentRequestFilter.payees || [],
startDate: defaultPaymentRequestFilter.startDate,
endDate: defaultPaymentRequestFilter.endDate,
};
}, [status, selectedProjectId]);
const methods = useForm({
resolver: zodResolver(SearchPaymentRequestSchema),
defaultValues: defaultPaymentRequestFilter,
defaultValues: dynamicDefaultFilter,
});
const { control, handleSubmit, reset, setValue, watch } = methods;
@ -52,12 +65,28 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
};
const handleGroupChange = (e) => {
const group = groupByList.find((g) => g.id === e.target.value);
if (group) setSelectedGroup(group);
};
useImperativeHandle(ref, () => ({
resetFieldValue: (name, value) => {
// Reset specific field
if (value !== undefined) {
setValue(name, value);
} else {
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
}
},
getValues: methods.getValues, // optional, to read current filter state
}));
useEffect(() => {
if (data && setFilterdata) {
setFilterdata(data);
}
}, [data, setFilterdata]);
const onSubmit = (formData) => {
onApply({
@ -179,7 +208,7 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
))}
</div>
</div>
</div>
</div>
<div className="d-flex justify-content-end py-3 gap-2">
<button
@ -197,6 +226,6 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
</FormProvider>
</>
);
};
});
export default PaymentRequestFilterPanel;

View File

@ -20,8 +20,9 @@ import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import Error from "../common/Error";
import Pagination from "../common/Pagination";
import PaymentRequestFilterChips from "./PaymentRequestFilterChips";
const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
const PaymentRequestList = ({ filters, filterData, removeFilterChip, clearFilter, search, groupBy = "submittedBy" }) => {
const { setManageRequest, setVieRequest } = usePaymentRequestContext();
const navigate = useNavigate();
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
@ -92,33 +93,6 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
align: "text-start",
getValue: (e) => e.title || "N/A",
},
// {
// key: "SubmittedBy",
// label: "Submitted By",
// align: "text-start",
// getValue: (e) =>
// `${e.createdBy?.firstName ?? ""} ${
// e.createdBy?.lastName ?? ""
// }`.trim() || "N/A",
// customRender: (e) => (
// <div
// className="d-flex align-items-center cursor-pointer"
// onClick={() => navigate(`/employee/${e.createdBy?.id}`)}
// >
// <Avatar
// size="xs"
// classAvatar="m-0"
// firstName={e.createdBy?.firstName}
// lastName={e.createdBy?.lastName}
// />
// <span className="text-truncate">
// {`${e.createdBy?.firstName ?? ""} ${
// e.createdBy?.lastName ?? ""
// }`.trim() || "N/A"}
// </span>
// </div>
// ),
// },
{
key: "createdAt",
label: "Created At",
@ -250,6 +224,14 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
)}
<div className="card page-min-h table-responsive px-sm-4">
<div className="card-datatable mx-2" id="payment-request-table ">
<div className="col-12 mb-2 mt-2">
<PaymentRequestFilterChips
filters={filters}
filterData={filterData}
removeFilterChip={removeFilterChip}
clearFilter={clearFilter}
/>
</div>
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>

View File

@ -9,7 +9,7 @@ import {
import ReactApexChart from "react-apexcharts";
import Chart from "react-apexcharts";
const ProjectOverview = ({ project }) => {
const ProjectStatistics = ({ project }) => {
const { data } = useProjects();
const [current_project, setCurrentProject] = useState(
data?.find((pro) => pro.id == project)
@ -133,10 +133,10 @@ const ProjectOverview = ({ project }) => {
},
},
stroke: {
lineCap: "round",
lineCap: "round",
},
labels: ["Progress"],
series: [percentage],
labels: ["Progress"],
series: [percentage],
};
};
const [radialPercentage, setRadialPercentage] = useState(75); // Initial percentage
@ -165,7 +165,7 @@ const ProjectOverview = ({ project }) => {
}, [selectedProject]);
return (
<div className="card" style={{ minHeight: "490px" }}>
<div className="card h-100">
<div className="card-header text-start">
<h5 className="card-action-title mb-0">
{" "}
@ -173,78 +173,78 @@ const ProjectOverview = ({ project }) => {
<span className="ms-2 fw-bold">Project Statistics</span>
</h5>
</div>
<div className="card-body">
<ul className="list-unstyled m-0 p-0">
<li className="d-flex flex-wrap">
<div className="w-100 d-flex flex-wrap">
{/* Centered Chart */}
<div className="w-100 d-flex justify-content-center mb-3">
<div >
<Chart
options={radialBarOptions}
series={radialBarOptions.series}
type="radialBar"
height="100%"
/>
</div>
</div>
{/* Info Section */}
<div className="mb-2" style={{ flex: "1 1 auto" }}>
<div>
{/* Tasks Planned */}
<div className="d-flex align-items-center mb-3">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-primary">
<i className="bx bx-check text-primary fs-4"></i>
</span>
<div className="card-body">
<ul className="list-unstyled m-0 p-0">
<li className="d-flex flex-wrap">
<div className="w-100 d-flex flex-wrap">
{/* Centered Chart */}
<div className="w-100 d-flex justify-content-center mb-3">
<div >
<Chart
options={radialBarOptions}
series={radialBarOptions.series}
type="radialBar"
height="100%"
/>
</div>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Tasks Planned</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.plannedWork)}
</h5>
{/* Info Section */}
<div className="mb-2" style={{ flex: "1 1 auto" }}>
<div>
{/* Tasks Planned */}
<div className="d-flex align-items-center mb-3">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-primary">
<i className="bx bx-check text-primary fs-4"></i>
</span>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Tasks Planned</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.plannedWork)}
</h5>
</div>
</div>
{/* Tasks Completed */}
<div className="d-flex align-items-center mb-3">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-info">
<i className="bx bx-star text-info fs-4"></i>
</span>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Tasks Completed</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.completedWork)}
</h5>
</div>
</div>
{/* Team Size */}
<div className="d-flex align-items-center">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-primary">
<i className="bx bx-group text-primary fs-4"></i>
</span>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Current Team Size</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.teamSize)}
</h5>
</div>
</div>
</div>
</div>
</div>
{/* Tasks Completed */}
<div className="d-flex align-items-center mb-3">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-info">
<i className="bx bx-star text-info fs-4"></i>
</span>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Tasks Completed</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.completedWork)}
</h5>
</div>
</div>
{/* Team Size */}
<div className="d-flex align-items-center">
<div className="avatar me-2">
<span className="avatar-initial rounded-2 bg-label-primary">
<i className="bx bx-group text-primary fs-4"></i>
</span>
</div>
<div className="d-flex flex-column text-start">
<small className="fw-bold">Current Team Size</small>
<h5 className="mb-0">
{FormattedNumber(current_project?.teamSize)}
</h5>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
);
};
export default ProjectOverview;
export default ProjectStatistics;

View File

@ -105,7 +105,7 @@ const JobList = () => {
{jobGrid.map((col) => (
<th
key={col.key}
className={`${col.className} text-center`}
className={`${col.align || "text-center"} ${col.className || ""}`}
scope="col"
>
<div className={col.className}>{col.label}</div>
@ -120,14 +120,14 @@ const JobList = () => {
<tbody>
{Array.isArray(data?.data) && data.data.length > 0 ? (
data.data.map((row, i) => (
<tr key={i}>
<tr key={i} className="text-start">
{jobGrid.map((col) => (
<td key={col.key} className={col.className}>
{col.getValue(row)}
</td>
))}
<td>
<div className="dropdown">
<div className="dropdown text-center">
<button
className="btn btn-icon dropdown-toggle hide-arrow"
data-bs-toggle="dropdown"

View File

@ -57,7 +57,7 @@ const Jobs = () => {
>
<ManageJob Job={manageJob.jobId} />
</OffcanvasComponent>
<div className="card page-min-h my-2 px-4">
<div className="card page-min-h my-2 px-7">
<div className="row">
<div className="col-12 py-2 d-flex justify-content-end ">
<div className="px-2">

View File

@ -117,31 +117,37 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
<Label htmlFor="name" required>
Client
</Label>
<select
className="select2 form-select form-select-sm"
aria-label="Default select example"
{...register("clientId", {
required: "Client is required",
valueAsNumber: false,
})}
>
{isLoading ? (
<option>Loading...</option>
) : (
<>
<option value="">Select Client</option>
{organization?.data?.map((org) => (
<option key={org.id} value={org.id}>
{org.name}
</option>
))}
</>
)}
</select>
<div className="d-flex align-items-center gap-2">
<select
className="select2 form-select form-select-sm flex-grow-1"
aria-label="Default select example"
{...register("clientId", {
required: "Client is required",
valueAsNumber: false,
})}
>
{isLoading ? (
<option>Loading...</option>
) : (
<>
<option value="">Select Client</option>
{organization?.data?.map((org) => (
<option key={org.id} value={org.id}>
{org.name}
</option>
))}
</>
)}
</select>
<i
className="bx bx-plus-circle bx-xs cursor-pointer text-primary"
/>
</div>
{errors?.clientId && (
<span className="danger-text">{errors.clientId.message}</span>
)}
</div>
<div className="col-12 mb-2">
<Label htmlFor="name" required>
Project Name

View File

@ -72,7 +72,7 @@ const AddPayment = ({ onClose }) => {
<div className="col-12 col-md-6 mb-2">
<Label required>Transaction Date </Label>
<DatePicker
<DatePicker className="w-100"
name="paymentReceivedDate"
control={control}
minDate={

View File

@ -27,16 +27,16 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
const selectedProject = useSelectedProject();
const searchDebounce = useDebounce(searchString, 500);
const { data, isLoading, isError, error } = useCollections(
ITEMS_PER_PAGE,
currentPage,
selectedProject,
searchDebounce,
localToUtc(fromDate),
localToUtc(toDate),
isPending,
ITEMS_PER_PAGE,
currentPage,
true,
selectedProject,
searchDebounce
isPending
);
const { setProcessedPayment, setAddPayment, setViewCollection } =
useCollectionContext();
@ -186,84 +186,84 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
canViewCollection ||
canEditCollection ||
canCreate) && (
<td
className="sticky-action-column text-center"
style={{ padding: "12px 8px" }}
>
<div className="dropdown z-2">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<td
className="sticky-action-column text-center"
style={{ padding: "12px 8px" }}
>
<div className="dropdown z-2">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
{/* View */}
<ul className="dropdown-menu dropdown-menu-end">
{/* View */}
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => setViewCollection(row.id)}
>
<i className="bx bx-show me-2 text-primary"></i>
<span>View</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => setViewCollection(row.id)}
>
<i className="bx bx-show me-2 text-primary"></i>
<span>View</span>
</a>
</li>
{/* Only if not completed */}
{!row?.markAsCompleted && (
<>
{/* Add Payment */}
{(isAdmin || canAddPayment) && (
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setAddPayment({
isOpen: true,
invoiceId: row.id,
})
}
>
<i className="bx bx-wallet me-2 text-warning"></i>
<span>Add Payment</span>
</a>
</li>
)}
{/* Only if not completed */}
{!row?.markAsCompleted && (
<>
{/* Add Payment */}
{(isAdmin || canAddPayment) && (
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setAddPayment({
isOpen: true,
invoiceId: row.id,
})
}
>
<i className="bx bx-wallet me-2 text-warning"></i>
<span>Add Payment</span>
</a>
</li>
)}
{/* Mark Payment */}
{isAdmin && (
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setProcessedPayment({
isOpen: true,
invoiceId: row.id,
})
}
>
<i className="bx bx-check-circle me-2 text-success"></i>
<span>Mark Payment</span>
</a>
</li>
)}
</>
)}
</ul>
</div>
</td>
)}
{/* Mark Payment */}
{isAdmin && (
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setProcessedPayment({
isOpen: true,
invoiceId: row.id,
})
}
>
<i className="bx bx-check-circle me-2 text-success"></i>
<span>Mark Payment</span>
</a>
</li>
)}
</>
)}
</ul>
</div>
</td>
)}
</tr>
))
) : (

View File

@ -2,39 +2,80 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { CollectionRepository } from "../repositories/ColllectionRepository";
import showToast from "../services/toastService";
// export const useCollections = (
// pageSize,
// pageNumber,
// fromDate,
// toDate,
// isPending,
// isActive,
// projectId,
// searchString
// ) => {
// return useQuery({
// queryKey: [
// "collections",
// pageSize,
// pageNumber,
// fromDate,
// toDate,
// isPending,
// isActive,
// projectId,
// searchString,
// ],
// queryFn: async () => {
// const response = await CollectionRepository.getCollections(
// pageSize,
// pageNumber,
// fromDate,
// toDate,
// isPending,
// isActive,
// projectId,
// searchString
// );
// return response.data;
// },
// keepPreviousData: true,
// staleTime: 1000 * 60 * 1,
// });
// };
export const useCollections = (
pageSize,
pageNumber,
projectId,
searchString,
fromDate,
toDate,
isPending,
pageSize,
pageNumber,
isActive,
projectId,
searchString
isPending
) => {
return useQuery({
queryKey: [
"collections",
pageSize,
pageNumber,
fromDate,
toDate,
isPending,
isActive,
projectId,
searchString,
fromDate,
toDate,
pageSize,
pageNumber,
isActive,
isPending,
],
queryFn: async () => {
const response = await CollectionRepository.getCollections(
pageSize,
pageNumber,
projectId,
searchString,
fromDate,
toDate,
isPending,
pageSize,
pageNumber,
isActive,
projectId,
searchString
isPending
);
return response.data;
},
@ -44,15 +85,14 @@ export const useCollections = (
});
};
export const useCollection =(collectionId)=>{
export const useCollection = (collectionId) => {
return useQuery({
queryKey:["collection",collectionId],
queryFn:async()=> {
queryKey: ["collection", collectionId],
queryFn: async () => {
const resp = await CollectionRepository.getCollection(collectionId);
return resp.data
},
enabled:!!collectionId
enabled: !!collectionId
})
}
@ -133,11 +173,11 @@ export const useAddComment = (onSuccessCallBack) => {
});
};
export const useUpdateCollection =(onSuccessCallBack)=>{
const client = useQueryClient();
export const useUpdateCollection = (onSuccessCallBack) => {
const client = useQueryClient();
return useMutation({
mutationFn:async({collectionId,payload})=> await CollectionRepository.updateCollection(collectionId,payload),
onSuccess: () => {
mutationFn: async ({ collectionId, payload }) => await CollectionRepository.updateCollection(collectionId, payload),
onSuccess: () => {
client.invalidateQueries({ queryKey: ["collections"] });
client.invalidateQueries({ queryKey: ["collection"] });
showToast("Collection Updated Successfully", "success");

View File

@ -20,13 +20,13 @@ export const useCurrentService = () => {
// ------------------------------Query-------------------
export const useProjects = (pageSize,pageNumber) => {
export const useProjects = (pageNumber,pageSize) => {
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
return useQuery({
queryKey: ["ProjectsList",pageSize,pageNumber],
queryKey: ["ProjectsList",pageNumber,pageSize],
queryFn: async () => {
const response = await ProjectRepository.getProjectList(pageSize,pageNumber);
return response.data;
const response = await ProjectRepository.getProjectList(pageNumber,pageSize);
return response?.data?.data;
},
enabled: !!loggedUser,
});

View File

@ -26,7 +26,7 @@ export const useAdvancePaymentContext = () => {
};
const AdvancePaymentPage = () => {
const [balance, setBalance] = useState(null);
const {control, reset, watch } = useForm({
const { control, reset, watch } = useForm({
defaultValues: {
employeeId: "",
},
@ -39,14 +39,13 @@ const AdvancePaymentPage = () => {
employeeId: selectedEmpoyee || "",
});
}, [reset]);
return (
<AdvancePaymentContext.Provider value={{ setBalance }}>
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Home", link: "/dashboard" },
{ label: "Finance", link: "/advance-payment" },
{ label: "Advance Payment" },
]}
@ -73,7 +72,12 @@ const AdvancePaymentPage = () => {
balance > 0 ? "text-success" : "text-danger"
} fs-5 fw-bold ms-1`}
>
{ balance > 0 ? <i className="bx bx-plus b-sm"></i> : <i className="bx bx-minus b-sm"></i>} {formatFigure(balance, {
{balance > 0 ? (
<i className="bx bx-plus b-sm"></i>
) : (
<i className="bx bx-minus b-sm"></i>
)}{" "}
{formatFigure(balance, {
type: "currency",
currency: "INR",
})}
@ -85,8 +89,6 @@ const AdvancePaymentPage = () => {
</div>
</div>
<AdvancePaymentList employeeId={selectedEmployeeId} />
</div>
</div>
</AdvancePaymentContext.Provider>

View File

@ -1,4 +1,10 @@
import React, { createContext, useContext, useState, useEffect, useRef } from "react";
import React, {
createContext,
useContext,
useState,
useEffect,
useRef,
} from "react";
import { useForm, useFormContext } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useSelector } from "react-redux";
@ -19,7 +25,10 @@ import {
VIEW_SELF_EXPENSE,
} from "../../utils/constants";
import { defaultFilter, SearchSchema } from "../../components/Expenses/ExpenseSchema";
import {
defaultFilter,
SearchSchema,
} from "../../components/Expenses/ExpenseSchema";
import PreviewDocument from "../../components/Expenses/PreviewDocument";
// Context
@ -102,14 +111,14 @@ const ExpensePage = () => {
setManageExpenseModal,
setDocumentView,
filterData,
removeFilterChip
removeFilterChip,
};
return (
<ExpenseContext.Provider value={contextValue}>
<div className="container-fluid">
<Breadcrumb
data={[{ label: "Home", link: "/" }, { label: "Expense" }]}
data={[{ label: "Home", link: "/dashboard" }, { label: "Expense" }]}
/>
{IsViewAll || IsViewSelf || IsCreatedAble ? (
@ -128,7 +137,6 @@ const ExpensePage = () => {
</div>
<div className="col-6 text-end mt-2 mt-sm-0">
{IsCreatedAble && (
<button
className="btn btn-sm btn-primary"
@ -151,8 +159,6 @@ const ExpensePage = () => {
</div>
</div>
<ExpenseList
filters={filters}
groupBy={groupBy}

View File

@ -36,10 +36,9 @@ export const useGalleryContext = () => {
};
const ImageGalleryPage = () => {
const [filter,setFilter] = useState()
const [filter, setFilter] = useState();
const selectedProjectId = useSelectedProject();
const { projectNames } = useProjectName();
const [openGallery, setOpenGallery] = useState({ isOpen: false, data: null });
@ -56,24 +55,26 @@ const ImageGalleryPage = () => {
setOpenGallery,
};
const { setOffcanvasContent, setShowTrigger } = useFab();
const { setOffcanvasContent, setShowTrigger } = useFab();
useEffect(()=>{
useEffect(() => {
setShowTrigger(true);
setOffcanvasContent("Gallery Filter",<GalleryFilterPanel onApply={setFilter}/>);
return ()=>{
setOffcanvasContent("",null)
setShowTrigger(false);
}
},[])
setOffcanvasContent(
"Gallery Filter",
<GalleryFilterPanel onApply={setFilter} />
);
return () => {
setOffcanvasContent("", null);
setShowTrigger(false);
};
}, []);
return (
<GalleryContext.Provider value={contextMessager}>
<div className="container-fluid">
<Breadcrumb
data={[{ label: "Home", link: "/" }, { label: "Gallery" }]}
data={[{ label: "Home", link: "/dashboard" }, { label: "Gallery" }]}
/>
<div className="card page-min-h p-2">
@ -104,7 +105,7 @@ const ImageGalleryPage = () => {
</div>
)} */}
<ImageGalleryListView filter={filter}/>
<ImageGalleryListView filter={filter} />
</div>
{openGallery?.isOpen && (

View File

@ -6,12 +6,15 @@ import OrganizationsList from "../../components/Organization/OrganizationsList";
const OrganizationPage = () => {
const { isOpen, orgData, startStep, onOpen, flowType } =
useOrganizationModal();
const [searchText, setSearchText] = useState("")
const [searchText, setSearchText] = useState("");
return (
<div className="container-fluid">
<Breadcrumb
data={[{ label: "Home", link: "/" }, { label: "Organizations" }]}
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Organizations" },
]}
/>
<div className="card my-3 px-sm-4 px-0">
<div className="card-body py-2 px-3">
@ -29,27 +32,24 @@ const OrganizationPage = () => {
</div>
</div>
<div className="col-6 text-end mt-2 mt-sm-0">
<div className="col-6 text-end mt-2 mt-sm-0">
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() => onOpen({ startStep: 2, flowType: "default" })}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add New Organization
</span>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add New Organization
</span>
</button>
</div>
</div>
</div>
</div>
<div className="card page-min-h px-sm-4">
<OrganizationsList searchText={searchText} />
<OrganizationsList searchText={searchText} />
</div>
</div>
);
};

View File

@ -1,12 +1,11 @@
import React, { createContext, useState, useEffect, useContext } from "react";
import React, { createContext, useState, useEffect, useContext, useRef } from "react";
import Breadcrumb from "../../components/common/Breadcrumb";
import GlobalModel from "../../components/common/GlobalModel";
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
import ExpenseFilterPanel from "../../components/Expenses/ExpenseFilterPanel";
import { useFab } from "../../Context/FabContext";
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRequestFilterPanel";
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "../../components/PaymentRequest/PaymentRequestSchema";
import { defaultPaymentRequestFilter } from "../../components/PaymentRequest/PaymentRequestSchema";
import ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
import PreviewDocument from "../../components/Expenses/PreviewDocument";
import MakeExpense from "../../components/PaymentRequest/MakeExpense";
@ -15,26 +14,22 @@ export const PaymentRequestContext = createContext();
export const usePaymentRequestContext = () => {
const context = useContext(PaymentRequestContext);
if (!context) {
throw new Error("usePaymentRequestContext must be used within an ExpenseProvider");
throw new Error("usePaymentRequestContext must be used within PaymentRequestContext.Provider");
}
return context;
};
const PaymentRequestPage = () => {
const [ManageRequest, setManageRequest] = useState({
IsOpen: null,
RequestId: null,
});
const [ViewRequest,setVieRequest] = useState({view:false,requestId:null})
const { setOffcanvasContent, setShowTrigger } = useFab();
const [ManageRequest, setManageRequest] = useState({ IsOpen: null, RequestId: null });
const [ViewRequest, setVieRequest] = useState({ view: false, requestId: null });
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
const [ViewDocument, setDocumentView] = useState({
IsOpen: false,
Image: null,
});
const [isExpenseGenerate,setIsExpenseGenerate] = useState({IsOpen: null,
RequestId: null,})
const [modalSize,setModalSize] = useState("md")
const [filterData, setFilterdata] = useState(null);
const [ViewDocument, setDocumentView] = useState({ IsOpen: false, Image: null });
const [isExpenseGenerate, setIsExpenseGenerate] = useState({ IsOpen: null, RequestId: null });
const [modalSize, setModalSize] = useState("md");
const [search, setSearch] = useState("");
const updatedRef = useRef();
const { setOffcanvasContent, setShowTrigger } = useFab();
const contextValue = {
setManageRequest,
@ -42,15 +37,21 @@ const PaymentRequestPage = () => {
setDocumentView,
setModalSize,
setIsExpenseGenerate,
isExpenseGenerate
isExpenseGenerate,
};
useEffect(() => {
const clearFilter = () => setFilters(defaultPaymentRequestFilter);
useEffect(() => {
setShowTrigger(true);
setOffcanvasContent(
"Payment Request Filters",
<PaymentRequestFilterPanel onApply={setFilters} />
<PaymentRequestFilterPanel
onApply={setFilters}
ref={updatedRef}
clearFilter={clearFilter}
setFilterdata={setFilterdata}
/>
);
return () => {
@ -59,13 +60,29 @@ const PaymentRequestPage = () => {
};
}, []);
const handleRemoveChip = (key, id) => {
setFilters((prev) => {
const updated = { ...prev };
if (Array.isArray(updated[key])) {
updated[key] = updated[key].filter((v) => v !== id);
setTimeout(() => updatedRef.current?.resetFieldValue(key, updated[key]), 0);
} else {
updated[key] = null;
setTimeout(() => updatedRef.current?.resetFieldValue(key, null), 0);
}
return updated;
});
};
return (
<PaymentRequestContext.Provider value={contextValue}>
<div className="container-fluid">
{/* Breadcrumb */}
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Home", link: "/dashboard" },
{ label: "Finance", link: "/Payment Request" },
{ label: "Payment Request" },
]}
@ -81,7 +98,7 @@ const PaymentRequestPage = () => {
className="form-control form-control-sm w-auto"
placeholder="Search Payment Request"
value={search}
style={{minWidth:"200px"}}
style={{ minWidth: "200px" }}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
@ -109,6 +126,9 @@ const PaymentRequestPage = () => {
<PaymentRequestList
search={search}
filters={filters}
filterData={filterData}
removeFilterChip={handleRemoveChip}
clearFilter={clearFilter}
/>
{/* Add/Edit Modal */}
@ -116,20 +136,16 @@ const PaymentRequestPage = () => {
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setManageRequest({ IsOpen: null, expenseId: null })
}
closeModal={() => setManageRequest({ IsOpen: null, expenseId: null })}
>
<ManagePaymentRequest
key={ManageRequest.RequestId ?? "new"}
requestToEdit={ManageRequest.RequestId}
closeModal={() =>
setManageRequest({ IsOpen: null, RequestId: null })
}
closeModal={() => setManageRequest({ IsOpen: null, RequestId: null })}
/>
</GlobalModel>
)}
{ViewRequest.view && (
<GlobalModel
isOpen
@ -137,17 +153,18 @@ const PaymentRequestPage = () => {
modalType="top"
closeModal={() => setVieRequest({ requestId: null, view: false })}
>
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
</GlobalModel>
)}
{isExpenseGenerate.IsOpen && (
{isExpenseGenerate.IsOpen && (
<GlobalModel
isOpen
size="md"
closeModal={() => setIsExpenseGenerate({IsOpen:false, requestId: null})}
closeModal={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
>
<MakeExpe
nse onClose={() => setIsExpenseGenerate({IsOpen:false, requestId: null})} />
<MakeExpense
onClose={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
/>
</GlobalModel>
)}
@ -161,7 +178,6 @@ const PaymentRequestPage = () => {
<PreviewDocument imageUrl={ViewDocument.Image} />
</GlobalModel>
)}
</div>
</PaymentRequestContext.Provider>
);

View File

@ -101,7 +101,7 @@ const CollectionPage = () => {
) {
return (
<AccessDenied
data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
data={[{ label: "Home", link: "/dashboard" }, { label: "Collection" }]}
/>
);
}
@ -109,7 +109,10 @@ const CollectionPage = () => {
<CollectionContext.Provider value={contextMassager}>
<div className="container-fluid">
<Breadcrumb
data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Collection" },
]}
/>
<div className="card my-3 py-2 px-sm-4 px-2">
@ -117,7 +120,8 @@ const CollectionPage = () => {
{/* 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">
<FormProvider {...methods}>
<DateRangePicker1 howManyDay={180} />
<DateRangePicker1 howManyDay={180} startField="fromDate"
endField="toDate" />
</FormProvider>
<div className="form-check form-switch d-flex align-items-center mt-1">
@ -152,16 +156,18 @@ const CollectionPage = () => {
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() => setCollection({ isOpen: true, invoiceId: null })}
onClick={() =>
setCollection({ isOpen: true, invoiceId: null })
}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">Add New Collection</span>
<span className="d-none d-md-inline-block">
Add New Collection
</span>
</button>
)}
</div>
</div>
</div>
<CollectionList

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from "react";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import ProjectOverview from "../../components/Project/ProjectOverview";
import ProjectOverview from "../../components/Project/ProjectStatistics";
import AboutProject from "../../components/Project/AboutProject";
import ProjectNav from "../../components/Project/ProjectNav";
import Teams from "../../components/Project/Team/Teams";
@ -15,7 +15,7 @@ import { useProjectDetails, useProjectName } from "../../hooks/useProjects";
import { ComingSoonPage } from "../Misc/ComingSoonPage";
import eventBus from "../../services/eventBus";
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
import AttendanceOverview from "../../components/Dashboard/AttendanceChart";
import AttendanceOverview from "../../components/Dashboard/AttendanceOverview";
import { setProjectId } from "../../slices/localVariablesSlice";
import ProjectDocuments from "../../components/Project/ProjectDocuments";
import ProjectSetting from "../../components/Project/ProjectSetting";

View File

@ -4,26 +4,46 @@ import { DirectoryRepository } from "./DirectoryRepository";
export const CollectionRepository = {
createNewCollection: (data) =>
api.post(`/api/Collection/invoice/create`, data),
updateCollection:(id,data)=>{
api.put(`/api/Collection/invoice/edit/${id}`,data)
updateCollection: (id, data) => {
api.put(`/api/Collection/invoice/edit/${id}`, data)
},
getCollections: (pageSize, pageNumber,fromDate,toDate, isPending,isActive,projectId, searchString) => {
let url = `/api/Collection/invoice/list?pageSize=${pageSize}&pageNumber=${pageNumber}&isPending=${isPending}&isActive=${isActive}&searchString=${searchString}`;
// getCollections: (pageSize, pageNumber,fromDate,toDate, isPending,isActive,projectId, searchString) => {
// let url = `/api/Collection/invoice/list?pageSize=${pageSize}&pageNumber=${pageNumber}&isPending=${isPending}&isActive=${isActive}&searchString=${searchString}`;
// const params = [];
// if (fromDate) params.push(`fromDate=${fromDate}`);
// if (toDate) params.push(`toDate=${toDate}`);
// if(projectId) params.push(`projectId=${projectId}`)
// if (params.length > 0) {
// url += `&${params.join("&")}`;
// }
// return api.get(url);
// },
getCollections: (projectId, searchString, fromDate, toDate, pageSize, pageNumber, isActive, isPending) => {
let url = `/api/Collection/invoice/list`;
const params = [];
if (fromDate) params.push(`fromDate=${fromDate}`);
if (toDate) params.push(`toDate=${toDate}`);
if(projectId) params.push(`projectId=${projectId}`)
if (projectId) params.push(`projectId=${projectId}`);
if (searchString) params.push(`search=${searchString}`);
if (fromDate) params.push(`dateFrom=${fromDate}`);
if (toDate) params.push(`dateTo=${toDate}`);
if (pageSize) params.push(`pageSize=${pageSize}`);
if (pageNumber) params.push(`pageNumber=${pageNumber}`);
if (isActive) params.push(`isActive=${isActive}`);
if (isPending) params.push(`isPending=${isPending}`);
if (params.length > 0) {
url += `&${params.join("&")}`;
url += "?" + params.join("&");
}
return api.get(url);
return api.get(url);
},
makeReceivePayment:(data)=> api.post(`/api/Collection/invoice/payment/received`,data),
markPaymentReceived:(invoiceId)=>api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`),
getCollection:(id)=>api.get(`/api/Collection/invoice/details/${id}`),
addComment:(data)=>api.post(`/api/Collection/invoice/add/comment`,data)
makeReceivePayment: (data) => api.post(`/api/Collection/invoice/payment/received`, data),
markPaymentReceived: (invoiceId) => api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`),
getCollection: (id) => api.get(`/api/Collection/invoice/details/${id}`),
addComment: (data) => api.post(`/api/Collection/invoice/add/comment`, data)
};

View File

@ -1,7 +1,7 @@
import { api } from "../utils/axiosClient";
const ProjectRepository = {
getProjectList: (pageSize,pageNumber) => api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}`),
getProjectList: (pageNumber,pageSize) => api.get(`/api/project/list?&pageNumber=${pageNumber}&pageSize=${pageSize}`),
getProjectByprojectId: (projetid) =>
api.get(`/api/project/details/${projetid}`),