273 lines
10 KiB
JavaScript
273 lines
10 KiB
JavaScript
import React, { useEffect, useState } from "react";
|
|
import WorkItem from "./WorkItem";
|
|
import { useProjectDetails } from "../../../hooks/useProjects";
|
|
import { cacheData, getCachedData } from "../../../slices/apiDataManager";
|
|
import { useDispatch } from "react-redux";
|
|
import { refreshData } from "../../../slices/localVariablesSlice";
|
|
import ProjectRepository from "../../../repositories/ProjectRepository";
|
|
import showToast from "../../../services/toastService";
|
|
import { useHasUserPermission } from "../../../hooks/useHasUserPermission";
|
|
import {
|
|
ASSIGN_REPORT_TASK,
|
|
MANAGE_PROJECT_INFRA,
|
|
MANAGE_TASK,
|
|
} from "../../../utils/constants";
|
|
import { useParams } from "react-router-dom";
|
|
import ProgressDonutChart from "../../Charts/ProgressDonutChart";
|
|
import ProgressBar from "../../common/ProgressBar";
|
|
import { componentsToColor } from "pdf-lib";
|
|
|
|
const WorkArea = ({ workArea, floor, forBuilding }) => {
|
|
const [workItems, setWorkItems] = useState([]);
|
|
const dispatch = useDispatch();
|
|
const [Project, setProject] = useState();
|
|
const { projectId } = useParams();
|
|
|
|
const ManageTasks = useHasUserPermission(MANAGE_TASK);
|
|
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
|
const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK);
|
|
|
|
const [workAreaStatus, setWorkAreaStatus] = useState({
|
|
completed: 0,
|
|
planned: 100,
|
|
});
|
|
|
|
useEffect(() => {
|
|
const totalCompleted = workItems.reduce(
|
|
(sum, i) => sum + (i.workItem?.completedWork || 0),
|
|
0
|
|
);
|
|
const totalPlanned = workItems.reduce(
|
|
(sum, i) => sum + (i.workItem?.plannedWork || 0),
|
|
0
|
|
);
|
|
const percent =
|
|
totalPlanned > 0 ? (totalCompleted / totalPlanned) * 100 : 0;
|
|
//setPercentComplete(Math.min(percent, 100)); // cap at 100%
|
|
setWorkAreaStatus({ completed: totalCompleted, planned: totalPlanned });
|
|
}, [workItems]);
|
|
|
|
useEffect(() => {
|
|
const project = getCachedData("projectInfo");
|
|
setProject(project);
|
|
|
|
if (!project || !forBuilding?.id || !floor?.id || !workArea?.id) return;
|
|
const building = project.buildings?.find((b) => b.id === forBuilding.id);
|
|
const floors = building?.floors?.find((f) => f.id === floor.id);
|
|
const workAreas = floor?.workAreas?.find((wa) => wa.id === workArea.id);
|
|
setWorkItems(workAreas?.workItems || []);
|
|
}, [workArea, floor, floor]);
|
|
|
|
const HanldeDeleteActivity = async (workItemId) => {
|
|
try {
|
|
const updatedProject = { ...Project.data };
|
|
const response = await ProjectRepository.deleteProjectTask(workItemId);
|
|
const newProject = {
|
|
...updatedProject,
|
|
buildings: updatedProject?.buildings.map((building) =>
|
|
building?.id === building?.id
|
|
? {
|
|
...building,
|
|
floors: building?.floors?.map((floor) =>
|
|
floor.id === floor?.id
|
|
? {
|
|
...floor,
|
|
workAreas: floor.workAreas.map((workArea) =>
|
|
workArea.id === workArea?.id
|
|
? {
|
|
...workArea,
|
|
workItems: workArea.workItems.filter(
|
|
(item) =>
|
|
String(item?.workItem?.id ?? item?.id) !==
|
|
String(workItemId)
|
|
),
|
|
}
|
|
: workArea
|
|
),
|
|
}
|
|
: floor
|
|
),
|
|
}
|
|
: building
|
|
),
|
|
};
|
|
|
|
cacheData("projectInfo", {
|
|
projectId: newProject.id,
|
|
data: newProject,
|
|
});
|
|
|
|
dispatch(refreshData(true));
|
|
|
|
showToast("Activity Deleted Successfully", "success");
|
|
} catch (error) {
|
|
const message =
|
|
error.response?.data?.message ||
|
|
error.message ||
|
|
"An unexpected error occurred";
|
|
showToast(message, "error");
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const toggleButtons = document.querySelectorAll(".accordion-button");
|
|
|
|
toggleButtons.forEach((btn) => {
|
|
const icon = btn.querySelector(".toggle-icon");
|
|
|
|
btn.addEventListener("click", () => {
|
|
setTimeout(() => {
|
|
if (btn.classList.contains("collapsed")) {
|
|
icon.classList.remove("bx-minus-circle");
|
|
icon.classList.add("bx-plus-circle");
|
|
} else {
|
|
icon.classList.remove("bx-plus-circle");
|
|
icon.classList.add("bx-minus-circle");
|
|
}
|
|
}, 300); // allow Bootstrap collapse to complete
|
|
});
|
|
});
|
|
|
|
return () => {
|
|
toggleButtons.forEach((btn) => {
|
|
btn.removeEventListener("click", () => {});
|
|
});
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<React.Fragment key={workArea.id}>
|
|
<tr>
|
|
<td colSpan="4" className="p-0">
|
|
<div
|
|
className="accordion border-none"
|
|
id={`accordion-${workArea.id}`}
|
|
>
|
|
<div className="accordion-item background border-0">
|
|
{/* Accordion Header */}
|
|
<p
|
|
className="accordion-header mb-0"
|
|
id={`heading-${workArea.id}`}
|
|
>
|
|
<button
|
|
className={`accordion-button text-start px-2 py-2 custom-accordion-btn ${
|
|
workItems && workItems.length > 0 ? "collapsed" : "disabled"
|
|
}`}
|
|
type="button"
|
|
data-bs-toggle={
|
|
workItems && workItems.length > 0 ? "collapse" : ""
|
|
}
|
|
data-bs-target={
|
|
workItems && workItems.length > 0
|
|
? `#collapse-${workArea.id}`
|
|
: undefined
|
|
}
|
|
aria-expanded="false"
|
|
aria-controls={`collapse-${workArea.id}`}
|
|
disabled={!(workItems && workItems.length > 0)}
|
|
>
|
|
<i
|
|
className={`bx me-2 toggle-icon ${
|
|
workItems && workItems.length > 0
|
|
? "bx-plus-circle"
|
|
: "bx-block"
|
|
}`}
|
|
style={{
|
|
fontSize: "1.2rem",
|
|
color:
|
|
workItems && workItems.length > 0 ? "" : "transparent",
|
|
}}
|
|
></i>
|
|
|
|
<div className="d-flex justify-content-start row w-100 align-items-center">
|
|
<div className="d-flex col-5">
|
|
<span className="fw-semibold text-primary small">
|
|
Floor:
|
|
</span>
|
|
<span className="fw-normal text-darkgreen small px-2">
|
|
{floor.floorName}
|
|
</span>
|
|
</div>
|
|
<div className="text-start col-5">
|
|
<span className="fw-semibold text-primary small">
|
|
Work Area:
|
|
</span>
|
|
<span className="fw-normal text-darkgreen small px-2">
|
|
{workArea.areaName}
|
|
</span>
|
|
</div>
|
|
{workArea?.workItems?.length > 0 && (
|
|
<div className="col-2">
|
|
<ProgressBar
|
|
completedWork={workAreaStatus.completed}
|
|
plannedWork={workAreaStatus.planned}
|
|
className="m-0 text-info"
|
|
></ProgressBar>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</button>
|
|
</p>
|
|
|
|
{/* Accordion Body */}
|
|
{workItems && workItems.length > 0 && (
|
|
<div
|
|
id={`collapse-${workArea.id}`}
|
|
className="accordion-collapse collapse"
|
|
aria-labelledby={`heading-${workArea.id}`}
|
|
>
|
|
<div className="accordion-body px-1">
|
|
<table className="table table-sm mx-1">
|
|
<thead>
|
|
<tr>
|
|
<th className="infra-activity-table-header-first">
|
|
Activity
|
|
</th>
|
|
<th className="infra-activity-table-header d-sm-table-cell d-md-none">
|
|
Status
|
|
</th>
|
|
<th className="infra-activity-table-header d-none d-md-table-cell">
|
|
Category
|
|
</th>
|
|
<th className="infra-activity-table-header d-none d-md-table-cell">
|
|
Completed/Planned
|
|
</th>
|
|
<th className="infra-activity-table-header d-none d-md-table-cell">
|
|
Today's Planned
|
|
</th>
|
|
<th className="infra-activity-table-header">
|
|
Progress
|
|
</th>
|
|
{(ManageInfra ||
|
|
(!projectId && ManageAndAssignTak)) && (
|
|
<th className="infra-activity-table-header text-end">
|
|
<span className="px-2">Actions</span>
|
|
</th>
|
|
)}
|
|
</tr>
|
|
</thead>
|
|
<tbody className="table-border-bottom-0">
|
|
{workArea.workItems.map((workItem) => (
|
|
<WorkItem
|
|
key={workItem.workItemId}
|
|
workItem={workItem}
|
|
forBuilding={forBuilding}
|
|
forFloor={floor}
|
|
forWorkArea={workArea}
|
|
deleteHandleTask={HanldeDeleteActivity}
|
|
/>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
export default WorkArea;
|