Merge branch 'Purchase_Invoice_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Purchase_Invoice_Management
This commit is contained in:
commit
b62fc82a9c
95
src/components/DailyProgressRport/TaskReportFilterChips.jsx
Normal file
95
src/components/DailyProgressRport/TaskReportFilterChips.jsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||||
|
|
||||||
|
const TaskReportFilterChips = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||||
|
const data = filterData?.data || filterData || {};
|
||||||
|
|
||||||
|
const filterChips = useMemo(() => {
|
||||||
|
const chips = [];
|
||||||
|
|
||||||
|
const addGroup = (ids, list, label, key) => {
|
||||||
|
if (!ids || ids.length === 0) return;
|
||||||
|
|
||||||
|
const items = ids.map((id) => ({
|
||||||
|
id,
|
||||||
|
name: list?.find((i) => i.id === id)?.name || id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
chips.push({ key, label, items });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Building
|
||||||
|
addGroup(filter?.buildingIds, data?.buildings, "Building", "buildingIds");
|
||||||
|
|
||||||
|
// Floor
|
||||||
|
addGroup(filter?.floorIds, data?.floors, "Floor", "floorIds");
|
||||||
|
|
||||||
|
// Activities
|
||||||
|
addGroup(filter?.activityIds, data?.activities, "Activity", "activityIds");
|
||||||
|
|
||||||
|
// Date Range Chips
|
||||||
|
if (filter?.dateFrom || filter?.dateTo) {
|
||||||
|
chips.push({
|
||||||
|
key: "date",
|
||||||
|
label: "Date Range",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "date-range",
|
||||||
|
name: `${filter?.dateFrom ? formatUTCToLocalTime(filter.dateFrom) : ""}
|
||||||
|
${filter?.dateTo ? " to " + formatUTCToLocalTime(filter.dateTo) : ""}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return chips;
|
||||||
|
}, [filter, filterData]);
|
||||||
|
|
||||||
|
if (!filterChips.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-2 mt-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>
|
||||||
|
|
||||||
|
{/* If date chip → remove whole date range */}
|
||||||
|
{chipGroup.key === "date" ? (
|
||||||
|
<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={() => clearFilter()}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<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 TaskReportFilterChips;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||||
import { useCurrentService } from "../../hooks/useProjects";
|
import { useCurrentService } from "../../hooks/useProjects";
|
||||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
@ -11,8 +11,9 @@ import { DateRangePicker1 } from "../common/DateRangePicker";
|
|||||||
import SelectMultiple from "../common/SelectMultiple";
|
import SelectMultiple from "../common/SelectMultiple";
|
||||||
import { localToUtc } from "../../utils/appUtils";
|
import { localToUtc } from "../../utils/appUtils";
|
||||||
import { useTaskFilter } from "../../hooks/useTasks";
|
import { useTaskFilter } from "../../hooks/useTasks";
|
||||||
|
import { set } from "date-fns";
|
||||||
|
|
||||||
const TaskReportFilterPanel = ({ handleFilter }) => {
|
const TaskReportFilterPanel = forwardRef(({ handleFilter, setFilterdata, clearFilter }, ref) => {
|
||||||
const [resetKey, setResetKey] = useState(0);
|
const [resetKey, setResetKey] = useState(0);
|
||||||
const selectedProject = useSelectedProject();
|
const selectedProject = useSelectedProject();
|
||||||
const selectedService = useCurrentService();
|
const selectedService = useCurrentService();
|
||||||
@ -23,10 +24,42 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
|||||||
defaultValues: TaskReportDefaultValue,
|
defaultValues: TaskReportDefaultValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dynamicDefaultFilter = useMemo(() => {
|
||||||
|
return {
|
||||||
|
...TaskReportDefaultValue,
|
||||||
|
buildingIds: TaskReportDefaultValue.buildingIds || [],
|
||||||
|
floorIds: TaskReportDefaultValue.floorIds || [],
|
||||||
|
activityIds: TaskReportDefaultValue.activityIds || [],
|
||||||
|
dateFrom: TaskReportDefaultValue.dateFrom,
|
||||||
|
dateTo: TaskReportDefaultValue.dateTo,
|
||||||
|
};
|
||||||
|
}, [selectedProject]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
resetFieldValue: (name, value) => {
|
||||||
|
// Reset specific field
|
||||||
|
if (value !== undefined) {
|
||||||
|
setValue(name, value);
|
||||||
|
} else {
|
||||||
|
// Fix: Use TaskReportDefaultValue or get current values before setting new ones
|
||||||
|
// reset({ ...methods.getValues(), [name]: TaskReportDefaultValue[name] }); // Updated to use TaskReportDefaultValue
|
||||||
|
setValue(name, TaskReportDefaultValue[name]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getValues: methods.getValues,
|
||||||
|
onClear: onClear, // 💡 EXPOSE THE ONCLEAR FUNCTION
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && setFilterdata) {
|
||||||
|
setFilterdata(data);
|
||||||
|
}
|
||||||
|
}, [data, setFilterdata]);
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
reset,
|
reset,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = methods;
|
} = methods;
|
||||||
const closePanel = () => {
|
const closePanel = () => {
|
||||||
@ -52,7 +85,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
|||||||
<div className="mb-3 w-100">
|
<div className="mb-3 w-100">
|
||||||
<label className="fw-semibold">Choose Date Range:</label>
|
<label className="fw-semibold">Choose Date Range:</label>
|
||||||
<DateRangePicker1
|
<DateRangePicker1
|
||||||
className="w-100"
|
className="w-100"
|
||||||
placeholder="DD-MM-YYYY To DD-MM-YYYY"
|
placeholder="DD-MM-YYYY To DD-MM-YYYY"
|
||||||
startField="dateFrom"
|
startField="dateFrom"
|
||||||
endField="dateTo"
|
endField="dateTo"
|
||||||
@ -105,6 +138,6 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
|||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default TaskReportFilterPanel;
|
export default TaskReportFilterPanel;
|
||||||
|
|||||||
@ -17,8 +17,9 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
|||||||
import Pagination from "../common/Pagination";
|
import Pagination from "../common/Pagination";
|
||||||
import { TaskReportListSkeleton } from "./TaskRepprtListSkeleton";
|
import { TaskReportListSkeleton } from "./TaskRepprtListSkeleton";
|
||||||
import HoverPopup from "../common/HoverPopup";
|
import HoverPopup from "../common/HoverPopup";
|
||||||
|
import TaskReportFilterChips from "./TaskReportFilterChips";
|
||||||
|
|
||||||
const TaskReportList = () => {
|
const TaskReportList = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [filters, setFilters] = useState({
|
const [filters, setFilters] = useState({
|
||||||
selectedBuilding: "",
|
selectedBuilding: "",
|
||||||
@ -29,7 +30,7 @@ const TaskReportList = () => {
|
|||||||
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
|
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
|
||||||
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
|
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||||
|
|
||||||
const { service, openModal, closeModal, filter } = useDailyProgrssContext();
|
const { service, openModal, closeModal, filter: contextFilter } = useDailyProgrssContext();
|
||||||
const selectedProject = useSelectedProject();
|
const selectedProject = useSelectedProject();
|
||||||
const { projectNames } = useProjectName();
|
const { projectNames } = useProjectName();
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ const TaskReportList = () => {
|
|||||||
selectedProject,
|
selectedProject,
|
||||||
ITEMS_PER_PAGE,
|
ITEMS_PER_PAGE,
|
||||||
currentPage,
|
currentPage,
|
||||||
service, filter
|
service, contextFilter
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProgrssReportColumn = [
|
const ProgrssReportColumn = [
|
||||||
@ -193,124 +194,135 @@ const TaskReportList = () => {
|
|||||||
if (isError) return <div>Loading....</div>;
|
if (isError) return <div>Loading....</div>;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mt-2 table-responsive text-nowrap">
|
|
||||||
<table className="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th className="text-start">Activity</th>
|
|
||||||
<th>
|
|
||||||
<span>
|
|
||||||
Total Pending{" "}
|
|
||||||
<HoverPopup
|
|
||||||
id="total_pending_task"
|
|
||||||
title="Total Pending Task"
|
|
||||||
content={
|
|
||||||
<div className="text-wrap" style={{ minWidth: "200px" }}>
|
|
||||||
This shows the total pending tasks for each activity on that date.
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
|
||||||
</HoverPopup>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
|
|
||||||
<th>
|
<div className="main-content">
|
||||||
<span>
|
<div className="col-12 mb-2 mt-2 px-4">
|
||||||
Reported/Planned{" "}
|
<TaskReportFilterChips
|
||||||
<HoverPopup
|
filter={filter}
|
||||||
id="reportes_and_planned_task"
|
filterData={filterData}
|
||||||
title="Reported and Planned Task"
|
removeFilterChip={removeFilterChip}
|
||||||
content={
|
clearFilter={clearFilter}
|
||||||
<div className="text-wrap" style={{ maxWidth: "200px" }}>
|
/>
|
||||||
This shows the reported versus planned tasks for each activity on that date.
|
</div>
|
||||||
</div>
|
<div className="mt-2 table-responsive text-nowrap">
|
||||||
}
|
<table className="table">
|
||||||
>
|
<thead>
|
||||||
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
|
||||||
</HoverPopup>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
|
|
||||||
<th>Assign Date</th>
|
|
||||||
<th>Team</th>
|
|
||||||
<th className="text-center">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{groupedTasks.length === 0 && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={6} className="text-center align-middle" style={{ height: "200px", borderBottom: "none" }}>
|
<th className="text-start">Activity</th>
|
||||||
No reports available
|
<th>
|
||||||
</td>
|
<span>
|
||||||
</tr>
|
Total Pending{" "}
|
||||||
)}
|
<HoverPopup
|
||||||
|
id="total_pending_task"
|
||||||
|
title="Total Pending Task"
|
||||||
|
content={
|
||||||
|
<div className="text-wrap" style={{ minWidth: "200px" }}>
|
||||||
|
This shows the total pending tasks for each activity on that date.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
||||||
|
</HoverPopup>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
|
||||||
{groupedTasks.map(({ date, tasks }) => (
|
<th>
|
||||||
<React.Fragment key={date}>
|
<span>
|
||||||
<tr className="table-row-header text-start">
|
Reported/Planned{" "}
|
||||||
<td colSpan={6}>
|
<HoverPopup
|
||||||
<strong>{formatUTCToLocalTime(date)}</strong>
|
id="reportes_and_planned_task"
|
||||||
|
title="Reported and Planned Task"
|
||||||
|
content={
|
||||||
|
<div className="text-wrap" style={{ maxWidth: "200px" }}>
|
||||||
|
This shows the reported versus planned tasks for each activity on that date.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
||||||
|
</HoverPopup>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th>Assign Date</th>
|
||||||
|
<th>Team</th>
|
||||||
|
<th className="text-center">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{groupedTasks.length === 0 && (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={6} className="text-center align-middle" style={{ height: "200px", borderBottom: "none" }}>
|
||||||
|
No reports available
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{tasks.map((task, idx) => (
|
)}
|
||||||
<tr key={task.id || idx}>
|
|
||||||
<td className="flex-wrap text-start">
|
{groupedTasks.map(({ date, tasks }) => (
|
||||||
<div>
|
<React.Fragment key={date}>
|
||||||
{task.workItem.activityMaster?.activityName || "No Activity Name"}
|
<tr className="table-row-header text-start">
|
||||||
</div>
|
<td colSpan={6}>
|
||||||
<div className="text-sm py-2">
|
<strong>{formatUTCToLocalTime(date)}</strong>
|
||||||
{task.workItem.workArea?.floor?.building?.name} ›{" "}
|
|
||||||
{task.workItem.workArea?.floor?.floorName} ›{" "}
|
|
||||||
{task.workItem.workArea?.areaName}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{formatNumber(task.workItem.plannedWork)}
|
|
||||||
</td>
|
|
||||||
<td>{`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}</td>
|
|
||||||
<td>{formatUTCToLocalTime(task.assignmentDate)}</td>
|
|
||||||
<td className="text-center">{renderTeamMembers(task, idx)}</td>
|
|
||||||
<td className="text-center">
|
|
||||||
<div className="d-flex justify-content-end gap-2">
|
|
||||||
{ReportTaskRights && !task.reportedDate && (
|
|
||||||
<button className="btn btn-xs btn-primary" onClick={() => openModal("report", task)}>
|
|
||||||
Report
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{ApprovedTaskRights && task.reportedDate && !task.approvedBy && (
|
|
||||||
<button
|
|
||||||
className="btn btn-xs btn-warning"
|
|
||||||
onClick={() => openModal("comments", { task, isActionAllow: true })}
|
|
||||||
>
|
|
||||||
QC
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
onClick={() => openModal("comments", { task, isActionAllow: false })}
|
|
||||||
>
|
|
||||||
Comment
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
{tasks.map((task, idx) => (
|
||||||
</React.Fragment>
|
<tr key={task.id || idx}>
|
||||||
))}
|
<td className="flex-wrap text-start">
|
||||||
</tbody>
|
<div>
|
||||||
</table>
|
{task.workItem.activityMaster?.activityName || "No Activity Name"}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm py-2">
|
||||||
|
{task.workItem.workArea?.floor?.building?.name} ›{" "}
|
||||||
|
{task.workItem.workArea?.floor?.floorName} ›{" "}
|
||||||
|
{task.workItem.workArea?.areaName}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{formatNumber(task.workItem.plannedWork)}
|
||||||
|
</td>
|
||||||
|
<td>{`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}</td>
|
||||||
|
<td>{formatUTCToLocalTime(task.assignmentDate)}</td>
|
||||||
|
<td className="text-center">{renderTeamMembers(task, idx)}</td>
|
||||||
|
<td className="text-center">
|
||||||
|
<div className="d-flex justify-content-end gap-2">
|
||||||
|
{ReportTaskRights && !task.reportedDate && (
|
||||||
|
<button className="btn btn-xs btn-primary" onClick={() => openModal("report", task)}>
|
||||||
|
Report
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{ApprovedTaskRights && task.reportedDate && !task.approvedBy && (
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-warning"
|
||||||
|
onClick={() => openModal("comments", { task, isActionAllow: true })}
|
||||||
|
>
|
||||||
|
QC
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-primary"
|
||||||
|
onClick={() => openModal("comments", { task, isActionAllow: false })}
|
||||||
|
>
|
||||||
|
Comment
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
data?.data?.length > 0 && (
|
data?.data?.length > 0 && (
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalPages={data.totalPages}
|
totalPages={data.totalPages}
|
||||||
onPageChange={paginate}
|
onPageChange={paginate}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
</div >
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -59,12 +59,18 @@ const DocumentFilterPanel = forwardRef(
|
|||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
resetFieldValue: (name, value) => {
|
resetFieldValue: (name, value) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
setValue(name, value);
|
setValue(name, value, { shouldValidate: true, shouldDirty: true });
|
||||||
} else {
|
} else {
|
||||||
reset({ ...methods.getValues(), [name]: DocumentFilterDefaultValues[name] });
|
reset({ ...methods.getValues(), [name]: DocumentFilterDefaultValues[name] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((name === "startDate" || name === "endDate") && value === null) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setResetKey((prev) => prev + 1);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getValues: methods.getValues, // optional, to read current filter state
|
getValues: methods.getValues,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//changes
|
//changes
|
||||||
@ -141,6 +147,7 @@ const DocumentFilterPanel = forwardRef(
|
|||||||
defaultRange={false}
|
defaultRange={false}
|
||||||
resetSignal={resetKey}
|
resetSignal={resetKey}
|
||||||
maxDate={new Date()}
|
maxDate={new Date()}
|
||||||
|
className="w-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -124,26 +124,27 @@ const Documents = ({ Document_Entity, Entity }) => {
|
|||||||
const updatedFilters = { ...filters };
|
const updatedFilters = { ...filters };
|
||||||
if (Array.isArray(updatedFilters[key])) {
|
if (Array.isArray(updatedFilters[key])) {
|
||||||
updatedFilters[key] = updatedFilters[key].filter((v) => v !== id);
|
updatedFilters[key] = updatedFilters[key].filter((v) => v !== id);
|
||||||
updatedRef.current?.resetFieldValue(key,updatedFilters[key]);
|
updatedRef.current?.resetFieldValue(key, updatedFilters[key]);
|
||||||
}
|
}
|
||||||
else if (key === "dateRange") {
|
else if (key === "dateRange") {
|
||||||
updatedFilters.startDate = null;
|
updatedFilters.startDate = null;
|
||||||
updatedFilters.endDate = null;
|
updatedFilters.endDate = null;
|
||||||
updatedRef.current?.resetFieldValue("startDate",null);
|
// These calls correctly tell the DocumentFilterPanel to update its form state:
|
||||||
updatedRef.current?.resetFieldValue("endDate",null);
|
updatedRef.current?.resetFieldValue("startDate", null);
|
||||||
|
updatedRef.current?.resetFieldValue("endDate", null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
updatedFilters[key] = null;
|
updatedFilters[key] = null;
|
||||||
}
|
}
|
||||||
setFilter(updatedFilters);
|
setFilter(updatedFilters);
|
||||||
return updatedFilters;
|
return updatedFilters;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DocumentContext.Provider value={contextValues}>
|
<DocumentContext.Provider value={contextValues}>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<div className="card page-min-h d-flex p-5">
|
<div className="card page-min-h d-flex p-5">
|
||||||
<DocumentFilterChips filters={filters} filterData={filterData} removeFilterChip={removeFilterChip} />
|
|
||||||
<div className="row align-items-center">
|
<div className="row align-items-center">
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="d-flex flex-row gap-2 col-12 col-md-8 col-lg-4 mb-md-0 align-items-center mb-2">
|
<div className="d-flex flex-row gap-2 col-12 col-md-8 col-lg-4 mb-md-0 align-items-center mb-2">
|
||||||
@ -194,6 +195,9 @@ const Documents = ({ Document_Entity, Entity }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="ms-n1">
|
||||||
|
<DocumentFilterChips filters={filters} filterData={filterData} removeFilterChip={removeFilterChip} />
|
||||||
|
</div>
|
||||||
<DocumentsList
|
<DocumentsList
|
||||||
Document_Entity={DocumentEntity}
|
Document_Entity={DocumentEntity}
|
||||||
Entity={Entity}
|
Entity={Entity}
|
||||||
|
|||||||
@ -127,7 +127,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
|||||||
const hasAccessAplication = watch("hasApplicationAccess");
|
const hasAccessAplication = watch("hasApplicationAccess");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="p-sm-0 p-2">
|
<form onSubmit={handleSubmit(onSubmit)} className="p-sm-2 p-2">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="fs-5 fw-semibold">
|
<p className="fs-5 fw-semibold">
|
||||||
{" "}
|
{" "}
|
||||||
|
|||||||
@ -94,6 +94,14 @@ const ExpenseFilterPanel = forwardRef(
|
|||||||
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
|
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// --- START FIX: Add resetDateRange method ---
|
||||||
|
resetDateRange: () => {
|
||||||
|
setValue("startDate", null);
|
||||||
|
setValue("endDate", null);
|
||||||
|
// Trigger re-render/reset of the DateRangePicker component
|
||||||
|
setResetKey((prev) => prev + 1);
|
||||||
|
},
|
||||||
|
// --- END FIX ---
|
||||||
getValues: methods.getValues, // optional, to read current filter state
|
getValues: methods.getValues, // optional, to read current filter state
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -137,13 +145,13 @@ const ExpenseFilterPanel = forwardRef(
|
|||||||
: dynamicDefaultFilter.projectIds || [],
|
: dynamicDefaultFilter.projectIds || [],
|
||||||
startDate: dynamicDefaultFilter.startDate
|
startDate: dynamicDefaultFilter.startDate
|
||||||
? moment
|
? moment
|
||||||
.utc(dynamicDefaultFilter.startDate, "DD-MM-YYYY")
|
.utc(dynamicDefaultFilter.startDate, "DD-MM-YYYY")
|
||||||
.toISOString()
|
.toISOString()
|
||||||
: undefined,
|
: undefined,
|
||||||
endDate: dynamicDefaultFilter.endDate
|
endDate: dynamicDefaultFilter.endDate
|
||||||
? moment
|
? moment
|
||||||
.utc(dynamicDefaultFilter.endDate, "DD-MM-YYYY")
|
.utc(dynamicDefaultFilter.endDate, "DD-MM-YYYY")
|
||||||
.toISOString()
|
.toISOString()
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,18 +184,16 @@ const ExpenseFilterPanel = forwardRef(
|
|||||||
<div className="d-inline-flex border rounded-pill mb-1 overflow-hidden shadow-none">
|
<div className="d-inline-flex border rounded-pill mb-1 overflow-hidden shadow-none">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn px-2 py-1 rounded-0 text-tiny ${
|
className={`btn px-2 py-1 rounded-0 text-tiny ${isTransactionDate ? "active btn-primary text-white" : ""
|
||||||
isTransactionDate ? "active btn-primary text-white" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setValue("isTransactionDate", true)}
|
onClick={() => setValue("isTransactionDate", true)}
|
||||||
>
|
>
|
||||||
Transaction Date
|
Transaction Date
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn px-2 py-1 rounded-0 text-tiny ${
|
className={`btn px-2 py-1 rounded-0 text-tiny ${!isTransactionDate ? "active btn-primary text-white" : ""
|
||||||
!isTransactionDate ? "active btn-primary text-white" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setValue("isTransactionDate", false)}
|
onClick={() => setValue("isTransactionDate", false)}
|
||||||
>
|
>
|
||||||
Submitted Date
|
Submitted Date
|
||||||
|
|||||||
@ -83,64 +83,64 @@ const ExpenseList = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const groupByField = (items, field) => {
|
const groupByField = (items, field) => {
|
||||||
if (!field || field === "none") {
|
if (!field || field === "none") {
|
||||||
return {
|
return {
|
||||||
All: {
|
All: {
|
||||||
key: "All",
|
key: "All",
|
||||||
displayField: "All",
|
displayField: "All",
|
||||||
items: items || []
|
items: items || []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.reduce((acc, item) => {
|
||||||
|
let key;
|
||||||
|
let displayField;
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case "transactionDate":
|
||||||
|
key = formatUTCToLocalTime(item?.transactionDate);
|
||||||
|
displayField = "Transaction Date";
|
||||||
|
break;
|
||||||
|
case "status":
|
||||||
|
key = item?.status?.displayName || "Unknown";
|
||||||
|
displayField = "Status";
|
||||||
|
break;
|
||||||
|
case "submittedBy":
|
||||||
|
key = `${item?.createdBy?.firstName ?? ""} ${item?.createdBy?.lastName ?? ""}`.trim();
|
||||||
|
displayField = "Submitted By";
|
||||||
|
break;
|
||||||
|
case "project":
|
||||||
|
key = item?.project?.name || "Unknown Project";
|
||||||
|
displayField = "Project";
|
||||||
|
break;
|
||||||
|
case "paymentMode":
|
||||||
|
key = item?.paymentMode?.name || "Unknown Mode";
|
||||||
|
displayField = "Payment Mode";
|
||||||
|
break;
|
||||||
|
case "expenseCategory":
|
||||||
|
key = item?.expenseCategory?.name || "Unknown Type";
|
||||||
|
displayField = "Expense Category";
|
||||||
|
break;
|
||||||
|
case "createdAt":
|
||||||
|
key = item?.createdAt?.split("T")[0] || "Unknown Date";
|
||||||
|
displayField = "Created Date";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
key = "Others";
|
||||||
|
displayField = "Others";
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.reduce((acc, item) => {
|
const groupKey = `${field}_${key}`;
|
||||||
let key;
|
if (!acc[groupKey]) {
|
||||||
let displayField;
|
acc[groupKey] = { key, displayField, items: [] };
|
||||||
|
}
|
||||||
|
|
||||||
switch (field) {
|
acc[groupKey].items.push(item);
|
||||||
case "transactionDate":
|
return acc;
|
||||||
key = formatUTCToLocalTime(item?.transactionDate);
|
}, {});
|
||||||
displayField = "Transaction Date";
|
};
|
||||||
break;
|
|
||||||
case "status":
|
|
||||||
key = item?.status?.displayName || "Unknown";
|
|
||||||
displayField = "Status";
|
|
||||||
break;
|
|
||||||
case "submittedBy":
|
|
||||||
key = `${item?.createdBy?.firstName ?? ""} ${item?.createdBy?.lastName ?? ""}`.trim();
|
|
||||||
displayField = "Submitted By";
|
|
||||||
break;
|
|
||||||
case "project":
|
|
||||||
key = item?.project?.name || "Unknown Project";
|
|
||||||
displayField = "Project";
|
|
||||||
break;
|
|
||||||
case "paymentMode":
|
|
||||||
key = item?.paymentMode?.name || "Unknown Mode";
|
|
||||||
displayField = "Payment Mode";
|
|
||||||
break;
|
|
||||||
case "expenseCategory":
|
|
||||||
key = item?.expenseCategory?.name || "Unknown Type";
|
|
||||||
displayField = "Expense Category";
|
|
||||||
break;
|
|
||||||
case "createdAt":
|
|
||||||
key = item?.createdAt?.split("T")[0] || "Unknown Date";
|
|
||||||
displayField = "Created Date";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
key = "Others";
|
|
||||||
displayField = "Others";
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupKey = `${field}_${key}`;
|
|
||||||
if (!acc[groupKey]) {
|
|
||||||
acc[groupKey] = { key, displayField, items: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
acc[groupKey].items.push(item);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const expenseColumns = [
|
const expenseColumns = [
|
||||||
@ -167,9 +167,8 @@ const groupByField = (items, field) => {
|
|||||||
label: "Submitted By",
|
label: "Submitted By",
|
||||||
align: "text-start",
|
align: "text-start",
|
||||||
getValue: (e) =>
|
getValue: (e) =>
|
||||||
`${e.createdBy?.firstName ?? ""} ${
|
`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
||||||
e.createdBy?.lastName ?? ""
|
}`.trim() || "N/A",
|
||||||
}`.trim() || "N/A",
|
|
||||||
customRender: (e) => (
|
customRender: (e) => (
|
||||||
<div
|
<div
|
||||||
className="d-flex align-items-center cursor-pointer"
|
className="d-flex align-items-center cursor-pointer"
|
||||||
@ -182,9 +181,8 @@ const groupByField = (items, field) => {
|
|||||||
lastName={e.createdBy?.lastName}
|
lastName={e.createdBy?.lastName}
|
||||||
/>
|
/>
|
||||||
<span className="text-truncate">
|
<span className="text-truncate">
|
||||||
{`${e.createdBy?.firstName ?? ""} ${
|
{`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
||||||
e.createdBy?.lastName ?? ""
|
}`.trim() || "N/A"}
|
||||||
}`.trim() || "N/A"}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -216,9 +214,8 @@ const groupByField = (items, field) => {
|
|||||||
align: "text-center",
|
align: "text-center",
|
||||||
getValue: (e) => (
|
getValue: (e) => (
|
||||||
<span
|
<span
|
||||||
className={`badge bg-label-${
|
className={`badge bg-label-${getColorNameFromHex(e?.status?.color) || "secondary"
|
||||||
getColorNameFromHex(e?.status?.color) || "secondary"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{e.status?.name || "Unknown"}
|
{e.status?.name || "Unknown"}
|
||||||
</span>
|
</span>
|
||||||
@ -238,16 +235,16 @@ const groupByField = (items, field) => {
|
|||||||
return <ExpenseTableSkeleton headers={headers} />;
|
return <ExpenseTableSkeleton headers={headers} />;
|
||||||
if (isError) return <div>{error?.message}</div>;
|
if (isError) return <div>{error?.message}</div>;
|
||||||
|
|
||||||
const isNoGrouping = !groupBy || groupBy === "none";
|
const isNoGrouping = !groupBy || groupBy === "none";
|
||||||
const grouped = isNoGrouping
|
const grouped = isNoGrouping
|
||||||
? { All: { key: "All", displayField: "All", items: data?.data ?? [] } }
|
? { All: { key: "All", displayField: "All", items: data?.data ?? [] } }
|
||||||
: groupByField(data?.data ?? [], groupBy);
|
: groupByField(data?.data ?? [], groupBy);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const IsGroupedByDate = [
|
const IsGroupedByDate = [
|
||||||
{key:"none",displayField:"None"},
|
{ key: "none", displayField: "None" },
|
||||||
{ key: "transactionDate", displayField: "Transaction Date" },
|
{ key: "transactionDate", displayField: "Transaction Date" },
|
||||||
{ key: "createdAt", displayField: "created Date", },
|
{ key: "createdAt", displayField: "created Date", },
|
||||||
]?.includes(groupBy);
|
]?.includes(groupBy);
|
||||||
@ -343,26 +340,22 @@ const grouped = isNoGrouping
|
|||||||
(col.isAlwaysVisible || groupBy !== col.key) && (
|
(col.isAlwaysVisible || groupBy !== col.key) && (
|
||||||
<td
|
<td
|
||||||
key={col.key}
|
key={col.key}
|
||||||
className={`d-table-cell ml-2 ${
|
className={`d-table-cell ml-2 ${col.align ?? ""
|
||||||
col.align ?? ""
|
} `}
|
||||||
} `}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`d-flex px-2 ${
|
className={`d-flex px-2 ${col.key === "status"
|
||||||
col.key === "status"
|
|
||||||
? "justify-content-center"
|
? "justify-content-center"
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
${
|
${col.key === "amount"
|
||||||
col.key === "amount"
|
? "justify-content-end"
|
||||||
? "justify-content-end"
|
: ""
|
||||||
: ""
|
}
|
||||||
}
|
${col.key === "submitted"
|
||||||
${
|
? "justify-content-center"
|
||||||
col.key === "submitted"
|
: ""
|
||||||
? "justify-content-center"
|
}
|
||||||
: ""
|
|
||||||
}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{col.customRender
|
{col.customRender
|
||||||
@ -445,7 +438,18 @@ const grouped = isNoGrouping
|
|||||||
<tr>
|
<tr>
|
||||||
<td colSpan={8} className="text-center border-0 ">
|
<td colSpan={8} className="text-center border-0 ">
|
||||||
<div className="py-8">
|
<div className="py-8">
|
||||||
<p>No Expense Found</p>
|
<p
|
||||||
|
className="text-center"
|
||||||
|
style={{
|
||||||
|
height: "250px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No Expense found
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import moment from "moment";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clearFilter }) => {
|
const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clearFilter }) => {
|
||||||
@ -22,6 +23,21 @@ const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clea
|
|||||||
addGroup(filters.projectIds, data.projects, "Projects", "projectIds");
|
addGroup(filters.projectIds, data.projects, "Projects", "projectIds");
|
||||||
addGroup(filters.statusIds, data.status, "Status", "statusIds");
|
addGroup(filters.statusIds, data.status, "Status", "statusIds");
|
||||||
|
|
||||||
|
if (filters.startDate || filters.endDate) {
|
||||||
|
const start = filters.startDate ? moment(filters.startDate).format("DD MMM YYYY") : "";
|
||||||
|
const end = filters.endDate ? moment(filters.endDate).format("DD MMM YYYY") : "";
|
||||||
|
chips.push({
|
||||||
|
key: "dateRange",
|
||||||
|
label: "Date",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "dateRange",
|
||||||
|
name: start && end ? `${start} to ${end}` : start || end,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return chips;
|
return chips;
|
||||||
}, [filters, filterData]);
|
}, [filters, filterData]);
|
||||||
|
|
||||||
|
|||||||
@ -76,9 +76,18 @@ const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilte
|
|||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
setValue(name, value);
|
setValue(name, value);
|
||||||
} else {
|
} else {
|
||||||
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
|
// NOTE: Using defaultPaymentRequestFilter for clear functionality
|
||||||
|
reset({ ...methods.getValues(), [name]: defaultPaymentRequestFilter[name] });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// --- START FIX: Add resetDateRange method ---
|
||||||
|
resetDateRange: () => {
|
||||||
|
setValue("startDate", null);
|
||||||
|
setValue("endDate", null);
|
||||||
|
// Trigger re-render/reset of the DateRangePicker component
|
||||||
|
setResetKey((prev) => prev + 1);
|
||||||
|
},
|
||||||
|
// --- END FIX ---
|
||||||
getValues: methods.getValues, // optional, to read current filter state
|
getValues: methods.getValues, // optional, to read current filter state
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -94,7 +103,7 @@ const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilte
|
|||||||
startDate: moment.utc(formData.startDate, "DD-MM-YYYY").toISOString(),
|
startDate: moment.utc(formData.startDate, "DD-MM-YYYY").toISOString(),
|
||||||
endDate: moment.utc(formData.endDate, "DD-MM-YYYY").toISOString(),
|
endDate: moment.utc(formData.endDate, "DD-MM-YYYY").toISOString(),
|
||||||
});
|
});
|
||||||
handleGroupBy(selectedGroup.id);
|
// handleGroupBy(selectedGroup.id);
|
||||||
// closePanel();
|
// closePanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -355,8 +355,18 @@ const PaymentRequestList = ({ filters, filterData, removeFilterChip, clearFilter
|
|||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={8} className="text-center border-0 ">
|
<td colSpan={8} className="text-center border-0 ">
|
||||||
<div className="py-8">
|
<div >
|
||||||
<p>No Request Found</p>
|
<p
|
||||||
|
className="text-center"
|
||||||
|
style={{
|
||||||
|
height: "250px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No data found
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -105,7 +105,7 @@ const ProjectCard = ({ project, isCore = true }) => {
|
|||||||
>
|
>
|
||||||
{project?.shortName ? project?.shortName : project?.name}
|
{project?.shortName ? project?.shortName : project?.name}
|
||||||
</h5>
|
</h5>
|
||||||
<div className="client-info text-body">
|
<div className="client-info text-body text-start">
|
||||||
<span>{project?.shortName ? project?.name : ""}</span>
|
<span>{project?.shortName ? project?.name : ""}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
100
src/components/Tenant/TenantFilterChips.jsx
Normal file
100
src/components/Tenant/TenantFilterChips.jsx
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { TENANT_STATUS, reference } from "../../utils/constants";
|
||||||
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||||
|
|
||||||
|
const TenantFilterChips = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||||
|
const data = filterData || {};
|
||||||
|
|
||||||
|
const filterChips = useMemo(() => {
|
||||||
|
const chips = [];
|
||||||
|
|
||||||
|
const addGroup = (ids, list, label, key, valueKey = "id") => {
|
||||||
|
if (!ids || ids.length === 0) return;
|
||||||
|
|
||||||
|
const items = ids.map((id) => {
|
||||||
|
const found = list?.find((i) => i[valueKey] === id);
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: found?.name || id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
chips.push({ key, label, items });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Industries
|
||||||
|
addGroup(filter?.industryIds, data, "Industries", "industryIds");
|
||||||
|
|
||||||
|
// References
|
||||||
|
addGroup(filter?.references, reference, "References", "references", "val");
|
||||||
|
|
||||||
|
// Tenant Status
|
||||||
|
addGroup(filter?.tenantStatusIds, TENANT_STATUS, "Status", "tenantStatusIds");
|
||||||
|
|
||||||
|
// Date Range Chip
|
||||||
|
if (filter?.startDate || filter?.endDate) {
|
||||||
|
chips.push({
|
||||||
|
key: "date",
|
||||||
|
label: "Date Range",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "date-range",
|
||||||
|
name: `${filter?.startDate ? formatUTCToLocalTime(filter.startDate) : ""}${
|
||||||
|
filter?.endDate ? " to " + formatUTCToLocalTime(filter.endDate) : ""
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return chips;
|
||||||
|
}, [filter, filterData]);
|
||||||
|
|
||||||
|
if (!filterChips.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-2 mt-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>
|
||||||
|
|
||||||
|
{/* Remove Date Range → remove full filter */}
|
||||||
|
{chipGroup.key === "date" ? (
|
||||||
|
<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={clearFilter}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<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 TenantFilterChips;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import React, { useState, useCallback, useEffect } from "react";
|
import React, { useState, useCallback, useEffect, useImperativeHandle, forwardRef, useMemo } from "react";
|
||||||
import { FormProvider, useForm, useFormContext } from "react-hook-form";
|
import { FormProvider, useForm, useFormContext } from "react-hook-form";
|
||||||
import { defaultFilterValues, filterSchema } from "./TenantSchema";
|
import { defaultFilterValues, filterSchema } from "./TenantSchema";
|
||||||
import Label from "../common/Label";
|
import Label from "../common/Label";
|
||||||
@ -10,16 +10,44 @@ import { DateRangePicker1 } from "../common/DateRangePicker";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const TenantFilterPanel = ({ onApply }) => {
|
const TenantFilterPanel = forwardRef(({ onApply, setFilterdata, clearFilter }, ref) => {
|
||||||
const [resetKey, setResetKey] = useState(0);
|
const [resetKey, setResetKey] = useState(0);
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: defaultFilterValues,
|
defaultValues: defaultFilterValues,
|
||||||
});
|
});
|
||||||
|
const { handleSubmit, reset, setValue } = methods;
|
||||||
|
const { data: industries, isLoading } = useIndustries();
|
||||||
|
|
||||||
|
const dynamicDefaultFilter = useMemo(() => {
|
||||||
|
return {
|
||||||
|
...defaultFilterValues,
|
||||||
|
industryIds: defaultFilterValues.industryIds || [],
|
||||||
|
tenantStatusIds: defaultFilterValues.tenantStatusIds || [],
|
||||||
|
references: defaultFilterValues.references || [],
|
||||||
|
startDate: defaultFilterValues.startDate,
|
||||||
|
endDate: defaultFilterValues.endDate,
|
||||||
|
};
|
||||||
|
}, [defaultFilterValues]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
resetFieldValue: (name, value) => {
|
||||||
|
setValue(name, value);
|
||||||
|
},
|
||||||
|
resetFields: () => {
|
||||||
|
reset(defaultFilterValues);
|
||||||
|
setResetKey(prev => prev + 1); // reset date picker
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (industries && setFilterdata) {
|
||||||
|
setFilterdata(industries);
|
||||||
|
}
|
||||||
|
}, [industries, setFilterdata]);
|
||||||
|
|
||||||
const { handleSubmit, reset } = methods;
|
|
||||||
const { data: industries = [], isLoading } = useIndustries();
|
|
||||||
|
|
||||||
const handleClosePanel = useCallback(() => {
|
const handleClosePanel = useCallback(() => {
|
||||||
document.querySelector(".offcanvas.show .btn-close")?.click();
|
document.querySelector(".offcanvas.show .btn-close")?.click();
|
||||||
@ -32,9 +60,8 @@ const TenantFilterPanel = ({ onApply }) => {
|
|||||||
startDate: moment.utc(formData.startDate, "DD-MM-YYYY").toISOString(),
|
startDate: moment.utc(formData.startDate, "DD-MM-YYYY").toISOString(),
|
||||||
endDate: moment.utc(formData.endDate, "DD-MM-YYYY").toISOString(),
|
endDate: moment.utc(formData.endDate, "DD-MM-YYYY").toISOString(),
|
||||||
});
|
});
|
||||||
handleClosePanel();
|
|
||||||
},
|
},
|
||||||
[onApply, handleClosePanel]
|
[onApply]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -65,6 +92,7 @@ const TenantFilterPanel = ({ onApply }) => {
|
|||||||
endField="endDate"
|
endField="endDate"
|
||||||
resetSignal={resetKey}
|
resetSignal={resetKey}
|
||||||
defaultRange={false}
|
defaultRange={false}
|
||||||
|
className="w-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-strat mb-2">
|
<div className="text-strat mb-2">
|
||||||
@ -119,6 +147,6 @@ const TenantFilterPanel = ({ onApply }) => {
|
|||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default TenantFilterPanel;
|
export default TenantFilterPanel;
|
||||||
|
|||||||
@ -7,12 +7,14 @@ import Pagination from "../common/Pagination";
|
|||||||
import { TenantTableSkeleton } from "./TenanatSkeleton";
|
import { TenantTableSkeleton } from "./TenanatSkeleton";
|
||||||
import { useTenantContext } from "../../pages/Tenant/TenantPage";
|
import { useTenantContext } from "../../pages/Tenant/TenantPage";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import TenantFilterChips from "./TenantFilterChips";
|
||||||
|
|
||||||
const TenantsList = ({
|
const TenantsList = ({
|
||||||
filters,
|
filters,
|
||||||
searchText,
|
searchText,
|
||||||
setIsRefetching,
|
setIsRefetching,
|
||||||
setRefetchFn,
|
setRefetchFn,
|
||||||
|
filterData, removeFilterChip, clearFilter
|
||||||
}) => {
|
}) => {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -135,54 +137,65 @@ const TenantsList = ({
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className=" mt-3">
|
<div className="main-content">
|
||||||
<div className=" text-nowrap table-responsive">
|
<div className="col-12 mb-2 mt-2 px-4">
|
||||||
<table className="table border-top dataTable text-nowrap">
|
<TenantFilterChips
|
||||||
<thead>
|
filter={filters}
|
||||||
<tr className="shadow-sm">
|
filterData={filterData}
|
||||||
{TenantColumns.map((col) => (
|
removeFilterChip={removeFilterChip}
|
||||||
<th key={col.key} className="sorting d-table-cell">
|
clearFilter={clearFilter}
|
||||||
<div className={col.align}>{col.label}</div>
|
/>
|
||||||
</th>
|
|
||||||
))}
|
</div>
|
||||||
</tr>
|
<div className=" mt-3">
|
||||||
</thead>
|
<div className=" text-nowrap table-responsive">
|
||||||
<tbody>
|
<table className="table border-top dataTable text-nowrap">
|
||||||
{data?.data.length > 0 ? (
|
<thead>
|
||||||
data.data.map((tenant) => (
|
<tr className="shadow-sm">
|
||||||
<tr key={tenant.id} style={{ height: "50px" }}>
|
{TenantColumns.map((col) => (
|
||||||
{TenantColumns.map((col) => (
|
<th key={col.key} className="sorting d-table-cell">
|
||||||
<td
|
<div className={col.align}>{col.label}</div>
|
||||||
key={col.key}
|
</th>
|
||||||
className={`d-table-cell px-3 py-2 align-middle ${col.align ?? ""
|
))}
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{col.customRender
|
|
||||||
? col.customRender(tenant)
|
|
||||||
: col.getValue(tenant)}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
colSpan={TenantColumns.length + 1}
|
|
||||||
className="text-center py-4 border-0"
|
|
||||||
>
|
|
||||||
No Tenants Found
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{data?.data.length > 0 ? (
|
||||||
{data?.data?.length > 0 && (
|
data.data.map((tenant) => (
|
||||||
<Pagination
|
<tr key={tenant.id} style={{ height: "50px" }}>
|
||||||
currentPage={currentPage}
|
{TenantColumns.map((col) => (
|
||||||
totalPages={data.totalPages}
|
<td
|
||||||
onPageChange={paginate}
|
key={col.key}
|
||||||
/>
|
className={`d-table-cell px-3 py-2 align-middle ${col.align ?? ""
|
||||||
)}
|
}`}
|
||||||
|
>
|
||||||
|
{col.customRender
|
||||||
|
? col.customRender(tenant)
|
||||||
|
: col.getValue(tenant)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
colSpan={TenantColumns.length + 1}
|
||||||
|
className="text-center py-4 border-0"
|
||||||
|
>
|
||||||
|
No Tenants Found
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{data?.data?.length > 0 && (
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
totalPages={data.totalPages}
|
||||||
|
onPageChange={paginate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
|
||||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||||
import { useServices } from "../../hooks/masterHook/useMaster";
|
import { useServices } from "../../hooks/masterHook/useMaster";
|
||||||
import TaskReportList from "../../components/DailyProgressRport/TaskReportList";
|
import TaskReportList from "../../components/DailyProgressRport/TaskReportList";
|
||||||
@ -13,6 +13,7 @@ import { useSelectedProject } from "../../slices/apiDataManager";
|
|||||||
import SelectField from "../../components/common/Forms/SelectField";
|
import SelectField from "../../components/common/Forms/SelectField";
|
||||||
import { AppFormController } from "../../hooks/appHooks/useAppForm";
|
import { AppFormController } from "../../hooks/appHooks/useAppForm";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { TaskReportDefaultValue } from "../../components/DailyProgressRport/TaskRportScheam";
|
||||||
|
|
||||||
const DailyProgrssContext = createContext();
|
const DailyProgrssContext = createContext();
|
||||||
export const useDailyProgrssContext = () => {
|
export const useDailyProgrssContext = () => {
|
||||||
@ -30,8 +31,9 @@ const DailyProgrssReport = () => {
|
|||||||
const [service, setService] = useState("");
|
const [service, setService] = useState("");
|
||||||
const [filter, setFilter] = useState('')
|
const [filter, setFilter] = useState('')
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
const updatedRef = useRef();
|
||||||
const { data, isLoading, isError, error } = useProjectAssignedServices(selectedProject);
|
const { data, isLoading, isError, error } = useProjectAssignedServices(selectedProject);
|
||||||
|
const [filterData, setFilterdata] = useState(null);
|
||||||
const [modal, setModal] = useState({ type: null, data: null });
|
const [modal, setModal] = useState({ type: null, data: null });
|
||||||
|
|
||||||
const openModal = (type, data = null) => setModal({ type, data });
|
const openModal = (type, data = null) => setModal({ type, data });
|
||||||
@ -49,20 +51,40 @@ const DailyProgrssReport = () => {
|
|||||||
serviceFilter: ""
|
serviceFilter: ""
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const clearFilter = () => {
|
||||||
|
updatedRef.current?.onClear();
|
||||||
|
};
|
||||||
const handleFilter = (filterObj) => {
|
const handleFilter = (filterObj) => {
|
||||||
setFilter(filterObj)
|
setFilter(filterObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
setOffcanvasContent("Report Filter", <TaskReportFilterPanel handleFilter={handleFilter} />);
|
setOffcanvasContent("Report Filter", <TaskReportFilterPanel handleFilter={handleFilter} ref={updatedRef}
|
||||||
|
clearFilter={clearFilter}
|
||||||
|
setFilterdata={setFilterdata} />);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setShowTrigger(false);
|
setShowTrigger(false);
|
||||||
setOffcanvasContent("", null);
|
setOffcanvasContent("", null);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleRemoveChip = (key, id) => {
|
||||||
|
setFilter((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 (
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<DailyProgrssContext.Provider value={contextDispatcher}>
|
<DailyProgrssContext.Provider value={contextDispatcher}>
|
||||||
@ -97,7 +119,7 @@ const DailyProgrssReport = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="card card-fullscreen p-5">
|
<div className="card page-min-h p-5">
|
||||||
{data?.length > 0 && (
|
{data?.length > 0 && (
|
||||||
<div className="col-sm-4 col-md-3 col-12 text-start">
|
<div className="col-sm-4 col-md-3 col-12 text-start">
|
||||||
<AppFormController
|
<AppFormController
|
||||||
@ -107,15 +129,15 @@ const DailyProgrssReport = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<SelectField
|
<SelectField
|
||||||
label="Services"
|
label="Services"
|
||||||
options={[{ id: "", name: "All Projects" }, ...(data ?? [])]}
|
options={[{ id: "", name: "All Projects" }, ...(data ?? [])]}
|
||||||
placeholder="Select Service"
|
placeholder="Select Service"
|
||||||
labelKey="name"
|
labelKey="name"
|
||||||
valueKey="id"
|
valueKey="id"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
field.onChange(val);
|
field.onChange(val);
|
||||||
setService(val);
|
setService(val);
|
||||||
}}
|
}}
|
||||||
className="m-0"
|
className="m-0"
|
||||||
/>
|
/>
|
||||||
@ -125,7 +147,10 @@ const DailyProgrssReport = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TaskReportList />
|
<TaskReportList filter={filter}
|
||||||
|
filterData={filterData}
|
||||||
|
removeFilterChip={handleRemoveChip}
|
||||||
|
clearFilter={clearFilter} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -106,10 +106,13 @@ const ExpensePage = () => {
|
|||||||
updated[key] = updated[key].filter((v) => v !== id);
|
updated[key] = updated[key].filter((v) => v !== id);
|
||||||
filterPanelRef.current?.resetFieldValue(key, updated[key]);
|
filterPanelRef.current?.resetFieldValue(key, updated[key]);
|
||||||
} else if (key === "dateRange") {
|
} else if (key === "dateRange") {
|
||||||
|
// --- START FIX: Use a dedicated function to reset the date range ---
|
||||||
updated.startDate = null;
|
updated.startDate = null;
|
||||||
updated.endDate = null;
|
updated.endDate = null;
|
||||||
filterPanelRef.current?.resetFieldValue("startDate", null);
|
|
||||||
filterPanelRef.current?.resetFieldValue("endDate", null);
|
// Call the new resetDateRange method on the ref
|
||||||
|
filterPanelRef.current?.resetDateRange();
|
||||||
|
// --- END FIX ---
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -71,6 +71,11 @@ const PaymentRequestPage = () => {
|
|||||||
if (Array.isArray(updated[key])) {
|
if (Array.isArray(updated[key])) {
|
||||||
updated[key] = updated[key].filter((v) => v !== id);
|
updated[key] = updated[key].filter((v) => v !== id);
|
||||||
setTimeout(() => updatedRef.current?.resetFieldValue(key, updated[key]), 0);
|
setTimeout(() => updatedRef.current?.resetFieldValue(key, updated[key]), 0);
|
||||||
|
} else if (key === "dateRange") { // Handle date range reset
|
||||||
|
updated.startDate = null;
|
||||||
|
updated.endDate = null;
|
||||||
|
// Call the new resetDateRange method on the ref
|
||||||
|
updatedRef.current?.resetDateRange();
|
||||||
} else {
|
} else {
|
||||||
updated[key] = null;
|
updated[key] = null;
|
||||||
setTimeout(() => updatedRef.current?.resetFieldValue(key, null), 0);
|
setTimeout(() => updatedRef.current?.resetFieldValue(key, null), 0);
|
||||||
@ -80,9 +85,9 @@ const PaymentRequestPage = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExport = (type) => {
|
const handleExport = (type) => {
|
||||||
HandlePaymentRequestExport(type, filters, search, tableRef, setExportLoading);
|
HandlePaymentRequestExport(type, filters, search, tableRef, setExportLoading);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentRequestContext.Provider value={contextValue}>
|
<PaymentRequestContext.Provider value={contextValue}>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import React, {
|
|||||||
useContext,
|
useContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useRef,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@ -59,6 +60,7 @@ const TenantPage = () => {
|
|||||||
const [refetchFn, setRefetchFn] = useState(null);
|
const [refetchFn, setRefetchFn] = useState(null);
|
||||||
const [filters, setFilters] = useState();
|
const [filters, setFilters] = useState();
|
||||||
|
|
||||||
|
|
||||||
// ---------- Hooks ----------
|
// ---------- Hooks ----------
|
||||||
const debouncedSearch = useDebounce(searchText, 500);
|
const debouncedSearch = useDebounce(searchText, 500);
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
@ -66,6 +68,9 @@ const TenantPage = () => {
|
|||||||
const isSuperTenant = useHasUserPermission(SUPPER_TENANT);
|
const isSuperTenant = useHasUserPermission(SUPPER_TENANT);
|
||||||
const canManageTenants = useHasUserPermission(MANAGE_TENANTS);
|
const canManageTenants = useHasUserPermission(MANAGE_TENANTS);
|
||||||
const isSelfTenant = useHasUserPermission(VIEW_TENANTS);
|
const isSelfTenant = useHasUserPermission(VIEW_TENANTS);
|
||||||
|
const updatedRef = useRef();
|
||||||
|
const [filterData, setFilterdata] = useState(null);
|
||||||
|
const [filter, setFilter] = useState('')
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
@ -77,8 +82,18 @@ const TenantPage = () => {
|
|||||||
setFilters(values);
|
setFilters(values);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const clearFilter = () => {
|
||||||
|
setFilters(defaultFilterValues); // update active filters
|
||||||
|
updatedRef.current?.resetFields?.(); // reset RHF fields in panel
|
||||||
|
};
|
||||||
|
|
||||||
const filterPanelElement = useMemo(
|
const filterPanelElement = useMemo(
|
||||||
() => <TenantFilterPanel onApply={handleApplyFilters} />,
|
() => <TenantFilterPanel
|
||||||
|
onApply={handleApplyFilters}
|
||||||
|
ref={updatedRef}
|
||||||
|
clearFilter={clearFilter}
|
||||||
|
setFilterdata={setFilterdata}
|
||||||
|
/>,
|
||||||
[handleApplyFilters]
|
[handleApplyFilters]
|
||||||
);
|
);
|
||||||
// ---------- Fab Filter Panel ----------
|
// ---------- Fab Filter Panel ----------
|
||||||
@ -112,6 +127,20 @@ const TenantPage = () => {
|
|||||||
// ---------- Context Value ----------
|
// ---------- Context Value ----------
|
||||||
const contextValue = {};
|
const contextValue = {};
|
||||||
|
|
||||||
|
const handleRemoveChip = (key, id) => {
|
||||||
|
setFilters((prev) => {
|
||||||
|
const updated = { ...prev };
|
||||||
|
if (Array.isArray(updated[key])) {
|
||||||
|
updated[key] = updated[key].filter((v) => v !== id);
|
||||||
|
} else {
|
||||||
|
updated[key] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => updatedRef.current?.resetFieldValue(key, updated[key]), 0);
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TenantContext.Provider value={contextValue}>
|
<TenantContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -121,7 +150,7 @@ const TenantPage = () => {
|
|||||||
{ label: "Tenant", link: null },
|
{ label: "Tenant", link: null },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<div className="card text-center my-4 p-md-5 px-1 py-3 pb-10">
|
<div className="card page-min-h text-center my-4 p-md-5 px-1 py-3 pb-10">
|
||||||
{/* Super Tenant Actions */}
|
{/* Super Tenant Actions */}
|
||||||
{isSuperTenant && (
|
{isSuperTenant && (
|
||||||
<div className="p-0">
|
<div className="p-0">
|
||||||
@ -157,10 +186,13 @@ const TenantPage = () => {
|
|||||||
{/* Tenant List or Access Denied */}
|
{/* Tenant List or Access Denied */}
|
||||||
{isSuperTenant ? (
|
{isSuperTenant ? (
|
||||||
<TenantsList
|
<TenantsList
|
||||||
|
setRefetchFn={setRefetchFn}
|
||||||
|
setIsRefetching={setIsRefetching}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
searchText={debouncedSearch}
|
searchText={debouncedSearch}
|
||||||
setIsRefetching={setIsRefetching}
|
filterData={filterData}
|
||||||
setRefetchFn={setRefetchFn}
|
removeFilterChip={handleRemoveChip}
|
||||||
|
clearFilter={clearFilter}
|
||||||
/>
|
/>
|
||||||
) : !isSelfTenant ? (
|
) : !isSelfTenant ? (
|
||||||
<div className="text-center my-4 p-2">
|
<div className="text-center my-4 p-2">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user