249 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { useTaskList } from "../../hooks/useTasks";
import { useProjectName } from "../../hooks/useProjects";
import { setProjectId } from "../../slices/localVariablesSlice";
import Breadcrumb from "../../components/common/Breadcrumb";
import DateRangePicker from "../../components/common/DateRangePicker";
import FilterIcon from "../../components/common/FilterIcon";
import GlobalModel from "../../components/common/GlobalModel";
import ReportTask from "../../components/Activities/ReportTask";
import ReportTaskComments from "../../components/Activities/ReportTaskComments";
import SubTask from "../../components/Activities/SubTask";
import { formatNumber, formatUTCToLocalTime } from "../../utils/dateUtils";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants";
import { useSelectedProject } from "../../slices/apiDataManager";
import moment from "moment";
import Loader from "../../components/common/Loader";
const DailyTask = () => {
const dispatch = useDispatch();
const selectedProject = useSelectedProject();
const { projectNames } = useProjectName();
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
const [filters, setFilters] = useState({
selectedBuilding: "",
selectedFloors: [],
selectedActivities: [],
});
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
const { TaskList, loading: taskLoading } = useTaskList(
selectedProject || null,
dateRange?.startDate || null,
dateRange?.endDate || null
);
// Ensure project is set
useEffect(() => {
if (!selectedProject && projectNames.length > 0) {
debugger
dispatch(setProjectId(projectNames[0].id));
}
}, [selectedProject, projectNames, dispatch]);
// 🔹 Reset filters when project changes
useEffect(() => {
setFilters({
selectedBuilding: "",
selectedFloors: [],
selectedActivities: [],
});
}, [selectedProject]);
// Memoized filtering
const filteredTasks = useMemo(() => {
if (!TaskList) return [];
return TaskList.filter((task) => {
const { selectedBuilding, selectedFloors, selectedActivities } = filters;
if (selectedBuilding && task?.workItem?.workArea?.floor?.building?.name !== selectedBuilding) return false;
if (selectedFloors.length > 0 && !selectedFloors.includes(task?.workItem?.workArea?.floor?.floorName)) return false;
if (selectedActivities.length > 0 && !selectedActivities.includes(task?.workItem?.activityMaster?.activityName)) return false;
return true;
});
}, [TaskList, filters]);
// Memoized dates
const groupedTasks = useMemo(() => {
const groups = {};
filteredTasks.forEach((task) => {
const date = task.assignmentDate.split("T")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(task);
});
return Object.keys(groups)
.sort((a, b) => new Date(b) - new Date(a))
.map((date) => ({ date, tasks: groups[date] }));
}, [filteredTasks]);
// --- Modal State
const [modal, setModal] = useState({ type: null, data: null });
const openModal = (type, data = null) => setModal({ type, data });
const closeModal = () => setModal({ type: null, data: null });
// --- Render helpers
const renderTeamMembers = (task, refIndex) => (
<div
key={refIndex}
tabIndex="0"
className="d-flex align-items-center avatar-group justify-content-center"
data-bs-toggle="popover"
data-bs-trigger="focus"
data-bs-placement="left"
data-bs-html="true"
data-bs-content={`
<div class="border border-secondary rounded custom-popover p-2 px-3">
${task.teamMembers
.map(
(m) => `
<div class="d-flex align-items-center gap-2 mb-2">
<div class="avatar avatar-xs">
<span class="avatar-initial rounded-circle bg-label-primary">
${m?.firstName?.charAt(0) || ""}${m?.lastName?.charAt(0) || ""}
</span>
</div>
<span>${m.firstName} ${m.lastName}</span>
</div>`
)
.join("")}
</div>
`}
>
{task.teamMembers.slice(0, 3).map((m) => (
<div key={m.id} className="avatar avatar-xs" title={`${m.firstName} ${m.lastName}`}>
<span className="avatar-initial rounded-circle bg-label-primary">
{m?.firstName.slice(0, 1)}
</span>
</div>
))}
{task.teamMembers.length > 3 && (
<div className="avatar avatar-xs" title={`${task.teamMembers.length - 3} more`}>
<span className="avatar-initial rounded-circle bg-label-secondary">+{task.teamMembers.length - 3}</span>
</div>
)}
</div>
);
return (
<>
{/* --- Modals --- */}
{modal.type === "report" && (
<GlobalModel isOpen size="md" closeModal={closeModal}>
<ReportTask report={modal.data} closeModal={closeModal} />
</GlobalModel>
)}
{modal.type === "comments" && (
<GlobalModel isOpen size="lg" closeModal={closeModal}>
<ReportTaskComments
commentsData={modal.data.task}
actionAllow={modal.data.isActionAllow}
handleCloseAction={(isSubTask) => {
if (isSubTask) openModal("subtask", modal.data.task);
else closeModal();
}}
closeModal={closeModal}
/>
</GlobalModel>
)}
{modal.type === "subtask" && (
<GlobalModel isOpen size="lg" closeModal={closeModal}>
<SubTask activity={modal.data} onClose={closeModal} />
</GlobalModel>
)}
<div className="container-fluid">
<Breadcrumb data={[{ label: "Home", link: "/dashboard" }, { label: "Daily Progress Report" }]} />
<div className="card card-action mb-6">
<div className="card-body p-1 p-sm-2">
{!selectedProject && (<div className="text-center text-muted">Please Select Project</div>)}
{/* --- Filters --- */}
<div className="d-flex align-items-center mb-2">
<DateRangePicker onRangeChange={setDateRange} endDateMode="today" DateDifference="6" dateFormat="DD-MM-YYYY" />
<FilterIcon
taskListData={TaskList}
onApplyFilters={setFilters}
currentSelectedBuilding={filters.selectedBuilding}
currentSelectedFloors={filters.selectedFloors}
currentSelectedActivities={filters.selectedActivities}
selectedProject={selectedProject}
/>
</div>
{/* --- Table --- */}
<div className="table-responsive text-nowrap mt-3">
<table className="table">
<thead>
<tr>
<th>Activity</th>
<th>Assigned</th>
<th>Completed</th>
<th>Assign On</th>
<th>Team</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{taskLoading && (
<tr>
<td colSpan={6} className="text-center">
<Loader/>
</td>
</tr>
)}
{!taskLoading && groupedTasks.length === 0 && (
<tr>
<td colSpan={6} className="text-center">No Reports Found</td>
</tr>
)}
{!taskLoading &&
groupedTasks.map(({ date, tasks }) => (
<React.Fragment key={date}>
<tr className="table-row-header text-start">
<td colSpan={6}><strong>{formatUTCToLocalTime(date)}</strong></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">
{task.workItem.workArea?.floor?.building?.name} {task.workItem.workArea?.floor?.floorName} {task.workItem.workArea?.areaName}
</div>
</td>
<td>{formatNumber(task.plannedTask)} / {formatNumber(task.workItem.plannedWork - task.workItem.completedWork)}</td>
<td>{task.completedTask}</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>
</div>
</div>
</>
);
};
export default DailyTask;