Adding filter chips in daily Progress Report.
This commit is contained in:
parent
07ba95e533
commit
6b9d7c56bc
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 { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
@ -11,8 +11,9 @@ import { DateRangePicker1 } from "../common/DateRangePicker";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import { localToUtc } from "../../utils/appUtils";
|
||||
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 selectedProject = useSelectedProject();
|
||||
const selectedService = useCurrentService();
|
||||
@ -23,10 +24,39 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
defaultValues: TaskReportDefaultValue,
|
||||
});
|
||||
|
||||
const dynamicDefaultFilter = useMemo(() => {
|
||||
return {
|
||||
...TaskReportDefaultValue,
|
||||
buildingIds: TaskReportDefaultValue.buildingIds || [],
|
||||
floorIds: TaskReportDefaultValue.floorIds || [],
|
||||
activityIds: TaskReportDefaultValue.activityIds || [],
|
||||
dateFrom: TaskReportDefaultValue.startDate,
|
||||
dateTo: TaskReportDefaultValue.endDate,
|
||||
};
|
||||
}, [selectedProject]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
resetFieldValue: (name, value) => {
|
||||
// Reset specific field
|
||||
if (value !== undefined) {
|
||||
setValue(name, value);
|
||||
} else {
|
||||
reset({ ...methods.getValues(), [name]: defaultFilter[name] });
|
||||
}
|
||||
},
|
||||
getValues: methods.getValues, // optional, to read current filter state
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (data && setFilterdata) {
|
||||
setFilterdata(data);
|
||||
}
|
||||
}, [data, setFilterdata]);
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = methods;
|
||||
const closePanel = () => {
|
||||
@ -52,7 +82,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
<div className="mb-3 w-100">
|
||||
<label className="fw-semibold">Choose Date Range:</label>
|
||||
<DateRangePicker1
|
||||
className="w-100"
|
||||
className="w-100"
|
||||
placeholder="DD-MM-YYYY To DD-MM-YYYY"
|
||||
startField="dateFrom"
|
||||
endField="dateTo"
|
||||
@ -105,6 +135,6 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default TaskReportFilterPanel;
|
||||
|
||||
@ -17,8 +17,9 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { TaskReportListSkeleton } from "./TaskRepprtListSkeleton";
|
||||
import HoverPopup from "../common/HoverPopup";
|
||||
import TaskReportFilterChips from "./TaskReportFilterChips";
|
||||
|
||||
const TaskReportList = () => {
|
||||
const TaskReportList = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [filters, setFilters] = useState({
|
||||
selectedBuilding: "",
|
||||
@ -29,7 +30,7 @@ const TaskReportList = () => {
|
||||
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
|
||||
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||
|
||||
const { service, openModal, closeModal, filter } = useDailyProgrssContext();
|
||||
const { service, openModal, closeModal, filter: contextFilter } = useDailyProgrssContext();
|
||||
const selectedProject = useSelectedProject();
|
||||
const { projectNames } = useProjectName();
|
||||
|
||||
@ -37,7 +38,7 @@ const TaskReportList = () => {
|
||||
selectedProject,
|
||||
ITEMS_PER_PAGE,
|
||||
currentPage,
|
||||
service, filter
|
||||
service, contextFilter
|
||||
);
|
||||
|
||||
const ProgrssReportColumn = [
|
||||
@ -193,124 +194,136 @@ const TaskReportList = () => {
|
||||
if (isError) return <div>Loading....</div>;
|
||||
return (
|
||||
<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>
|
||||
<span>
|
||||
Reported/Planned{" "}
|
||||
<HoverPopup
|
||||
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>
|
||||
<div className="main-content">
|
||||
<div className="col-12 mb-2 mt-2 px-4">
|
||||
<TaskReportFilterChips
|
||||
filter={filter}
|
||||
filterData={filterData}
|
||||
removeFilterChip={removeFilterChip}
|
||||
clearFilter={clearFilter}
|
||||
/>
|
||||
|
||||
<th>Assign Date</th>
|
||||
<th>Team</th>
|
||||
<th className="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{groupedTasks.length === 0 && (
|
||||
</div>
|
||||
<div className="mt-2 table-responsive text-nowrap">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center align-middle" style={{ height: "200px", borderBottom: "none" }}>
|
||||
No reports available
|
||||
</td>
|
||||
</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>
|
||||
|
||||
{groupedTasks.map(({ date, tasks }) => (
|
||||
<React.Fragment key={date}>
|
||||
<tr className="table-row-header text-start">
|
||||
<td colSpan={6}>
|
||||
<strong>{formatUTCToLocalTime(date)}</strong>
|
||||
<th>
|
||||
<span>
|
||||
Reported/Planned{" "}
|
||||
<HoverPopup
|
||||
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>
|
||||
</tr>
|
||||
{tasks.map((task, idx) => (
|
||||
<tr key={task.id || idx}>
|
||||
<td className="flex-wrap text-start">
|
||||
<div>
|
||||
{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>
|
||||
)}
|
||||
|
||||
{groupedTasks.map(({ date, tasks }) => (
|
||||
<React.Fragment key={date}>
|
||||
<tr className="table-row-header text-start">
|
||||
<td colSpan={6}>
|
||||
<strong>{formatUTCToLocalTime(date)}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{tasks.map((task, idx) => (
|
||||
<tr key={task.id || idx}>
|
||||
<td className="flex-wrap text-start">
|
||||
<div>
|
||||
{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>
|
||||
{
|
||||
data?.data?.length > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={data.totalPages}
|
||||
onPageChange={paginate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
data?.data?.length > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={data.totalPages}
|
||||
onPageChange={paginate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</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 { useServices } from "../../hooks/masterHook/useMaster";
|
||||
import TaskReportList from "../../components/DailyProgressRport/TaskReportList";
|
||||
@ -13,6 +13,7 @@ import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import SelectField from "../../components/common/Forms/SelectField";
|
||||
import { AppFormController } from "../../hooks/appHooks/useAppForm";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { TaskReportDefaultValue } from "../../components/DailyProgressRport/TaskRportScheam";
|
||||
|
||||
const DailyProgrssContext = createContext();
|
||||
export const useDailyProgrssContext = () => {
|
||||
@ -30,8 +31,9 @@ const DailyProgrssReport = () => {
|
||||
const [service, setService] = useState("");
|
||||
const [filter, setFilter] = useState('')
|
||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||
const updatedRef = useRef();
|
||||
const { data, isLoading, isError, error } = useProjectAssignedServices(selectedProject);
|
||||
|
||||
const [filterData, setFilterdata] = useState(null);
|
||||
const [modal, setModal] = useState({ type: null, data: null });
|
||||
|
||||
const openModal = (type, data = null) => setModal({ type, data });
|
||||
@ -49,20 +51,38 @@ const DailyProgrssReport = () => {
|
||||
serviceFilter: ""
|
||||
}
|
||||
});
|
||||
|
||||
const clearFilter = () => setFilter(TaskReportDefaultValue);
|
||||
const handleFilter = (filterObj) => {
|
||||
setFilter(filterObj)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setShowTrigger(true);
|
||||
setOffcanvasContent("Report Filter", <TaskReportFilterPanel handleFilter={handleFilter} />);
|
||||
setOffcanvasContent("Report Filter", <TaskReportFilterPanel handleFilter={handleFilter} ref={updatedRef}
|
||||
clearFilter={clearFilter}
|
||||
setFilterdata={setFilterdata} />);
|
||||
|
||||
return () => {
|
||||
setShowTrigger(false);
|
||||
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 (
|
||||
<div className="container-fluid">
|
||||
<DailyProgrssContext.Provider value={contextDispatcher}>
|
||||
@ -97,7 +117,7 @@ const DailyProgrssReport = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="card card-fullscreen p-5">
|
||||
<div className="card page-min-h p-5">
|
||||
{data?.length > 0 && (
|
||||
<div className="col-sm-4 col-md-3 col-12 text-start">
|
||||
<AppFormController
|
||||
@ -107,15 +127,15 @@ const DailyProgrssReport = () => {
|
||||
render={({ field }) => (
|
||||
<SelectField
|
||||
label="Services"
|
||||
options={[{ id: "", name: "All Projects" }, ...(data ?? [])]}
|
||||
options={[{ id: "", name: "All Projects" }, ...(data ?? [])]}
|
||||
placeholder="Select Service"
|
||||
labelKey="name"
|
||||
labelKey="name"
|
||||
valueKey="id"
|
||||
isLoading={isLoading}
|
||||
value={field.value}
|
||||
onChange={(val) => {
|
||||
field.onChange(val);
|
||||
setService(val);
|
||||
field.onChange(val);
|
||||
setService(val);
|
||||
}}
|
||||
className="m-0"
|
||||
/>
|
||||
@ -125,7 +145,10 @@ const DailyProgrssReport = () => {
|
||||
)}
|
||||
|
||||
<div>
|
||||
<TaskReportList />
|
||||
<TaskReportList filter={filter}
|
||||
filterData={filterData}
|
||||
removeFilterChip={handleRemoveChip}
|
||||
clearFilter={clearFilter} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user