Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment
This commit is contained in:
commit
8de1230f79
@ -100,9 +100,9 @@ const AttendanceOverview = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* 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">
|
<div className="card-title mb-0 text-start">
|
||||||
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
|
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
|
||||||
<p className="card-subtitle">Role-wise present count</p>
|
<p className="card-subtitle">Role-wise present count</p>
|
||||||
@ -12,11 +12,11 @@ import Teams from "./Teams";
|
|||||||
import TasksCard from "./Tasks";
|
import TasksCard from "./Tasks";
|
||||||
import ProjectCompletionChart from "./ProjectCompletionChart";
|
import ProjectCompletionChart from "./ProjectCompletionChart";
|
||||||
import ProjectProgressChart from "./ProjectProgressChart";
|
import ProjectProgressChart from "./ProjectProgressChart";
|
||||||
import ProjectOverview from "../Project/ProjectOverview";
|
import AttendanceOverview from "./AttendanceOverview";
|
||||||
import AttendanceOverview from "./AttendanceChart";
|
|
||||||
import ExpenseAnalysis from "./ExpenseAnalysis";
|
import ExpenseAnalysis from "./ExpenseAnalysis";
|
||||||
import ExpenseStatus from "./ExpenseStatus";
|
import ExpenseStatus from "./ExpenseStatus";
|
||||||
import ExpenseByProject from "./ExpenseByProject";
|
import ExpenseByProject from "./ExpenseByProject";
|
||||||
|
import ProjectStatistics from "../Project/ProjectStatistics";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
|
|
||||||
@ -29,16 +29,16 @@ const Dashboard = () => {
|
|||||||
<div className="row gy-4">
|
<div className="row gy-4">
|
||||||
{isAllProjectsSelected && (
|
{isAllProjectsSelected && (
|
||||||
<div className="col-sm-6 col-lg-4">
|
<div className="col-sm-6 col-lg-4">
|
||||||
<Projects />
|
<Projects />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
||||||
<Teams />
|
<Teams />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
||||||
<TasksCard/>
|
<TasksCard />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isAllProjectsSelected && (
|
{isAllProjectsSelected && (
|
||||||
@ -46,32 +46,31 @@ const Dashboard = () => {
|
|||||||
<ProjectCompletionChart />
|
<ProjectCompletionChart />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isAllProjectsSelected && (
|
|
||||||
<div className="col-xxl-6 col-lg-6">
|
|
||||||
<ProjectOverview />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="col-xxl-6 col-lg-6">
|
<div className="col-xxl-6 col-lg-6">
|
||||||
<ProjectProgressChart />
|
<ProjectProgressChart />
|
||||||
</div>
|
</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 && (
|
{!isAllProjectsSelected && (
|
||||||
<div className="col-12 col-md-6 mb-sm-0 mb-4">
|
<div className="col-12 col-md-6 mb-sm-0 mb-4">
|
||||||
<AttendanceOverview />
|
<AttendanceOverview />
|
||||||
</div>
|
</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">
|
<div className="col-12 col-md-6">
|
||||||
<ExpenseByProject />
|
<ExpenseByProject />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,11 +7,12 @@ import { FormProvider, useForm } from "react-hook-form";
|
|||||||
import { formatCurrency, localToUtc } from "../../utils/appUtils";
|
import { formatCurrency, localToUtc } from "../../utils/appUtils";
|
||||||
import { useProjectName } from "../../hooks/useProjects";
|
import { useProjectName } from "../../hooks/useProjects";
|
||||||
import { SpinnerLoader } from "../common/Loader";
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
|
import flatColors from "../Charts/flatColor";
|
||||||
|
|
||||||
const ExpenseAnalysis = () => {
|
const ExpenseAnalysis = () => {
|
||||||
const projectId = useSelectedProject();
|
const projectId = useSelectedProject();
|
||||||
const [projectName, setProjectName] = useState("All Project");
|
const [projectName, setProjectName] = useState("All Project");
|
||||||
const { projectNames, loading } = useProjectName();
|
const { projectNames } = useProjectName();
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
defaultValues: { startDate: "", endDate: "" },
|
defaultValues: { startDate: "", endDate: "" },
|
||||||
@ -50,7 +51,7 @@ const ExpenseAnalysis = () => {
|
|||||||
labels,
|
labels,
|
||||||
legend: { show: false },
|
legend: { show: false },
|
||||||
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
|
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
|
||||||
colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
|
colors: flatColors,
|
||||||
plotOptions: {
|
plotOptions: {
|
||||||
pie: {
|
pie: {
|
||||||
donut: {
|
donut: {
|
||||||
@ -79,22 +80,19 @@ const ExpenseAnalysis = () => {
|
|||||||
|
|
||||||
return (
|
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="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>
|
<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>
|
<p className="card-subtitle m-0">{projectName}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-end text-sm-end ">
|
<div className="text-end text-sm-end">
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<DateRangePicker1 />
|
<DateRangePicker1 />
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Card body */}
|
|
||||||
<div className="card-body position-relative">
|
<div className="card-body position-relative">
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div
|
<div
|
||||||
@ -114,7 +112,6 @@ const ExpenseAnalysis = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{!isLoading && report.length > 0 && (
|
{!isLoading && report.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{isFetching && (
|
{isFetching && (
|
||||||
@ -123,50 +120,59 @@ const ExpenseAnalysis = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="d-flex justify-content-center mb-3">
|
<div className="row">
|
||||||
<Chart
|
{/* Chart Column */}
|
||||||
options={donutOptions}
|
<div className="col-12 col-lg-6 d-flex justify-content-center mt-5 mb-3 mb-lg-0">
|
||||||
series={series}
|
<Chart
|
||||||
type="donut"
|
options={donutOptions}
|
||||||
width="100%"
|
series={series}
|
||||||
height={320}
|
type="donut"
|
||||||
/>
|
width="70%"
|
||||||
</div>
|
height={320}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mb-2 w-100">
|
{/* Data/Legend Column */}
|
||||||
<div className="row g-2">
|
<div className="col-12 mt-6 col-lg-6">
|
||||||
{report.map((item, idx) => (
|
<div className="row g-4">
|
||||||
<div
|
{report.map((item, idx) => (
|
||||||
className="col-12 col-sm-6 d-flex align-items-start"
|
<div
|
||||||
key={idx}
|
className="col-6"
|
||||||
>
|
key={idx}
|
||||||
<div className="avatar me-2">
|
style={{
|
||||||
<span
|
borderLeft: `3px solid ${flatColors[idx % flatColors.length]}`,
|
||||||
className="avatar-initial rounded-2"
|
}}
|
||||||
style={{
|
>
|
||||||
backgroundColor:
|
<div className="d-flex flex-column text-start">
|
||||||
donutOptions.colors[idx % donutOptions.colors.length],
|
<small
|
||||||
}}
|
className="fw-semibold text-wrap text-dark"
|
||||||
>
|
style={{
|
||||||
<i className="bx bx-receipt fs-4"></i>
|
fontSize: "0.8rem",
|
||||||
</span>
|
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>
|
||||||
<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">
|
</div>
|
||||||
{formatCurrency(item.totalApprovedAmount)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -92,7 +92,7 @@ const ExpenseByProject = () => {
|
|||||||
<div className="card shadow-sm h-100 rounded ">
|
<div className="card shadow-sm h-100 rounded ">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="card-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">
|
<div className="text-start">
|
||||||
<h5 className="mb-1 me-6 card-title">Monthly Expense -</h5>
|
<h5 className="mb-1 me-6 card-title">Monthly Expense -</h5>
|
||||||
<p className="card-subtitle m-0">{projectName}</p>
|
<p className="card-subtitle m-0">{projectName}</p>
|
||||||
|
|||||||
@ -103,7 +103,7 @@ const ExpenseStatus = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<small
|
<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`}
|
} text-gray-500`}
|
||||||
>
|
>
|
||||||
{item?.count || 0}
|
{item?.count || 0}
|
||||||
@ -122,7 +122,7 @@ const ExpenseStatus = () => {
|
|||||||
{isManageExpense && (
|
{isManageExpense && (
|
||||||
<div
|
<div
|
||||||
className="d-flex justify-content-between align-items-center cursor-pointer"
|
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">
|
<div className="d-block">
|
||||||
<span
|
<span
|
||||||
@ -137,7 +137,7 @@ const ExpenseStatus = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="d-flex align-items-center gap-2">
|
<div className="d-flex align-items-center gap-2">
|
||||||
<span
|
<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`}
|
} text-md`}
|
||||||
>
|
>
|
||||||
{formatCurrency(data?.totalAmount || 0)}
|
{formatCurrency(data?.totalAmount || 0)}
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import HorizontalBarChart from "../Charts/HorizontalBarChart";
|
import HorizontalBarChart from "../Charts/HorizontalBarChart";
|
||||||
import { useProjects } from "../../hooks/useProjects";
|
import { useProjects } from "../../hooks/useProjects";
|
||||||
|
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||||
|
|
||||||
const ProjectCompletionChart = () => {
|
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
|
// Bar chart logic
|
||||||
const projectNames = projects?.map((p) => p.name) || [];
|
const projectNames = projects?.map((p) => p.name) || [];
|
||||||
const projectProgress =
|
const projectProgress =
|
||||||
|
|||||||
@ -99,7 +99,7 @@ const MenuItem = (item) => {
|
|||||||
className={`menu-link ${hasSubmenu ? "menu-toggle" : ""}`}
|
className={`menu-link ${hasSubmenu ? "menu-toggle" : ""}`}
|
||||||
target={item.link?.includes("http") ? "_blank" : undefined}
|
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>
|
<div>{item.name}</div>
|
||||||
{item.available === false && (
|
{item.available === false && (
|
||||||
<div className="badge bg-label-primary fs-tiny rounded-pill ms-auto">
|
<div className="badge bg-label-primary fs-tiny rounded-pill ms-auto">
|
||||||
|
|||||||
@ -40,6 +40,8 @@ const ActionPaymentRequest = ({ requestId }) => {
|
|||||||
error: PaymentModeError,
|
error: PaymentModeError,
|
||||||
} = usePaymentMode();
|
} = usePaymentMode();
|
||||||
|
|
||||||
|
console.log("Kartik", data)
|
||||||
|
|
||||||
const IsReview = useHasUserPermission(REVIEW_EXPENSE);
|
const IsReview = useHasUserPermission(REVIEW_EXPENSE);
|
||||||
const [imageLoaded, setImageLoaded] = useState({});
|
const [imageLoaded, setImageLoaded] = useState({});
|
||||||
|
|
||||||
@ -175,6 +177,16 @@ const ActionPaymentRequest = ({ requestId }) => {
|
|||||||
const newFiles = files.filter((_, i) => i !== index);
|
const newFiles = files.filter((_, i) => i !== index);
|
||||||
setValue("billAttachments", newFiles, { shouldValidate: true });
|
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 (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
|
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
|
||||||
@ -219,7 +231,7 @@ const ActionPaymentRequest = ({ requestId }) => {
|
|||||||
{PaymentModeLoading ? (
|
{PaymentModeLoading ? (
|
||||||
<option disabled>Loading...</option>
|
<option disabled>Loading...</option>
|
||||||
) : (
|
) : (
|
||||||
PaymentModes?.map((payment) => (
|
filteredPaymentModes?.map((payment) => (
|
||||||
<option key={payment.id} value={payment.id}>
|
<option key={payment.id} value={payment.id}>
|
||||||
{payment.name}
|
{payment.name}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
60
src/components/PaymentRequest/PaymentRequestFilterChips.jsx
Normal file
60
src/components/PaymentRequest/PaymentRequestFilterChips.jsx
Normal 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;
|
||||||
@ -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 { FormProvider, useForm, Controller } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "./PaymentRequestSchema";
|
import { defaultPaymentRequestFilter, SearchPaymentRequestSchema } from "./PaymentRequestSchema";
|
||||||
|
|
||||||
import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker";
|
import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker";
|
||||||
import SelectMultiple from "../common/SelectMultiple";
|
import SelectMultiple from "../common/SelectMultiple";
|
||||||
@ -13,7 +13,7 @@ import moment from "moment";
|
|||||||
import { usePaymentRequestFilter } from "../../hooks/useExpense";
|
import { usePaymentRequestFilter } from "../../hooks/useExpense";
|
||||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata, clearFilter }, ref) => {
|
||||||
const { status } = useParams();
|
const { status } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const selectedProjectId = useSelector(
|
const selectedProjectId = useSelector(
|
||||||
@ -38,10 +38,23 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
|
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
|
||||||
const [resetKey, setResetKey] = useState(0);
|
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({
|
const methods = useForm({
|
||||||
resolver: zodResolver(SearchPaymentRequestSchema),
|
resolver: zodResolver(SearchPaymentRequestSchema),
|
||||||
defaultValues: defaultPaymentRequestFilter,
|
defaultValues: dynamicDefaultFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setValue, watch } = methods;
|
const { control, handleSubmit, reset, setValue, watch } = methods;
|
||||||
@ -52,12 +65,28 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleGroupChange = (e) => {
|
const handleGroupChange = (e) => {
|
||||||
const group = groupByList.find((g) => g.id === e.target.value);
|
const group = groupByList.find((g) => g.id === e.target.value);
|
||||||
if (group) setSelectedGroup(group);
|
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) => {
|
const onSubmit = (formData) => {
|
||||||
onApply({
|
onApply({
|
||||||
@ -197,6 +226,6 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => {
|
|||||||
</FormProvider>
|
</FormProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default PaymentRequestFilterPanel;
|
export default PaymentRequestFilterPanel;
|
||||||
@ -20,8 +20,9 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import Error from "../common/Error";
|
import Error from "../common/Error";
|
||||||
import Pagination from "../common/Pagination";
|
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 { setManageRequest, setVieRequest } = usePaymentRequestContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@ -92,33 +93,6 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
align: "text-start",
|
align: "text-start",
|
||||||
getValue: (e) => e.title || "N/A",
|
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",
|
key: "createdAt",
|
||||||
label: "Created At",
|
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 page-min-h table-responsive px-sm-4">
|
||||||
<div className="card-datatable mx-2" id="payment-request-table ">
|
<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">
|
<table className="table border-top dataTable text-nowrap align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
import ReactApexChart from "react-apexcharts";
|
import ReactApexChart from "react-apexcharts";
|
||||||
import Chart from "react-apexcharts";
|
import Chart from "react-apexcharts";
|
||||||
|
|
||||||
const ProjectOverview = ({ project }) => {
|
const ProjectStatistics = ({ project }) => {
|
||||||
const { data } = useProjects();
|
const { data } = useProjects();
|
||||||
const [current_project, setCurrentProject] = useState(
|
const [current_project, setCurrentProject] = useState(
|
||||||
data?.find((pro) => pro.id == project)
|
data?.find((pro) => pro.id == project)
|
||||||
@ -165,7 +165,7 @@ const ProjectOverview = ({ project }) => {
|
|||||||
}, [selectedProject]);
|
}, [selectedProject]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card" style={{ minHeight: "490px" }}>
|
<div className="card h-100">
|
||||||
<div className="card-header text-start">
|
<div className="card-header text-start">
|
||||||
<h5 className="card-action-title mb-0">
|
<h5 className="card-action-title mb-0">
|
||||||
{" "}
|
{" "}
|
||||||
@ -173,78 +173,78 @@ const ProjectOverview = ({ project }) => {
|
|||||||
<span className="ms-2 fw-bold">Project Statistics</span>
|
<span className="ms-2 fw-bold">Project Statistics</span>
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<ul className="list-unstyled m-0 p-0">
|
<ul className="list-unstyled m-0 p-0">
|
||||||
<li className="d-flex flex-wrap">
|
<li className="d-flex flex-wrap">
|
||||||
<div className="w-100 d-flex flex-wrap">
|
<div className="w-100 d-flex flex-wrap">
|
||||||
{/* Centered Chart */}
|
{/* Centered Chart */}
|
||||||
<div className="w-100 d-flex justify-content-center mb-3">
|
<div className="w-100 d-flex justify-content-center mb-3">
|
||||||
<div >
|
<div >
|
||||||
<Chart
|
<Chart
|
||||||
options={radialBarOptions}
|
options={radialBarOptions}
|
||||||
series={radialBarOptions.series}
|
series={radialBarOptions.series}
|
||||||
type="radialBar"
|
type="radialBar"
|
||||||
height="100%"
|
height="100%"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<div className="d-flex flex-column text-start">
|
|
||||||
<small className="fw-bold">Tasks Planned</small>
|
{/* Info Section */}
|
||||||
<h5 className="mb-0">
|
<div className="mb-2" style={{ flex: "1 1 auto" }}>
|
||||||
{FormattedNumber(current_project?.plannedWork)}
|
<div>
|
||||||
</h5>
|
{/* 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>
|
||||||
</div>
|
</div>
|
||||||
|
</li>
|
||||||
{/* Tasks Completed */}
|
</ul>
|
||||||
<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>
|
</div>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProjectOverview;
|
export default ProjectStatistics;
|
||||||
@ -105,7 +105,7 @@ const JobList = () => {
|
|||||||
{jobGrid.map((col) => (
|
{jobGrid.map((col) => (
|
||||||
<th
|
<th
|
||||||
key={col.key}
|
key={col.key}
|
||||||
className={`${col.className} text-center`}
|
className={`${col.align || "text-center"} ${col.className || ""}`}
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
<div className={col.className}>{col.label}</div>
|
<div className={col.className}>{col.label}</div>
|
||||||
@ -120,14 +120,14 @@ const JobList = () => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{Array.isArray(data?.data) && data.data.length > 0 ? (
|
{Array.isArray(data?.data) && data.data.length > 0 ? (
|
||||||
data.data.map((row, i) => (
|
data.data.map((row, i) => (
|
||||||
<tr key={i}>
|
<tr key={i} className="text-start">
|
||||||
{jobGrid.map((col) => (
|
{jobGrid.map((col) => (
|
||||||
<td key={col.key} className={col.className}>
|
<td key={col.key} className={col.className}>
|
||||||
{col.getValue(row)}
|
{col.getValue(row)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
<td>
|
<td>
|
||||||
<div className="dropdown">
|
<div className="dropdown text-center">
|
||||||
<button
|
<button
|
||||||
className="btn btn-icon dropdown-toggle hide-arrow"
|
className="btn btn-icon dropdown-toggle hide-arrow"
|
||||||
data-bs-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const Jobs = () => {
|
|||||||
>
|
>
|
||||||
<ManageJob Job={manageJob.jobId} />
|
<ManageJob Job={manageJob.jobId} />
|
||||||
</OffcanvasComponent>
|
</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="row">
|
||||||
<div className="col-12 py-2 d-flex justify-content-end ">
|
<div className="col-12 py-2 d-flex justify-content-end ">
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
|
|||||||
@ -117,31 +117,37 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
|
|||||||
<Label htmlFor="name" required>
|
<Label htmlFor="name" required>
|
||||||
Client
|
Client
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<div className="d-flex align-items-center gap-2">
|
||||||
className="select2 form-select form-select-sm"
|
<select
|
||||||
aria-label="Default select example"
|
className="select2 form-select form-select-sm flex-grow-1"
|
||||||
{...register("clientId", {
|
aria-label="Default select example"
|
||||||
required: "Client is required",
|
{...register("clientId", {
|
||||||
valueAsNumber: false,
|
required: "Client is required",
|
||||||
})}
|
valueAsNumber: false,
|
||||||
>
|
})}
|
||||||
{isLoading ? (
|
>
|
||||||
<option>Loading...</option>
|
{isLoading ? (
|
||||||
) : (
|
<option>Loading...</option>
|
||||||
<>
|
) : (
|
||||||
<option value="">Select Client</option>
|
<>
|
||||||
{organization?.data?.map((org) => (
|
<option value="">Select Client</option>
|
||||||
<option key={org.id} value={org.id}>
|
{organization?.data?.map((org) => (
|
||||||
{org.name}
|
<option key={org.id} value={org.id}>
|
||||||
</option>
|
{org.name}
|
||||||
))}
|
</option>
|
||||||
</>
|
))}
|
||||||
)}
|
</>
|
||||||
</select>
|
)}
|
||||||
|
</select>
|
||||||
|
<i
|
||||||
|
className="bx bx-plus-circle bx-xs cursor-pointer text-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{errors?.clientId && (
|
{errors?.clientId && (
|
||||||
<span className="danger-text">{errors.clientId.message}</span>
|
<span className="danger-text">{errors.clientId.message}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 mb-2">
|
<div className="col-12 mb-2">
|
||||||
<Label htmlFor="name" required>
|
<Label htmlFor="name" required>
|
||||||
Project Name
|
Project Name
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const AddPayment = ({ onClose }) => {
|
|||||||
|
|
||||||
<div className="col-12 col-md-6 mb-2">
|
<div className="col-12 col-md-6 mb-2">
|
||||||
<Label required>Transaction Date </Label>
|
<Label required>Transaction Date </Label>
|
||||||
<DatePicker
|
<DatePicker className="w-100"
|
||||||
name="paymentReceivedDate"
|
name="paymentReceivedDate"
|
||||||
control={control}
|
control={control}
|
||||||
minDate={
|
minDate={
|
||||||
|
|||||||
@ -29,14 +29,14 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
|||||||
const searchDebounce = useDebounce(searchString, 500);
|
const searchDebounce = useDebounce(searchString, 500);
|
||||||
|
|
||||||
const { data, isLoading, isError, error } = useCollections(
|
const { data, isLoading, isError, error } = useCollections(
|
||||||
ITEMS_PER_PAGE,
|
selectedProject,
|
||||||
currentPage,
|
searchDebounce,
|
||||||
localToUtc(fromDate),
|
localToUtc(fromDate),
|
||||||
localToUtc(toDate),
|
localToUtc(toDate),
|
||||||
isPending,
|
ITEMS_PER_PAGE,
|
||||||
|
currentPage,
|
||||||
true,
|
true,
|
||||||
selectedProject,
|
isPending
|
||||||
searchDebounce
|
|
||||||
);
|
);
|
||||||
const { setProcessedPayment, setAddPayment, setViewCollection } =
|
const { setProcessedPayment, setAddPayment, setViewCollection } =
|
||||||
useCollectionContext();
|
useCollectionContext();
|
||||||
@ -186,84 +186,84 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
|||||||
canViewCollection ||
|
canViewCollection ||
|
||||||
canEditCollection ||
|
canEditCollection ||
|
||||||
canCreate) && (
|
canCreate) && (
|
||||||
<td
|
<td
|
||||||
className="sticky-action-column text-center"
|
className="sticky-action-column text-center"
|
||||||
style={{ padding: "12px 8px" }}
|
style={{ padding: "12px 8px" }}
|
||||||
>
|
>
|
||||||
<div className="dropdown z-2">
|
<div className="dropdown z-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
|
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
|
||||||
data-bs-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||||
data-bs-toggle="tooltip"
|
data-bs-toggle="tooltip"
|
||||||
data-bs-offset="0,8"
|
data-bs-offset="0,8"
|
||||||
data-bs-placement="top"
|
data-bs-placement="top"
|
||||||
data-bs-custom-class="tooltip-dark"
|
data-bs-custom-class="tooltip-dark"
|
||||||
title="More Action"
|
title="More Action"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul className="dropdown-menu dropdown-menu-end">
|
<ul className="dropdown-menu dropdown-menu-end">
|
||||||
{/* View */}
|
{/* View */}
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className="dropdown-item cursor-pointer"
|
className="dropdown-item cursor-pointer"
|
||||||
onClick={() => setViewCollection(row.id)}
|
onClick={() => setViewCollection(row.id)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-show me-2 text-primary"></i>
|
<i className="bx bx-show me-2 text-primary"></i>
|
||||||
<span>View</span>
|
<span>View</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{/* Only if not completed */}
|
{/* Only if not completed */}
|
||||||
{!row?.markAsCompleted && (
|
{!row?.markAsCompleted && (
|
||||||
<>
|
<>
|
||||||
{/* Add Payment */}
|
{/* Add Payment */}
|
||||||
{(isAdmin || canAddPayment) && (
|
{(isAdmin || canAddPayment) && (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className="dropdown-item cursor-pointer"
|
className="dropdown-item cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setAddPayment({
|
setAddPayment({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
invoiceId: row.id,
|
invoiceId: row.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<i className="bx bx-wallet me-2 text-warning"></i>
|
<i className="bx bx-wallet me-2 text-warning"></i>
|
||||||
<span>Add Payment</span>
|
<span>Add Payment</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Mark Payment */}
|
{/* Mark Payment */}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className="dropdown-item cursor-pointer"
|
className="dropdown-item cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setProcessedPayment({
|
setProcessedPayment({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
invoiceId: row.id,
|
invoiceId: row.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<i className="bx bx-check-circle me-2 text-success"></i>
|
<i className="bx bx-check-circle me-2 text-success"></i>
|
||||||
<span>Mark Payment</span>
|
<span>Mark Payment</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -2,39 +2,80 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { CollectionRepository } from "../repositories/ColllectionRepository";
|
import { CollectionRepository } from "../repositories/ColllectionRepository";
|
||||||
import showToast from "../services/toastService";
|
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 = (
|
export const useCollections = (
|
||||||
pageSize,
|
projectId,
|
||||||
pageNumber,
|
searchString,
|
||||||
fromDate,
|
fromDate,
|
||||||
toDate,
|
toDate,
|
||||||
isPending,
|
pageSize,
|
||||||
|
pageNumber,
|
||||||
isActive,
|
isActive,
|
||||||
projectId,
|
isPending
|
||||||
searchString
|
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
"collections",
|
"collections",
|
||||||
pageSize,
|
|
||||||
pageNumber,
|
|
||||||
fromDate,
|
|
||||||
toDate,
|
|
||||||
isPending,
|
|
||||||
isActive,
|
|
||||||
projectId,
|
projectId,
|
||||||
searchString,
|
searchString,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
pageSize,
|
||||||
|
pageNumber,
|
||||||
|
isActive,
|
||||||
|
isPending,
|
||||||
],
|
],
|
||||||
|
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const response = await CollectionRepository.getCollections(
|
const response = await CollectionRepository.getCollections(
|
||||||
pageSize,
|
projectId,
|
||||||
pageNumber,
|
searchString,
|
||||||
fromDate,
|
fromDate,
|
||||||
toDate,
|
toDate,
|
||||||
isPending,
|
pageSize,
|
||||||
|
pageNumber,
|
||||||
isActive,
|
isActive,
|
||||||
projectId,
|
isPending
|
||||||
searchString
|
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
@ -44,15 +85,14 @@ export const useCollections = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCollection = (collectionId) => {
|
||||||
export const useCollection =(collectionId)=>{
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey:["collection",collectionId],
|
queryKey: ["collection", collectionId],
|
||||||
queryFn:async()=> {
|
queryFn: async () => {
|
||||||
const resp = await CollectionRepository.getCollection(collectionId);
|
const resp = await CollectionRepository.getCollection(collectionId);
|
||||||
return resp.data
|
return resp.data
|
||||||
},
|
},
|
||||||
enabled:!!collectionId
|
enabled: !!collectionId
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,11 +173,11 @@ export const useAddComment = (onSuccessCallBack) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateCollection =(onSuccessCallBack)=>{
|
export const useUpdateCollection = (onSuccessCallBack) => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn:async({collectionId,payload})=> await CollectionRepository.updateCollection(collectionId,payload),
|
mutationFn: async ({ collectionId, payload }) => await CollectionRepository.updateCollection(collectionId, payload),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
client.invalidateQueries({ queryKey: ["collections"] });
|
client.invalidateQueries({ queryKey: ["collections"] });
|
||||||
client.invalidateQueries({ queryKey: ["collection"] });
|
client.invalidateQueries({ queryKey: ["collection"] });
|
||||||
showToast("Collection Updated Successfully", "success");
|
showToast("Collection Updated Successfully", "success");
|
||||||
|
|||||||
@ -20,13 +20,13 @@ export const useCurrentService = () => {
|
|||||||
|
|
||||||
// ------------------------------Query-------------------
|
// ------------------------------Query-------------------
|
||||||
|
|
||||||
export const useProjects = (pageSize,pageNumber) => {
|
export const useProjects = (pageNumber,pageSize) => {
|
||||||
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
|
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["ProjectsList",pageSize,pageNumber],
|
queryKey: ["ProjectsList",pageNumber,pageSize],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const response = await ProjectRepository.getProjectList(pageSize,pageNumber);
|
const response = await ProjectRepository.getProjectList(pageNumber,pageSize);
|
||||||
return response.data;
|
return response?.data?.data;
|
||||||
},
|
},
|
||||||
enabled: !!loggedUser,
|
enabled: !!loggedUser,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const useAdvancePaymentContext = () => {
|
|||||||
};
|
};
|
||||||
const AdvancePaymentPage = () => {
|
const AdvancePaymentPage = () => {
|
||||||
const [balance, setBalance] = useState(null);
|
const [balance, setBalance] = useState(null);
|
||||||
const {control, reset, watch } = useForm({
|
const { control, reset, watch } = useForm({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
employeeId: "",
|
employeeId: "",
|
||||||
},
|
},
|
||||||
@ -40,13 +40,12 @@ const AdvancePaymentPage = () => {
|
|||||||
});
|
});
|
||||||
}, [reset]);
|
}, [reset]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdvancePaymentContext.Provider value={{ setBalance }}>
|
<AdvancePaymentContext.Provider value={{ setBalance }}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[
|
data={[
|
||||||
{ label: "Home", link: "/" },
|
{ label: "Home", link: "/dashboard" },
|
||||||
{ label: "Finance", link: "/advance-payment" },
|
{ label: "Finance", link: "/advance-payment" },
|
||||||
{ label: "Advance Payment" },
|
{ label: "Advance Payment" },
|
||||||
]}
|
]}
|
||||||
@ -73,7 +72,12 @@ const AdvancePaymentPage = () => {
|
|||||||
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 ? <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",
|
type: "currency",
|
||||||
currency: "INR",
|
currency: "INR",
|
||||||
})}
|
})}
|
||||||
@ -85,8 +89,6 @@ const AdvancePaymentPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AdvancePaymentList employeeId={selectedEmployeeId} />
|
<AdvancePaymentList employeeId={selectedEmployeeId} />
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AdvancePaymentContext.Provider>
|
</AdvancePaymentContext.Provider>
|
||||||
|
|||||||
@ -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 { useForm, useFormContext } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
@ -19,7 +25,10 @@ import {
|
|||||||
VIEW_SELF_EXPENSE,
|
VIEW_SELF_EXPENSE,
|
||||||
} from "../../utils/constants";
|
} from "../../utils/constants";
|
||||||
|
|
||||||
import { defaultFilter, SearchSchema } from "../../components/Expenses/ExpenseSchema";
|
import {
|
||||||
|
defaultFilter,
|
||||||
|
SearchSchema,
|
||||||
|
} from "../../components/Expenses/ExpenseSchema";
|
||||||
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
@ -102,14 +111,14 @@ const ExpensePage = () => {
|
|||||||
setManageExpenseModal,
|
setManageExpenseModal,
|
||||||
setDocumentView,
|
setDocumentView,
|
||||||
filterData,
|
filterData,
|
||||||
removeFilterChip
|
removeFilterChip,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpenseContext.Provider value={contextValue}>
|
<ExpenseContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[{ label: "Home", link: "/" }, { label: "Expense" }]}
|
data={[{ label: "Home", link: "/dashboard" }, { label: "Expense" }]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{IsViewAll || IsViewSelf || IsCreatedAble ? (
|
{IsViewAll || IsViewSelf || IsCreatedAble ? (
|
||||||
@ -128,7 +137,6 @@ const ExpensePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6 text-end mt-2 mt-sm-0">
|
<div className="col-6 text-end mt-2 mt-sm-0">
|
||||||
|
|
||||||
{IsCreatedAble && (
|
{IsCreatedAble && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
@ -151,8 +159,6 @@ const ExpensePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ExpenseList
|
<ExpenseList
|
||||||
filters={filters}
|
filters={filters}
|
||||||
groupBy={groupBy}
|
groupBy={groupBy}
|
||||||
|
|||||||
@ -36,11 +36,10 @@ export const useGalleryContext = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ImageGalleryPage = () => {
|
const ImageGalleryPage = () => {
|
||||||
const [filter,setFilter] = useState()
|
const [filter, setFilter] = useState();
|
||||||
const selectedProjectId = useSelectedProject();
|
const selectedProjectId = useSelectedProject();
|
||||||
const { projectNames } = useProjectName();
|
const { projectNames } = useProjectName();
|
||||||
|
|
||||||
|
|
||||||
const [openGallery, setOpenGallery] = useState({ isOpen: false, data: null });
|
const [openGallery, setOpenGallery] = useState({ isOpen: false, data: null });
|
||||||
|
|
||||||
const { data: assignedServices = [], isLoading } =
|
const { data: assignedServices = [], isLoading } =
|
||||||
@ -56,24 +55,26 @@ const ImageGalleryPage = () => {
|
|||||||
setOpenGallery,
|
setOpenGallery,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
setOffcanvasContent("Gallery Filter",<GalleryFilterPanel onApply={setFilter}/>);
|
setOffcanvasContent(
|
||||||
|
"Gallery Filter",
|
||||||
return ()=>{
|
<GalleryFilterPanel onApply={setFilter} />
|
||||||
setOffcanvasContent("",null)
|
);
|
||||||
setShowTrigger(false);
|
|
||||||
}
|
|
||||||
},[])
|
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setOffcanvasContent("", null);
|
||||||
|
setShowTrigger(false);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GalleryContext.Provider value={contextMessager}>
|
<GalleryContext.Provider value={contextMessager}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[{ label: "Home", link: "/" }, { label: "Gallery" }]}
|
data={[{ label: "Home", link: "/dashboard" }, { label: "Gallery" }]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="card page-min-h p-2">
|
<div className="card page-min-h p-2">
|
||||||
@ -104,7 +105,7 @@ const ImageGalleryPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)} */}
|
)} */}
|
||||||
|
|
||||||
<ImageGalleryListView filter={filter}/>
|
<ImageGalleryListView filter={filter} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{openGallery?.isOpen && (
|
{openGallery?.isOpen && (
|
||||||
|
|||||||
@ -6,12 +6,15 @@ import OrganizationsList from "../../components/Organization/OrganizationsList";
|
|||||||
const OrganizationPage = () => {
|
const OrganizationPage = () => {
|
||||||
const { isOpen, orgData, startStep, onOpen, flowType } =
|
const { isOpen, orgData, startStep, onOpen, flowType } =
|
||||||
useOrganizationModal();
|
useOrganizationModal();
|
||||||
const [searchText, setSearchText] = useState("")
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<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 my-3 px-sm-4 px-0">
|
||||||
<div className="card-body py-2 px-3">
|
<div className="card-body py-2 px-3">
|
||||||
@ -35,21 +38,18 @@ const OrganizationPage = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => onOpen({ startStep: 2, flowType: "default" })}
|
onClick={() => onOpen({ startStep: 2, flowType: "default" })}
|
||||||
>
|
>
|
||||||
<i className="bx bx-plus-circle me-2"></i>
|
<i className="bx bx-plus-circle me-2"></i>
|
||||||
<span className="d-none d-md-inline-block">
|
<span className="d-none d-md-inline-block">
|
||||||
Add New Organization
|
Add New Organization
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card page-min-h px-sm-4">
|
<div className="card page-min-h px-sm-4">
|
||||||
<OrganizationsList searchText={searchText} />
|
<OrganizationsList searchText={searchText} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 Breadcrumb from "../../components/common/Breadcrumb";
|
||||||
import GlobalModel from "../../components/common/GlobalModel";
|
import GlobalModel from "../../components/common/GlobalModel";
|
||||||
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
|
import ManagePaymentRequest from "../../components/PaymentRequest/ManagePaymentRequest";
|
||||||
import ExpenseFilterPanel from "../../components/Expenses/ExpenseFilterPanel";
|
|
||||||
import { useFab } from "../../Context/FabContext";
|
import { useFab } from "../../Context/FabContext";
|
||||||
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
|
import PaymentRequestList from "../../components/PaymentRequest/PaymentRequestList";
|
||||||
import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRequestFilterPanel";
|
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 ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
|
||||||
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
import PreviewDocument from "../../components/Expenses/PreviewDocument";
|
||||||
import MakeExpense from "../../components/PaymentRequest/MakeExpense";
|
import MakeExpense from "../../components/PaymentRequest/MakeExpense";
|
||||||
@ -15,26 +14,22 @@ export const PaymentRequestContext = createContext();
|
|||||||
export const usePaymentRequestContext = () => {
|
export const usePaymentRequestContext = () => {
|
||||||
const context = useContext(PaymentRequestContext);
|
const context = useContext(PaymentRequestContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error("usePaymentRequestContext must be used within an ExpenseProvider");
|
throw new Error("usePaymentRequestContext must be used within PaymentRequestContext.Provider");
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PaymentRequestPage = () => {
|
const PaymentRequestPage = () => {
|
||||||
const [ManageRequest, setManageRequest] = useState({
|
const [ManageRequest, setManageRequest] = useState({ IsOpen: null, RequestId: null });
|
||||||
IsOpen: null,
|
const [ViewRequest, setVieRequest] = useState({ view: false, requestId: null });
|
||||||
RequestId: null,
|
|
||||||
});
|
|
||||||
const [ViewRequest,setVieRequest] = useState({view:false,requestId:null})
|
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
|
||||||
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
|
const [filters, setFilters] = useState(defaultPaymentRequestFilter);
|
||||||
const [ViewDocument, setDocumentView] = useState({
|
const [filterData, setFilterdata] = useState(null);
|
||||||
IsOpen: false,
|
const [ViewDocument, setDocumentView] = useState({ IsOpen: false, Image: null });
|
||||||
Image: null,
|
const [isExpenseGenerate, setIsExpenseGenerate] = useState({ IsOpen: null, RequestId: null });
|
||||||
});
|
const [modalSize, setModalSize] = useState("md");
|
||||||
const [isExpenseGenerate,setIsExpenseGenerate] = useState({IsOpen: null,
|
|
||||||
RequestId: null,})
|
|
||||||
const [modalSize,setModalSize] = useState("md")
|
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const updatedRef = useRef();
|
||||||
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
|
||||||
const contextValue = {
|
const contextValue = {
|
||||||
setManageRequest,
|
setManageRequest,
|
||||||
@ -42,15 +37,21 @@ const PaymentRequestPage = () => {
|
|||||||
setDocumentView,
|
setDocumentView,
|
||||||
setModalSize,
|
setModalSize,
|
||||||
setIsExpenseGenerate,
|
setIsExpenseGenerate,
|
||||||
isExpenseGenerate
|
isExpenseGenerate,
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const clearFilter = () => setFilters(defaultPaymentRequestFilter);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
setOffcanvasContent(
|
setOffcanvasContent(
|
||||||
"Payment Request Filters",
|
"Payment Request Filters",
|
||||||
<PaymentRequestFilterPanel onApply={setFilters} />
|
<PaymentRequestFilterPanel
|
||||||
|
onApply={setFilters}
|
||||||
|
ref={updatedRef}
|
||||||
|
clearFilter={clearFilter}
|
||||||
|
setFilterdata={setFilterdata}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
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 (
|
return (
|
||||||
<PaymentRequestContext.Provider value={contextValue}>
|
<PaymentRequestContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
{/* Breadcrumb */}
|
{/* Breadcrumb */}
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[
|
data={[
|
||||||
{ label: "Home", link: "/" },
|
{ label: "Home", link: "/dashboard" },
|
||||||
{ label: "Finance", link: "/Payment Request" },
|
{ label: "Finance", link: "/Payment Request" },
|
||||||
{ label: "Payment Request" },
|
{ label: "Payment Request" },
|
||||||
]}
|
]}
|
||||||
@ -81,7 +98,7 @@ const PaymentRequestPage = () => {
|
|||||||
className="form-control form-control-sm w-auto"
|
className="form-control form-control-sm w-auto"
|
||||||
placeholder="Search Payment Request"
|
placeholder="Search Payment Request"
|
||||||
value={search}
|
value={search}
|
||||||
style={{minWidth:"200px"}}
|
style={{ minWidth: "200px" }}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -109,6 +126,9 @@ const PaymentRequestPage = () => {
|
|||||||
<PaymentRequestList
|
<PaymentRequestList
|
||||||
search={search}
|
search={search}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
|
filterData={filterData}
|
||||||
|
removeFilterChip={handleRemoveChip}
|
||||||
|
clearFilter={clearFilter}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Add/Edit Modal */}
|
{/* Add/Edit Modal */}
|
||||||
@ -116,16 +136,12 @@ const PaymentRequestPage = () => {
|
|||||||
<GlobalModel
|
<GlobalModel
|
||||||
isOpen
|
isOpen
|
||||||
size="lg"
|
size="lg"
|
||||||
closeModal={() =>
|
closeModal={() => setManageRequest({ IsOpen: null, expenseId: null })}
|
||||||
setManageRequest({ IsOpen: null, expenseId: null })
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<ManagePaymentRequest
|
<ManagePaymentRequest
|
||||||
key={ManageRequest.RequestId ?? "new"}
|
key={ManageRequest.RequestId ?? "new"}
|
||||||
requestToEdit={ManageRequest.RequestId}
|
requestToEdit={ManageRequest.RequestId}
|
||||||
closeModal={() =>
|
closeModal={() => setManageRequest({ IsOpen: null, RequestId: null })}
|
||||||
setManageRequest({ IsOpen: null, RequestId: null })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
@ -137,17 +153,18 @@ const PaymentRequestPage = () => {
|
|||||||
modalType="top"
|
modalType="top"
|
||||||
closeModal={() => setVieRequest({ requestId: null, view: false })}
|
closeModal={() => setVieRequest({ requestId: null, view: false })}
|
||||||
>
|
>
|
||||||
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
|
<ViewPaymentRequest requestId={ViewRequest?.requestId} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
{isExpenseGenerate.IsOpen && (
|
{isExpenseGenerate.IsOpen && (
|
||||||
<GlobalModel
|
<GlobalModel
|
||||||
isOpen
|
isOpen
|
||||||
size="md"
|
size="md"
|
||||||
closeModal={() => setIsExpenseGenerate({IsOpen:false, requestId: null})}
|
closeModal={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
|
||||||
>
|
>
|
||||||
<MakeExpe
|
<MakeExpense
|
||||||
nse onClose={() => setIsExpenseGenerate({IsOpen:false, requestId: null})} />
|
onClose={() => setIsExpenseGenerate({ IsOpen: false, requestId: null })}
|
||||||
|
/>
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -161,7 +178,6 @@ const PaymentRequestPage = () => {
|
|||||||
<PreviewDocument imageUrl={ViewDocument.Image} />
|
<PreviewDocument imageUrl={ViewDocument.Image} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</PaymentRequestContext.Provider>
|
</PaymentRequestContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -101,7 +101,7 @@ const CollectionPage = () => {
|
|||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<AccessDenied
|
<AccessDenied
|
||||||
data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
|
data={[{ label: "Home", link: "/dashboard" }, { label: "Collection" }]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -109,7 +109,10 @@ const CollectionPage = () => {
|
|||||||
<CollectionContext.Provider value={contextMassager}>
|
<CollectionContext.Provider value={contextMassager}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<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">
|
<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) */}
|
{/* 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 align-md-items-center gap-2 gap-md-3 mb-3 mb-md-0">
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<DateRangePicker1 howManyDay={180} />
|
<DateRangePicker1 howManyDay={180} startField="fromDate"
|
||||||
|
endField="toDate" />
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|
||||||
<div className="form-check form-switch d-flex align-items-center mt-1">
|
<div className="form-check form-switch d-flex align-items-center mt-1">
|
||||||
@ -152,16 +156,18 @@ const CollectionPage = () => {
|
|||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setCollection({ isOpen: true, invoiceId: null })}
|
onClick={() =>
|
||||||
|
setCollection({ isOpen: true, invoiceId: null })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i className="bx bx-plus-circle me-2"></i>
|
<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>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CollectionList
|
<CollectionList
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from "react";
|
|||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { useNavigate } from "react-router-dom";
|
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 AboutProject from "../../components/Project/AboutProject";
|
||||||
import ProjectNav from "../../components/Project/ProjectNav";
|
import ProjectNav from "../../components/Project/ProjectNav";
|
||||||
import Teams from "../../components/Project/Team/Teams";
|
import Teams from "../../components/Project/Team/Teams";
|
||||||
@ -15,7 +15,7 @@ import { useProjectDetails, useProjectName } from "../../hooks/useProjects";
|
|||||||
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
||||||
import eventBus from "../../services/eventBus";
|
import eventBus from "../../services/eventBus";
|
||||||
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
|
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
|
||||||
import AttendanceOverview from "../../components/Dashboard/AttendanceChart";
|
import AttendanceOverview from "../../components/Dashboard/AttendanceOverview";
|
||||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||||
import ProjectDocuments from "../../components/Project/ProjectDocuments";
|
import ProjectDocuments from "../../components/Project/ProjectDocuments";
|
||||||
import ProjectSetting from "../../components/Project/ProjectSetting";
|
import ProjectSetting from "../../components/Project/ProjectSetting";
|
||||||
|
|||||||
@ -4,26 +4,46 @@ import { DirectoryRepository } from "./DirectoryRepository";
|
|||||||
export const CollectionRepository = {
|
export const CollectionRepository = {
|
||||||
createNewCollection: (data) =>
|
createNewCollection: (data) =>
|
||||||
api.post(`/api/Collection/invoice/create`, data),
|
api.post(`/api/Collection/invoice/create`, data),
|
||||||
updateCollection:(id,data)=>{
|
updateCollection: (id, data) => {
|
||||||
api.put(`/api/Collection/invoice/edit/${id}`,data)
|
api.put(`/api/Collection/invoice/edit/${id}`, data)
|
||||||
},
|
},
|
||||||
getCollections: (pageSize, pageNumber,fromDate,toDate, isPending,isActive,projectId, 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}`;
|
// 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 = [];
|
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) {
|
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),
|
makeReceivePayment: (data) => api.post(`/api/Collection/invoice/payment/received`, data),
|
||||||
markPaymentReceived:(invoiceId)=>api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`),
|
markPaymentReceived: (invoiceId) => api.put(`/api/Collection/invoice/marked/completed/${invoiceId}`),
|
||||||
getCollection:(id)=>api.get(`/api/Collection/invoice/details/${id}`),
|
getCollection: (id) => api.get(`/api/Collection/invoice/details/${id}`),
|
||||||
addComment:(data)=>api.post(`/api/Collection/invoice/add/comment`,data)
|
addComment: (data) => api.post(`/api/Collection/invoice/add/comment`, data)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from "../utils/axiosClient";
|
import { api } from "../utils/axiosClient";
|
||||||
|
|
||||||
const ProjectRepository = {
|
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) =>
|
getProjectByprojectId: (projetid) =>
|
||||||
api.get(`/api/project/details/${projetid}`),
|
api.get(`/api/project/details/${projetid}`),
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user