Merge pull request 'Adding_Chips_DailyTask :- Adding Chips in Daily task report.' (#538) from Adding_Chips_DailyTask into Purchase_Invoice_Management

Reviewed-on: #538
merged
This commit is contained in:
pramod.mahajan 2025-12-10 11:01:16 +00:00
commit 01fc302011
19 changed files with 700 additions and 298 deletions

View 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;

View File

@ -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 = () => {
@ -105,6 +138,6 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
</form> </form>
</FormProvider> </FormProvider>
); );
}; });
export default TaskReportFilterPanel; export default TaskReportFilterPanel;

View File

@ -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,6 +194,16 @@ const TaskReportList = () => {
if (isError) return <div>Loading....</div>; if (isError) return <div>Loading....</div>;
return ( return (
<div> <div>
<div className="main-content">
<div className="col-12 mb-2 mt-2 px-4">
<TaskReportFilterChips
filter={filter}
filterData={filterData}
removeFilterChip={removeFilterChip}
clearFilter={clearFilter}
/>
</div>
<div className="mt-2 table-responsive text-nowrap"> <div className="mt-2 table-responsive text-nowrap">
<table className="table"> <table className="table">
<thead> <thead>
@ -312,6 +323,7 @@ const TaskReportList = () => {
) )
} }
</div > </div >
</div >
); );
}; };

View File

@ -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>

View File

@ -124,13 +124,14 @@ 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;
@ -143,7 +144,7 @@ const Documents = ({ Document_Entity, Entity }) => {
<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}

View File

@ -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">
{" "} {" "}

View File

@ -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
})); }));
@ -176,8 +184,7 @@ 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)}
> >
@ -185,8 +192,7 @@ const ExpenseFilterPanel = forwardRef(
</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)}
> >

View File

@ -83,7 +83,7 @@ const ExpenseList = ({
} }
}; };
const groupByField = (items, field) => { const groupByField = (items, field) => {
if (!field || field === "none") { if (!field || field === "none") {
return { return {
All: { All: {
@ -140,7 +140,7 @@ const groupByField = (items, field) => {
acc[groupKey].items.push(item); acc[groupKey].items.push(item);
return acc; return acc;
}, {}); }, {});
}; };
const expenseColumns = [ const expenseColumns = [
@ -167,8 +167,7 @@ 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
@ -182,8 +181,7 @@ 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,8 +214,7 @@ 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"}
@ -238,8 +235,8 @@ 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);
@ -247,7 +244,7 @@ const grouped = isNoGrouping
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,23 +340,19 @@ 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"
col.key === "submitted"
? "justify-content-center" ? "justify-content-center"
: "" : ""
} }
@ -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>

View File

@ -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]);

View File

@ -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();
}; };

View File

@ -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>

View File

@ -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>

View 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;

View File

@ -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;

View File

@ -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,6 +137,16 @@ const TenantsList = ({
); );
return ( return (
<> <>
<div className="main-content">
<div className="col-12 mb-2 mt-2 px-4">
<TenantFilterChips
filter={filters}
filterData={filterData}
removeFilterChip={removeFilterChip}
clearFilter={clearFilter}
/>
</div>
<div className=" mt-3"> <div className=" mt-3">
<div className=" text-nowrap table-responsive"> <div className=" text-nowrap table-responsive">
<table className="table border-top dataTable text-nowrap"> <table className="table border-top dataTable text-nowrap">
@ -185,6 +197,7 @@ const TenantsList = ({
)} )}
</div> </div>
</div> </div>
</div>
</> </>
); );
}; };

View File

@ -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
@ -125,7 +147,10 @@ const DailyProgrssReport = () => {
)} )}
<div> <div>
<TaskReportList /> <TaskReportList filter={filter}
filterData={filterData}
removeFilterChip={handleRemoveChip}
clearFilter={clearFilter} />
</div> </div>
</div> </div>

View File

@ -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;
}); });

View File

@ -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);
@ -82,7 +87,7 @@ 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}>

View File

@ -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">