partially integrated react-query in project infra
This commit is contained in:
parent
a02a33a247
commit
0c8ff7b28c
@ -8,7 +8,7 @@ import WorkAreaModel from "../Project/Infrastructure/WorkAreaModel";
|
|||||||
import TaskModel from "../Project/Infrastructure/TaskModel";
|
import TaskModel from "../Project/Infrastructure/TaskModel";
|
||||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||||
import {useProjectDetails, useProjects} from "../../hooks/useProjects";
|
import {useProjectDetails, useProjectInfra, useProjects} from "../../hooks/useProjects";
|
||||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||||
import {MANAGE_PROJECT_INFRA} from "../../utils/constants";
|
import {MANAGE_PROJECT_INFRA} from "../../utils/constants";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
@ -21,11 +21,12 @@ const InfraPlanning = () =>
|
|||||||
{
|
{
|
||||||
const {profile: LoggedUser, refetch : fetchData} = useProfile()
|
const {profile: LoggedUser, refetch : fetchData} = useProfile()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
// const {projects,loading:project_listLoader,error:projects_error} = useProjects()
|
|
||||||
|
|
||||||
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
||||||
|
const {projectInfra, isLoading, error} = useProjectInfra( selectedProject )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ManageInfra = useHasUserPermission( MANAGE_PROJECT_INFRA )
|
const ManageInfra = useHasUserPermission( MANAGE_PROJECT_INFRA )
|
||||||
const {projects_Details, loading: project_deatilsLoader, error: project_error,refetch} = useProjectDetails( selectedProject )
|
|
||||||
const reloadedData = useSelector( ( store ) => store.localVariables.reload )
|
const reloadedData = useSelector( ( store ) => store.localVariables.reload )
|
||||||
|
|
||||||
|
|
||||||
@ -45,12 +46,9 @@ const InfraPlanning = () =>
|
|||||||
<div className="card-body" style={{ padding: "0.5rem" }}>
|
<div className="card-body" style={{ padding: "0.5rem" }}>
|
||||||
<div className="align-items-center">
|
<div className="align-items-center">
|
||||||
<div className="row ">
|
<div className="row ">
|
||||||
{project_deatilsLoader && ( <p>Loading...</p> )}
|
{isLoading && ( <p>Loading...</p> )}
|
||||||
{( !project_deatilsLoader && projects_Details?.buildings.length === 0 ) && ( <p>No Result Found</p> )}
|
{( !isLoading && projectInfra.length === 0 ) && ( <p>No Result Found</p> )}
|
||||||
|
{(!isLoading && projectInfra?.length > 0) && (<InfraTable buildings={projectInfra} projectId={selectedProject}/>)}
|
||||||
|
|
||||||
|
|
||||||
{(!project_deatilsLoader && projects_Details?.buildings?.length > 0) && (<InfraTable buildings={projects_Details?.buildings} projectId={projects_Details.id}/>)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { getProjectStatusName } from "../../utils/projectStatus";
|
import { getProjectStatusName } from "../../utils/projectStatus";
|
||||||
const AboutProject = ({ data }) => {
|
import {useProjectDetails} from "../../hooks/useProjects";
|
||||||
const [CurrentProject, setCurrentProject] = useState(data);
|
import {useParams} from "react-router-dom";
|
||||||
|
const AboutProject = () => {
|
||||||
useEffect(() => {
|
const {projectId} = useParams();
|
||||||
setCurrentProject(data);
|
const {projects_Details,isLoading,error} = useProjectDetails(projectId)
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data && (
|
{projects_Details && (
|
||||||
<div className="card mb-6">
|
<div className="card mb-6">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<small className="card-text text-uppercase text-muted small">
|
<small className="card-text text-uppercase text-muted small">
|
||||||
@ -21,8 +19,8 @@ const AboutProject = ({ data }) => {
|
|||||||
<i className="bx bx-check"></i>
|
<i className="bx bx-check"></i>
|
||||||
<span className="fw-medium mx-2">Start Date:</span>{" "}
|
<span className="fw-medium mx-2">Start Date:</span>{" "}
|
||||||
<span>
|
<span>
|
||||||
{data.startDate
|
{projects_Details.startDate
|
||||||
? moment(data.startDate).format("DD-MMM-YYYY")
|
? moment(projects_Details.startDate).format("DD-MMM-YYYY")
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
@ -30,31 +28,32 @@ const AboutProject = ({ data }) => {
|
|||||||
<i className="bx bx-stop-circle"></i>{" "}
|
<i className="bx bx-stop-circle"></i>{" "}
|
||||||
<span className="fw-medium mx-2">End Date:</span>{" "}
|
<span className="fw-medium mx-2">End Date:</span>{" "}
|
||||||
<span>
|
<span>
|
||||||
{data.endDate
|
{projects_Details.endDate
|
||||||
? moment(data.endDate).format("DD-MMM-YYYY")
|
? moment(projects_Details.endDate).format("DD-MMM-YYYY")
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="d-flex align-items-center mb-2">
|
<li className="d-flex align-items-center mb-2">
|
||||||
<i className="bx bx-trophy"></i>
|
<i className="bx bx-trophy"></i>
|
||||||
<span className="fw-medium mx-2">Status:</span>{" "}
|
<span className="fw-medium mx-2">Status:</span>{" "}
|
||||||
<span>{getProjectStatusName(data.projectStatusId)}</span>
|
<span>{projects_Details?.projectStatus?.status
|
||||||
|
}</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="d-flex align-items-center mb-4">
|
<li className="d-flex align-items-center mb-4">
|
||||||
<i className="bx bx-user"></i>
|
<i className="bx bx-user"></i>
|
||||||
<span className="fw-medium mx-2">Contact:</span>{" "}
|
<span className="fw-medium mx-2">Contact:</span>{" "}
|
||||||
<span>{data.contactPerson}</span>
|
<span>{projects_Details.contactPerson}</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="d-flex flex-column align-items-start mb-4">
|
<li className="d-flex flex-column align-items-start mb-4">
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
<i className="bx bx-flag"></i>
|
<i className="bx bx-flag"></i>
|
||||||
<span className="fw-medium mx-2">Address:</span>
|
<span className="fw-medium mx-2">Address:</span>
|
||||||
{data.projectAddress?.length <= 20 && (
|
{projects_Details.projectAddress?.length <= 20 && (
|
||||||
<span>{data.projectAddress}</span>
|
<span>{projects_Details.projectAddress}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{data.projectAddress?.length > 20 && (
|
{projects_Details.projectAddress?.length > 20 && (
|
||||||
<div className="ms-4 text-start">{data.projectAddress}</div>
|
<div className="ms-4 text-start">{projects_Details.projectAddress}</div>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -62,7 +61,7 @@ const AboutProject = ({ data }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!data && <span>loading...</span>}
|
{isLoading && <span>loading...</span>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,8 @@ const Building = ({
|
|||||||
toggleBuilding,
|
toggleBuilding,
|
||||||
expandedBuildings,
|
expandedBuildings,
|
||||||
getContent,
|
getContent,
|
||||||
}) => {
|
} ) =>
|
||||||
|
{
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={building.id}>
|
<React.Fragment key={building.id}>
|
||||||
<tr className="overflow-auto">
|
<tr className="overflow-auto">
|
||||||
@ -20,7 +21,7 @@ const Building = ({
|
|||||||
>
|
>
|
||||||
<div className="row table-responsive">
|
<div className="row table-responsive">
|
||||||
<h6 style={{ marginBottom: "0px", fontSize: "14px" }}>
|
<h6 style={{ marginBottom: "0px", fontSize: "14px" }}>
|
||||||
{building.name}
|
{building.buildingName}
|
||||||
{expandedBuildings.includes(building.id) ? (
|
{expandedBuildings.includes(building.id) ? (
|
||||||
<i className="bx bx-chevron-down"></i>
|
<i className="bx bx-chevron-down"></i>
|
||||||
) : (
|
) : (
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useMemo } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
|
||||||
import { getCachedData } from "../../../slices/apiDataManager";
|
import { getCachedData } from "../../../slices/apiDataManager";
|
||||||
import showToast from "../../../services/toastService";
|
import showToast from "../../../services/toastService";
|
||||||
|
import { useManageProjectInfra } from "../../../hooks/useProjects";
|
||||||
|
import useSelect from "../../common/useSelect";
|
||||||
|
|
||||||
// Zod validation schema
|
|
||||||
const buildingSchema = z.object({
|
const buildingSchema = z.object({
|
||||||
Id: z.string().optional(),
|
Id: z.string().optional(),
|
||||||
name: z.string().min(1, "Building name is required"),
|
name: z.string().min(1, "Building name is required"),
|
||||||
@ -18,142 +17,113 @@ const buildingSchema = z.object({
|
|||||||
.max(160, "Description cannot exceed 160 characters"),
|
.max(160, "Description cannot exceed 160 characters"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const BuildingModel = ({
|
const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
|
||||||
project,
|
|
||||||
onClose,
|
|
||||||
onSubmit,
|
|
||||||
clearTrigger,
|
|
||||||
onClearComplete,
|
|
||||||
editingBuilding = null,
|
|
||||||
}) => {
|
|
||||||
const selectedProject = useSelector(
|
const selectedProject = useSelector(
|
||||||
(store) => store.localVariables.projectId
|
(store) => store.localVariables.projectId
|
||||||
);
|
);
|
||||||
const [buildings, setBuildings] = useState([]);
|
|
||||||
const projects_Details = getCachedData("projectInfo");
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
id: "",
|
|
||||||
name: "",
|
|
||||||
description: "",
|
|
||||||
projectId: project?.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (clearTrigger) {
|
|
||||||
setFormData({ id: null, name: "", description: "", projectId: project.id });
|
|
||||||
onClearComplete();
|
|
||||||
} else if (editingBuilding) {
|
|
||||||
setFormData({ ...editingBuilding, projectId: project.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setValue("name", "");
|
|
||||||
};
|
|
||||||
}, [clearTrigger, onClearComplete, editingBuilding, project?.id]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
setValue,
|
||||||
|
watch,
|
||||||
reset,
|
reset,
|
||||||
getValues,
|
|
||||||
} = useForm({
|
} = useForm({
|
||||||
resolver: zodResolver(buildingSchema),
|
resolver: zodResolver(buildingSchema),
|
||||||
defaultValues: formData, // Set default values from formData state
|
defaultValues: {
|
||||||
});
|
|
||||||
|
|
||||||
const handleBuildingChange = (e) => {
|
|
||||||
const selectedBuilding = project.buildings.find(
|
|
||||||
(b) => b.id === +e.target.value
|
|
||||||
);
|
|
||||||
if (selectedBuilding) {
|
|
||||||
setFormData({ ...selectedBuilding, projectId: project.id });
|
|
||||||
setValue("name", selectedBuilding.name); // Update name field
|
|
||||||
setValue("description", selectedBuilding.description); // Update description field
|
|
||||||
} else {
|
|
||||||
setFormData({ id: null, name: "", description: "", projectId: project.id });
|
|
||||||
setValue("name", "");
|
|
||||||
setValue("description", "");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmitHandler = async (data) => {
|
|
||||||
if (String(data.Id) === "0") {
|
|
||||||
data.Id = null;
|
|
||||||
}
|
|
||||||
onSubmit({ ...data, projectId: project.id });
|
|
||||||
reset({
|
|
||||||
Id: "0",
|
Id: "0",
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if (data.Id !== null) {
|
const watchedId = watch("Id");
|
||||||
showToast("Building updated successfully.", "success");
|
|
||||||
} else {
|
const { mutate: ManageBuilding, isPending } = useManageProjectInfra({
|
||||||
showToast("Building created successfully.", "success");
|
onSuccessCallback: (data, variables) => {
|
||||||
}
|
showToast(
|
||||||
};
|
watchedId != "0"
|
||||||
|
? "Building updated Successfully"
|
||||||
|
: "Building created Successfully",
|
||||||
|
"success"
|
||||||
|
);
|
||||||
|
reset({ Id: "0", name: "", description: "" });
|
||||||
|
onClose?.();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedBuildings = useMemo(() => {
|
||||||
|
return (project || [])
|
||||||
|
.filter((b) => b?.buildingName)
|
||||||
|
.sort((a, b) => a.buildingName.localeCompare(b?.buildingName));
|
||||||
|
}, [project]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(projects_Details){
|
if (!watchedId || watchedId === "0") {
|
||||||
setBuildings(projects_Details.data?.buildings);
|
setValue("name", "");
|
||||||
|
setValue("description", "");
|
||||||
|
} else {
|
||||||
|
const selected = sortedBuildings.find((b) => String(b.id) === watchedId);
|
||||||
|
if (selected) {
|
||||||
|
setValue("name", selected.buildingName || "");
|
||||||
|
setValue("description", selected.description || "");
|
||||||
}
|
}
|
||||||
}, [projects_Details]);
|
}
|
||||||
|
}, [watchedId, sortedBuildings, setValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editingBuilding) {
|
||||||
|
reset({
|
||||||
|
Id: String(editingBuilding.id),
|
||||||
|
name: editingBuilding.name,
|
||||||
|
description: editingBuilding.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [editingBuilding]);
|
||||||
|
|
||||||
|
const onSubmitHandler = (data) => {
|
||||||
|
const payload = {
|
||||||
|
...data,
|
||||||
|
Id: data.Id === "0" ? null : data.Id,
|
||||||
|
projectId: selectedProject,
|
||||||
|
};
|
||||||
|
|
||||||
|
let infraObject = [
|
||||||
|
{
|
||||||
|
building: payload,
|
||||||
|
floor: null,
|
||||||
|
workArea: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
ManageBuilding({ infraObject, projectId: selectedProject });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-body">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-close"
|
|
||||||
data-bs-dismiss="modal"
|
|
||||||
aria-label="Close"
|
|
||||||
onClick={() => {
|
|
||||||
onClose();
|
|
||||||
reset(); // Call reset here
|
|
||||||
}}
|
|
||||||
></button>
|
|
||||||
<h5 className="text-center mb-2">
|
|
||||||
Manage Buildings - {project?.name}
|
|
||||||
</h5>
|
|
||||||
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
|
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
|
||||||
|
<h5 className="text-center mb-2">Manage Buildings </h5>
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form-label">Select Building</label>
|
<label className="form-label">Select Building</label>
|
||||||
<select
|
<select
|
||||||
{...register("Id")}
|
{...register("Id")}
|
||||||
className="select2 form-select form-select-sm"
|
className="select2 form-select form-select-sm"
|
||||||
onChange={(e) => {
|
|
||||||
handleBuildingChange(e);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<option value="0">Add New Building</option>
|
<option value="0">Add New Building</option>
|
||||||
|
{sortedBuildings.length > 0 ? (
|
||||||
{project?.buildings?.length > 0 ? (
|
sortedBuildings.map((b) => (
|
||||||
project.buildings
|
<option key={b.id} value={b.id}>
|
||||||
.filter((building) => building?.name)
|
{b.buildingName}
|
||||||
.sort((a, b) => {
|
|
||||||
const nameA = a.name || "";
|
|
||||||
const nameB = b.name || "";
|
|
||||||
return nameA?.localeCompare(nameB);
|
|
||||||
})
|
|
||||||
.map((building) => (
|
|
||||||
<option key={building.id} value={building.id}>
|
|
||||||
{building.name}
|
|
||||||
</option>
|
</option>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<option disabled>No buildings found</option>
|
<option disabled>No buildings found</option>
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
{errors.Id && (
|
{errors.Id && <span className="danger-text">{errors.Id.message}</span>}
|
||||||
<span className="danger-text">{errors.Id.message}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Name */}
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form-label">
|
<label className="form-label">
|
||||||
{formData.id ? "Rename Building Name" : "New Building Name"}
|
{watchedId !== "0" ? "Rename Building Name" : "New Building Name"}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register("name")}
|
{...register("name")}
|
||||||
@ -165,47 +135,47 @@ const BuildingModel = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form-label">Description</label>
|
<label className="form-label">Description</label>
|
||||||
<textarea
|
<textarea
|
||||||
{...register("description")}
|
{...register("description")}
|
||||||
maxLength="160"
|
|
||||||
rows="5"
|
rows="5"
|
||||||
|
maxLength="160"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
{errors.description && (
|
||||||
<span className="danger-text">
|
<span className="danger-text">{errors.description.message}</span>
|
||||||
{errors.description.message}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 text-center">
|
<div className="col-12 text-center">
|
||||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
<button
|
||||||
{formData.id && getValues("name")
|
type="submit"
|
||||||
|
className="btn btn-sm btn-primary me-3"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
{isPending
|
||||||
|
? "Please wait..."
|
||||||
|
: watchedId !== "0"
|
||||||
? "Edit Building"
|
? "Edit Building"
|
||||||
: "Add Building"}
|
: "Add Building"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="reset"
|
type="reset"
|
||||||
className="btn btn-sm btn-label-secondary"
|
className="btn btn-sm btn-label-secondary"
|
||||||
data-bs-dismiss="modal"
|
disabled={isPending}
|
||||||
aria-label="Close"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
reset(); // Call reset here
|
reset();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BuildingModel;
|
export default BuildingModel;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useMemo, useState } 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";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { useActivitiesMaster, useWorkCategoriesMaster } from "../../../hooks/masterHook/useMaster";
|
|
||||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
|
||||||
import {
|
import {
|
||||||
cacheData,
|
useActivitiesMaster,
|
||||||
clearCacheKey,
|
useWorkCategoriesMaster,
|
||||||
getCachedData,
|
} from "../../../hooks/masterHook/useMaster";
|
||||||
} from "../../../slices/apiDataManager";
|
import { useManageTask } from "../../../hooks/useProjects";
|
||||||
|
import { cacheData, getCachedData } from "../../../slices/apiDataManager";
|
||||||
import { refreshData } from "../../../slices/localVariablesSlice";
|
import { refreshData } from "../../../slices/localVariablesSlice";
|
||||||
import showToast from "../../../services/toastService";
|
import showToast from "../../../services/toastService";
|
||||||
|
|
||||||
@ -20,18 +18,13 @@ const taskSchema = z
|
|||||||
activityID: z.string().min(1, "Activity is required"),
|
activityID: z.string().min(1, "Activity is required"),
|
||||||
workCategoryId: z.string().min(1, "Work Category is required"),
|
workCategoryId: z.string().min(1, "Work Category is required"),
|
||||||
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||||
completedWork: z.number().min( 0, "Completed Work must be greater than 0" ),
|
completedWork: z.number().min(0, "Completed Work must be ≥ 0"),
|
||||||
comment:z.string()
|
comment: z.string(),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine((data) => data.completedWork <= data.plannedWork, {
|
||||||
(data) =>
|
|
||||||
data.completedWork === undefined ||
|
|
||||||
data.completedWork <= data.plannedWork,
|
|
||||||
{
|
|
||||||
message: "Completed Work cannot be greater than Planned Work",
|
message: "Completed Work cannot be greater than Planned Work",
|
||||||
path: ["completedWork"], // error will show next to this field
|
path: ["completedWork"],
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const EditActivityModal = ({
|
const EditActivityModal = ({
|
||||||
workItem,
|
workItem,
|
||||||
@ -39,29 +32,12 @@ const EditActivityModal = ({
|
|||||||
building,
|
building,
|
||||||
floor,
|
floor,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
} ) =>
|
||||||
const selectedProject = useSelector(
|
{
|
||||||
(store) => store.localVariables.projectId
|
|
||||||
);
|
|
||||||
const defaultModel = {
|
|
||||||
activityID: 0,
|
|
||||||
workCategoryId: 0,
|
|
||||||
plannedWork: 0,
|
|
||||||
completedWork: 0,
|
|
||||||
comment:""
|
|
||||||
};
|
|
||||||
|
|
||||||
const { projects_Details, refetch } = useProjectDetails(selectedProject);
|
const { activities, loading: loadingActivities } = useActivitiesMaster();
|
||||||
const [ActivityUnit, setActivityUnit] = useState("");
|
const { categories, loading: loadingCategories } = useWorkCategoriesMaster();
|
||||||
const { activities, loading, error } = useActivitiesMaster();
|
|
||||||
const { categories, categoryLoading, categoryError } =
|
|
||||||
useWorkCategoriesMaster();
|
|
||||||
const [formData, setFormData] = useState(defaultModel);
|
|
||||||
const [selectedActivity, setSelectedActivity] = useState(null);
|
const [selectedActivity, setSelectedActivity] = useState(null);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
const [activityData, setActivityData] = useState([]);
|
|
||||||
const [categoryData, setCategoryData] = useState([]);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -73,359 +49,204 @@ const EditActivityModal = ({
|
|||||||
watch,
|
watch,
|
||||||
} = useForm({
|
} = useForm({
|
||||||
resolver: zodResolver(taskSchema),
|
resolver: zodResolver(taskSchema),
|
||||||
defaultValues: defaultModel,
|
defaultValues: {
|
||||||
|
activityID: "",
|
||||||
|
workCategoryId: "",
|
||||||
|
plannedWork: 0,
|
||||||
|
completedWork: 0,
|
||||||
|
comment: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: UpdateTask, isPending } = useManageTask({
|
||||||
|
onSuccessCallback: () => onClose?.()
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleActivityChange = (e) => {
|
const activityID = watch("activityID");
|
||||||
const selectedId = String(e.target.value);
|
|
||||||
const selected = activityData.find((a) => a.id === selectedId);
|
const sortedActivities = useMemo(
|
||||||
|
() =>
|
||||||
|
[...(activities || [])].sort((a, b) =>
|
||||||
|
a.activityName?.localeCompare(b.activityName)
|
||||||
|
),
|
||||||
|
[activities]
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedCategories = useMemo(
|
||||||
|
() => [...(categories || [])].sort((a, b) => a.name?.localeCompare(b.name)),
|
||||||
|
[categories]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reset({
|
||||||
|
activityID: String(
|
||||||
|
workItem?.workItem?.activityId || workItem?.activityId || ""
|
||||||
|
),
|
||||||
|
workCategoryId: String(
|
||||||
|
workItem?.workItem?.workCategoryId || workItem?.workCategoryId || ""
|
||||||
|
),
|
||||||
|
plannedWork:
|
||||||
|
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
||||||
|
completedWork:
|
||||||
|
workItem?.workItem?.completedWork || workItem?.completedWork || 0,
|
||||||
|
comment: workItem?.workItem?.description || workItem?.description || "",
|
||||||
|
});
|
||||||
|
}, [workItem, activities]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const selected = activities?.find((a) => a.id === activityID);
|
||||||
setSelectedActivity(selected || null);
|
setSelectedActivity(selected || null);
|
||||||
setValue("activityID", selectedId);
|
}, [activityID, activities]);
|
||||||
};
|
|
||||||
|
|
||||||
const handleCategoryChange = (e) => {
|
const onSubmitForm = (data) =>
|
||||||
const selectedId = String(e.target.value);
|
|
||||||
const category = categoryData.find((b) => b.id === selectedId);
|
|
||||||
setSelectedCategory(category || null);
|
|
||||||
setValue("workCategoryId", selectedId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmitForm = async ( data ) =>
|
|
||||||
{
|
{
|
||||||
setIsSubmitting(true)
|
const payload = {
|
||||||
const updatedProject = { ...projects_Details };
|
|
||||||
const finalData = {
|
|
||||||
...data,
|
...data,
|
||||||
id: workItem?.workItem?.id ?? workItem?.id,
|
id: workItem?.workItem?.id ?? workItem?.id,
|
||||||
buildingID: building?.id,
|
buildingID: building?.id,
|
||||||
floorId: floor?.id,
|
floorId: floor?.id,
|
||||||
workAreaId: workArea?.id,
|
workAreaId: workArea?.id,
|
||||||
};
|
};
|
||||||
|
UpdateTask([payload])
|
||||||
ProjectRepository.manageProjectTasks([finalData])
|
|
||||||
.then((response) => {
|
|
||||||
if (response?.data[0]) {
|
|
||||||
const { workItemId, workItem } = response.data[0];
|
|
||||||
|
|
||||||
let finalUpdatedWorkItem = null;
|
|
||||||
const newProject = {
|
|
||||||
...updatedProject,
|
|
||||||
buildings: updatedProject.buildings.map((building) =>
|
|
||||||
building.id === finalData.buildingID
|
|
||||||
? {
|
|
||||||
...building,
|
|
||||||
floors: building.floors.map((floor) =>
|
|
||||||
floor.id === finalData.floorId
|
|
||||||
? {
|
|
||||||
...floor,
|
|
||||||
workAreas: floor.workAreas.map((workArea) =>
|
|
||||||
workArea.id === workItem?.workAreaId
|
|
||||||
? {
|
|
||||||
...workArea,
|
|
||||||
workItems: (() => {
|
|
||||||
const exists = workArea.workItems.some(
|
|
||||||
(item) =>
|
|
||||||
String(
|
|
||||||
item?.workItem?.id ?? item?.id
|
|
||||||
) === String(finalData.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
finalUpdatedWorkItem = workItem;
|
|
||||||
|
|
||||||
return exists
|
|
||||||
? workArea.workItems.map((item) =>
|
|
||||||
String(
|
|
||||||
item?.workItem?.id ?? item?.id
|
|
||||||
) === String(finalData.id)
|
|
||||||
? workItem
|
|
||||||
: item
|
|
||||||
)
|
|
||||||
: [...workArea.workItems, workItem];
|
|
||||||
})(),
|
|
||||||
}
|
}
|
||||||
: workArea
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: floor
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: building
|
|
||||||
),
|
|
||||||
};
|
|
||||||
cacheData("projectInfo", {
|
|
||||||
projectId: newProject.id,
|
|
||||||
data: newProject,
|
|
||||||
});
|
|
||||||
resetForm();
|
|
||||||
dispatch( refreshData( true ) );
|
|
||||||
setIsSubmitting(false)
|
|
||||||
showToast("Activity Updated Successfully","success")
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch( ( error ) =>
|
|
||||||
{
|
|
||||||
setIsSubmitting(false)
|
|
||||||
const message = error.response.data.message || error.message || "Error Occured During Api Call"
|
|
||||||
showToast( message, "error" );
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
setFormData(defaultModel);
|
|
||||||
setSelectedActivity(null);
|
|
||||||
reset(defaultModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reset({
|
|
||||||
activityID: workItem?.workItem?.activityId || workItem?.activityId || 0,
|
|
||||||
workCategoryId:
|
|
||||||
workItem?.workItem?.workCategoryId || workItem?.workCategoryId || 0,
|
|
||||||
plannedWork:
|
|
||||||
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
|
||||||
completedWork:
|
|
||||||
workItem?.workItem?.completedWork || workItem?.completedWork || 0,
|
|
||||||
comment:
|
|
||||||
workItem?.workItem?.description || workItem?.description || ""
|
|
||||||
});
|
|
||||||
return () => reset();
|
|
||||||
}, [activities, workItem]);
|
|
||||||
|
|
||||||
const ISselectedActivity = watch("activityID");
|
|
||||||
useEffect(() => {
|
|
||||||
if (ISselectedActivity) {
|
|
||||||
const selected = activities.find((a) => a.id === ISselectedActivity);
|
|
||||||
setSelectedActivity(selected || null);
|
|
||||||
setActivityUnit(selected?.unitOfMeasurement);
|
|
||||||
}
|
|
||||||
}, [ISselectedActivity, activities]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
<form className="row g-2 p-2 p-md-1" onSubmit={handleSubmit(onSubmitForm)}>
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="row">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-close"
|
|
||||||
aria-label="Close"
|
|
||||||
onClick={onClose}
|
|
||||||
/>
|
|
||||||
<div className="text-center mb-1">
|
<div className="text-center mb-1">
|
||||||
<h5 className="mb-1">Manage Task</h5>
|
<h5 className="mb-1">Manage Task</h5>
|
||||||
</div>
|
</div>
|
||||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
|
||||||
{/* Select Building */}
|
<div className="row g-2">
|
||||||
<div className="col-6 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<label className="form-label" htmlFor="buildingID">
|
<label className="form-label">Select Building</label>
|
||||||
Select Building
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
value={building?.name}
|
value={building?.buildingName}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Select Floor */}
|
<div className="col-12 col-md-6">
|
||||||
<div className="col-6 col-md-6">
|
<label className="form-label">Select Floor</label>
|
||||||
<label className="form-label" htmlFor="floorId">
|
|
||||||
Select Floor
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
value={floor?.floorName}
|
value={floor?.floorName}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-12">
|
<div className="col-12">
|
||||||
<label className="form-label" htmlFor="workAreaId">
|
<label className="form-label">Select Work Area</label>
|
||||||
Select Work Area
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
value={workArea?.areaName}
|
value={workArea?.areaName}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Select Activity */}
|
<div className="col-12">
|
||||||
<div className="col-12 col-md-12">
|
<label className="form-label">Select Activity</label>
|
||||||
<label className="form-label" htmlFor="activityID">
|
|
||||||
Select Activity
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
id="activityID"
|
|
||||||
className="form-select form-select-sm"
|
|
||||||
{...register("activityID")}
|
{...register("activityID")}
|
||||||
|
className="form-select form-select-sm"
|
||||||
>
|
>
|
||||||
{loading ? (
|
|
||||||
<option value="">Loading...</option>
|
|
||||||
) : (
|
|
||||||
<option disabled>Select Activity</option>
|
<option disabled>Select Activity</option>
|
||||||
)}
|
{loadingActivities ? (
|
||||||
{activities &&
|
<option>Loading...</option>
|
||||||
activities.length > 0 &&
|
) : (
|
||||||
activities
|
sortedActivities.map((a) => (
|
||||||
.slice()
|
<option key={a.id} value={a.id}>
|
||||||
.sort((a, b) =>
|
{a.activityName}
|
||||||
(a.activityName || "")?.localeCompare(
|
|
||||||
b.activityName || ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map((activity) => (
|
|
||||||
<option key={activity.id} value={activity.id}>
|
|
||||||
{activity.activityName}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))
|
||||||
{!loading && activities.length === 0 && (
|
|
||||||
<option disabled>No activities available</option>
|
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{errors.activityID && (
|
{errors.activityID && (
|
||||||
<p className="danger-text">{errors.activityID.message}</p>
|
<p className="danger-text">{errors.activityID.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Select Category */}
|
<div className="col-12">
|
||||||
<div className="col-12 col-md-12">
|
<label className="form-label">Select Work Category</label>
|
||||||
<label className="form-label" htmlFor="activityID">
|
|
||||||
Select Work Category
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
id="workCategoryId"
|
|
||||||
className="form-select form-select-sm"
|
|
||||||
{...register("workCategoryId")}
|
{...register("workCategoryId")}
|
||||||
|
className="form-select form-select-sm"
|
||||||
>
|
>
|
||||||
{loading ? (
|
|
||||||
<option value="">Loading...</option>
|
|
||||||
) : (
|
|
||||||
<option disabled>Select Category</option>
|
<option disabled>Select Category</option>
|
||||||
)}
|
{loadingCategories ? (
|
||||||
{categories &&
|
<option>Loading...</option>
|
||||||
categories.length > 0 &&
|
) : (
|
||||||
categories
|
sortedCategories.map((c) => (
|
||||||
.slice()
|
<option key={c.id} value={c.id}>
|
||||||
.sort((a, b) =>
|
{c.name}
|
||||||
(a.name || "")?.localeCompare(
|
|
||||||
b.name || ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map((category) => (
|
|
||||||
<option key={category.id} value={category.id}>
|
|
||||||
{category.name}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))
|
||||||
{!loading && categories.length === 0 && (
|
|
||||||
<option disabled>No categories available</option>
|
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{errors.workCategoryId && (
|
{errors.workCategoryId && (
|
||||||
<p className="danger-text">{errors.workCategoryId.message}</p>
|
<p className="danger-text">{errors.workCategoryId.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Planned Work */}
|
<div className="col-5">
|
||||||
{/* {ISselectedActivity && ( */}
|
<label className="form-label">Planned Work</label>
|
||||||
<div className="col-5 col-md-5">
|
|
||||||
<label className="form-label" htmlFor="plannedWork">
|
|
||||||
Planned Work
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
{...register("plannedWork", { valueAsNumber: true })}
|
{...register("plannedWork", { valueAsNumber: true })}
|
||||||
type="number"
|
type="number"
|
||||||
className="form-control form-control-sm me-2"
|
className="form-control form-control-sm"
|
||||||
placeholder="Planned Work"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{errors.plannedWork && (
|
{errors.plannedWork && (
|
||||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* )} */}
|
|
||||||
|
|
||||||
{/* Completed Work */}
|
<div className="col-5">
|
||||||
{/* {ISselectedActivity && ( */}
|
<label className="form-label">Completed Work</label>
|
||||||
<div className="col-5 col-md-5">
|
|
||||||
<label className="form-label" htmlFor="completedWork">
|
|
||||||
Completed Work
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
{...register("completedWork", { valueAsNumber: true })}
|
{...register("completedWork", { valueAsNumber: true })}
|
||||||
type="number"
|
type="number"
|
||||||
className="form-control form-control-sm me-2"
|
|
||||||
placeholder="Completed Work"
|
|
||||||
disabled={getValues("completedWork") > 0}
|
disabled={getValues("completedWork") > 0}
|
||||||
|
className="form-control form-control-sm"
|
||||||
/>
|
/>
|
||||||
{errors.completedWork && (
|
{errors.completedWork && (
|
||||||
<p className="danger-text">{errors.completedWork.message}</p>
|
<p className="danger-text">{errors.completedWork.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* )} */}
|
|
||||||
|
|
||||||
{/* Unit */}
|
<div className="col-2">
|
||||||
{/* {ISselectedActivity && ( */}
|
<label className="form-label">Unit</label>
|
||||||
<div className="col-2 col-md-2">
|
|
||||||
<label className="form-label" htmlFor="unit">
|
|
||||||
Unit
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
className="form-control form-control-sm"
|
||||||
disabled
|
disabled
|
||||||
className="form-control form-control-sm me-2"
|
|
||||||
value={selectedActivity?.unitOfMeasurement || ""}
|
value={selectedActivity?.unitOfMeasurement || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* )} */}
|
|
||||||
|
|
||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label
|
<label className="form-label">Comment</label>
|
||||||
className="form-text fs-7 m-1 text-lg text-dark"
|
<textarea {...register("comment")} rows="2" className="form-control" />
|
||||||
htmlFor="descriptionTextarea"
|
|
||||||
>
|
|
||||||
Comment
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
{...register("comment")}
|
|
||||||
className="form-control"
|
|
||||||
id="descriptionTextarea"
|
|
||||||
rows="2"
|
|
||||||
/>
|
|
||||||
{errors.comment && (
|
{errors.comment && (
|
||||||
<div className="danger-text">
|
<div className="danger-text">{errors.comment.message}</div>
|
||||||
{errors.comment.message}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 text-center">
|
<div className="col-12 text-center">
|
||||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={activities.length === 0 || isSubmitting}>
|
<button
|
||||||
{isSubmitting ? "Please Wait.." : "Edit Task"}
|
type="submit"
|
||||||
|
className="btn btn-sm btn-primary me-2"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
{isPending ? "Please Wait..." : "Edit Task"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-sm btn-label-secondary"
|
className="btn btn-sm btn-label-secondary"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
disabled={isPending}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,13 +16,7 @@ const Floor = ({ floor, workAreas, forBuilding }) => {
|
|||||||
<tr>
|
<tr>
|
||||||
<td colSpan="4" className="text-start table-cell">
|
<td colSpan="4" className="text-start table-cell">
|
||||||
<div className="row ps-2">
|
<div className="row ps-2">
|
||||||
{/* <div className="col-1 col-md-1 d-flex justify-content-between align-items-center " >
|
|
||||||
<button
|
|
||||||
className="btn me-2"
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div> */}
|
|
||||||
<div className="col-12 ps-8">
|
<div className="col-12 ps-8">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="d-flex col-5">
|
<div className="d-flex col-5">
|
||||||
|
@ -1,236 +1,190 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import showToast from "../../../services/toastService";
|
import showToast from "../../../services/toastService";
|
||||||
|
import { useManageProjectInfra } from "../../../hooks/useProjects";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
// Schema
|
||||||
const floorSchema = z.object({
|
const floorSchema = z.object({
|
||||||
buildingId: z
|
buildingId: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => val !== "0", {
|
.refine((val) => val !== "0", { message: "Building is required" }),
|
||||||
message: "Building is required",
|
|
||||||
}),
|
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
floorName: z.string().min(1, "Floor Name is required"),
|
floorName: z.string().min(1, "Floor Name is required"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultModel = {
|
const defaultValues = {
|
||||||
id: "0",
|
id: "0",
|
||||||
floorName: "",
|
floorName: "",
|
||||||
buildingId: "0",
|
buildingId: "0",
|
||||||
};
|
};
|
||||||
|
|
||||||
const FloorModel = ({
|
const FloorModel = ({ project, onClose, onSubmit }) => {
|
||||||
project,
|
const selectedProject = useSelector(
|
||||||
onClose,
|
(store) => store.localVariables.projectId
|
||||||
onSubmit,
|
);
|
||||||
clearTrigger,
|
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
||||||
onClearComplete,
|
|
||||||
}) => {
|
|
||||||
const [formData, setFormData] = useState(defaultModel);
|
|
||||||
const [selectedBuilding, setSelectedBuilding] = useState({});
|
|
||||||
const [buildings, setBuildings] = useState(project?.buildings || []);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setValue,
|
setValue,
|
||||||
reset,
|
reset,
|
||||||
|
watch,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm({
|
} = useForm({
|
||||||
|
defaultValues,
|
||||||
resolver: zodResolver(floorSchema),
|
resolver: zodResolver(floorSchema),
|
||||||
defaultValues: defaultModel,
|
});
|
||||||
|
const watchId = watch("id");
|
||||||
|
const watchBuildingId = watch("buildingId");
|
||||||
|
const { mutate: ManageFloor, isPending } = useManageProjectInfra({
|
||||||
|
onSuccessCallback: (data, variables) => {
|
||||||
|
showToast(
|
||||||
|
watchId != "0"
|
||||||
|
? "Floor updated Successfully"
|
||||||
|
: "Floor created Successfully",
|
||||||
|
"success"
|
||||||
|
);
|
||||||
|
reset({ Id: "0", name: "", description: "" });
|
||||||
|
onClose?.();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (clearTrigger) {
|
reset(defaultValues);
|
||||||
reset(defaultModel);
|
}, []);
|
||||||
onClearComplete();
|
useEffect(() => {
|
||||||
}
|
const building = project?.find((b) => b.id === watchBuildingId);
|
||||||
}, [clearTrigger, onClearComplete, reset]);
|
setSelectedBuilding(building || null);
|
||||||
|
}, [watchBuildingId, project]);
|
||||||
|
|
||||||
const handleBuildigChange = (e) => {
|
const handleBuildingChange = (e) => {
|
||||||
const buildingId = e.target.value;
|
const id = e.target.value;
|
||||||
const building = buildings.find((b) => b.id === String(buildingId));
|
setValue("buildingId", id, { shouldValidate: true });
|
||||||
|
|
||||||
if (building) {
|
|
||||||
setSelectedBuilding(building);
|
|
||||||
setFormData({
|
|
||||||
id: "",
|
|
||||||
floorName: "",
|
|
||||||
buildingId: building.id,
|
|
||||||
});
|
|
||||||
setValue("buildingId", building.id, { shouldValidate: true }); // ✅ trigger validation
|
|
||||||
setValue("id", "0");
|
setValue("id", "0");
|
||||||
} else {
|
setValue("floorName", "");
|
||||||
setSelectedBuilding({});
|
|
||||||
setFormData({
|
|
||||||
id: "",
|
|
||||||
floorName: "",
|
|
||||||
buildingId: "0",
|
|
||||||
});
|
|
||||||
setValue("buildingId", "0", { shouldValidate: true }); // ✅ trigger validation
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFloorChange = (e) => {
|
const handleFloorChange = (e) => {
|
||||||
const id = e.target.value;
|
const id = e.target.value;
|
||||||
const floor = selectedBuilding.floors?.find((b) => b.id === String(id));
|
setValue("id", id);
|
||||||
|
|
||||||
|
const floor = selectedBuilding?.floors?.find((f) => f.id === id);
|
||||||
if (floor) {
|
if (floor) {
|
||||||
setFormData({
|
|
||||||
id: floor.id,
|
|
||||||
floorName: floor.floorName,
|
|
||||||
buildingId: selectedBuilding.id,
|
|
||||||
});
|
|
||||||
setValue("floorName", floor.floorName);
|
setValue("floorName", floor.floorName);
|
||||||
} else {
|
} else {
|
||||||
setFormData({
|
|
||||||
id: "0",
|
|
||||||
floorName: "",
|
|
||||||
buildingId: selectedBuilding.id,
|
|
||||||
});
|
|
||||||
setValue("floorName", "");
|
setValue("floorName", "");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFormSubmit = (data) => {
|
const onFormSubmit = (data) => {
|
||||||
if (data.id === "0") {
|
const isEdit = data.id !== "0";
|
||||||
data.id = null;
|
const payload = {
|
||||||
}
|
...data,
|
||||||
|
id: isEdit ? data.id : null,
|
||||||
|
};
|
||||||
|
let infraObject = [
|
||||||
|
{
|
||||||
|
building: null,
|
||||||
|
floor: payload,
|
||||||
|
workArea: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
onSubmit(data);
|
ManageFloor({ infraObject, projectId: selectedProject });
|
||||||
reset({ floorName: "" });
|
|
||||||
|
|
||||||
if (data.id !== null) {
|
|
||||||
showToast("Floor updated successfully.", "success");
|
|
||||||
} else {
|
|
||||||
showToast("Floor created successfully.", "success");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="row">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-close"
|
|
||||||
aria-label="Close"
|
|
||||||
onClick={onClose}
|
|
||||||
/>
|
|
||||||
<div className="text-center mb-1">
|
|
||||||
<h5 className="mb-1">Manage Floors - {project.name}</h5>
|
|
||||||
</div>
|
|
||||||
<form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}>
|
<form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<div className="col-12 col-md-12">
|
<div className="text-center mb-1">
|
||||||
<label className="form-label" htmlFor="buildingId">
|
<h5 className="mb-1">Manage Floor</h5>
|
||||||
Select Building
|
</div>
|
||||||
</label>
|
<div className="col-12">
|
||||||
|
<label className="form-label">Select Building</label>
|
||||||
<select
|
<select
|
||||||
id="buildingId"
|
|
||||||
className="select2 form-select form-select-sm"
|
|
||||||
aria-label="Select Building"
|
|
||||||
{...register("buildingId")}
|
{...register("buildingId")}
|
||||||
onChange={handleBuildigChange}
|
className="form-select form-select-sm"
|
||||||
|
onChange={handleBuildingChange}
|
||||||
>
|
>
|
||||||
<option value="0">Select Building</option>
|
<option value="0">Select Building</option>
|
||||||
{buildings?.length > 0 &&
|
{project?.length > 0 &&
|
||||||
buildings
|
project
|
||||||
.filter((building) => building?.name)
|
.filter((b) => b.buildingName)
|
||||||
.sort((a, b) =>
|
.sort((a, b) => a.buildingName.localeCompare(b.buildingName))
|
||||||
(a.name || "")?.localeCompare(b.name || "")
|
.map((b) => (
|
||||||
)
|
<option key={b.id} value={b.id}>
|
||||||
.map((building) => (
|
{b.buildingName}
|
||||||
<option key={building.id} value={building.id}>
|
|
||||||
{building.name}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
{buildings?.length === 0 && (
|
|
||||||
<option disabled>No buildings found</option>
|
|
||||||
)}
|
|
||||||
</select>
|
</select>
|
||||||
{errors.buildingId && (
|
{errors.buildingId && (
|
||||||
<p className="text-danger">{errors.buildingId.message}</p>
|
<p className="text-danger">{errors.buildingId.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{formData.buildingId !== "0" && (
|
{watchBuildingId !== "0" && (
|
||||||
<>
|
<>
|
||||||
<div className="col-12 col-md-12">
|
<div className="col-12">
|
||||||
<label className="form-label">Select Floor</label>
|
<label className="form-label">Select Floor</label>
|
||||||
<select
|
<select
|
||||||
id="id"
|
|
||||||
className="select2 form-select form-select-sm"
|
|
||||||
aria-label="Select Floor"
|
|
||||||
{...register("id")}
|
{...register("id")}
|
||||||
|
className="form-select form-select-sm"
|
||||||
onChange={handleFloorChange}
|
onChange={handleFloorChange}
|
||||||
>
|
>
|
||||||
<option value="0">Add New Floor</option>
|
<option value="0">Add New Floor</option>
|
||||||
{selectedBuilding?.floors?.length > 0 &&
|
{selectedBuilding?.floors?.length > 0 &&
|
||||||
[...selectedBuilding.floors]
|
selectedBuilding.floors
|
||||||
.filter((floor) => floor?.floorName)
|
.filter((f) => f.floorName)
|
||||||
.sort((a, b) =>
|
.sort((a, b) => a.floorName.localeCompare(b.floorName))
|
||||||
(a.floorName || "")?.localeCompare(
|
.map((f) => (
|
||||||
b.floorName || ""
|
<option key={f.id} value={f.id}>
|
||||||
)
|
{f.floorName}
|
||||||
)
|
|
||||||
.map((floor) => (
|
|
||||||
<option key={floor.id} value={floor.id}>
|
|
||||||
{floor.floorName}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
{selectedBuilding?.floors?.length === 0 && (
|
|
||||||
<option disabled>No floors found</option>
|
|
||||||
)}
|
|
||||||
</select>
|
</select>
|
||||||
{errors.id && (
|
|
||||||
<p className="text-danger">{errors.id.message}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-12">
|
<div className="col-12">
|
||||||
<label className="form-label">
|
<label className="form-label">
|
||||||
{formData.id !== "0" ? "Modify " : "Enter "} Floor Name
|
{watchId !== "0" ? "Edit Floor Name" : "New Floor Name"}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
id="floorName"
|
|
||||||
className="form-control form-control-sm me-2"
|
|
||||||
placeholder="Floor Name"
|
|
||||||
{...register("floorName")}
|
{...register("floorName")}
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
placeholder="Floor Name"
|
||||||
/>
|
/>
|
||||||
{errors.floorName && (
|
{errors.floorName && (
|
||||||
<p className="text-danger">
|
<p className="text-danger">{errors.floorName.message}</p>
|
||||||
{errors.floorName.message}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="col-12 text-center">
|
<div className="col-12 text-center">
|
||||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
<button
|
||||||
{formData.id !== "0" && formData.id !== ""
|
type="submit"
|
||||||
|
className="btn btn-sm btn-primary me-3"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
{isPending
|
||||||
|
? "Please Wait"
|
||||||
|
: watchId !== "0"
|
||||||
? "Edit Floor"
|
? "Edit Floor"
|
||||||
: "Add Floor"}
|
: "Add Floor"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-sm btn-label-secondary"
|
className="btn btn-sm btn-label-secondary"
|
||||||
data-bs-dismiss="modal"
|
disabled={isPending}
|
||||||
aria-label="Close"
|
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,14 +100,7 @@ const InfraTable = ({ buildings, projectId, signalRHandler }) => {
|
|||||||
No floors have been added yet. Start by adding floors to manage
|
No floors have been added yet. Start by adding floors to manage
|
||||||
this building.
|
this building.
|
||||||
</p>
|
</p>
|
||||||
{/* <button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
onClick={() => handleAddFloor(building)}
|
|
||||||
>
|
|
||||||
<i className="bx bx-plus-circle me-2"></i>
|
|
||||||
Add Floors
|
|
||||||
</button> */}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import WorkItem from "./WorkItem";
|
import WorkItem from "./WorkItem";
|
||||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
import { useProjectDetails, useProjectTasks } from "../../../hooks/useProjects";
|
||||||
import { cacheData, getCachedData } from "../../../slices/apiDataManager";
|
import { cacheData } from "../../../slices/apiDataManager";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { refreshData } from "../../../slices/localVariablesSlice";
|
import { refreshData } from "../../../slices/localVariablesSlice";
|
||||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||||
@ -13,89 +13,67 @@ import {
|
|||||||
MANAGE_TASK,
|
MANAGE_TASK,
|
||||||
} from "../../../utils/constants";
|
} from "../../../utils/constants";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import ProgressDonutChart from "../../Charts/ProgressDonutChart";
|
|
||||||
import ProgressBar from "../../common/ProgressBar";
|
import ProgressBar from "../../common/ProgressBar";
|
||||||
import { componentsToColor } from "pdf-lib";
|
|
||||||
|
|
||||||
const WorkArea = ( {workArea, floor, forBuilding} ) =>
|
const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||||
{
|
const selectedProject = useSelector((store) => store.localVariables.projectId);
|
||||||
const selectedProject = useSelector( ( store ) => store.localVariables.projectId )
|
const { projects_Details, loading } = useProjectDetails(selectedProject);
|
||||||
const { projects_Details, loading, error, refetch } = useProjectDetails(
|
const [IsExpandedArea, setIsExpandedArea] = useState(false);
|
||||||
selectedProject
|
|
||||||
);
|
|
||||||
const [workItems, setWorkItems] = useState([]);
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [Project, setProject] = useState();
|
const [Project, setProject] = useState();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
|
||||||
const ManageTasks = useHasUserPermission(MANAGE_TASK);
|
|
||||||
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
||||||
const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK);
|
const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||||
|
|
||||||
|
const { ProjectTaskList, isLoading } = useProjectTasks(workArea.id, IsExpandedArea);
|
||||||
|
|
||||||
const [workAreaStatus, setWorkAreaStatus] = useState({
|
const [workAreaStatus, setWorkAreaStatus] = useState({
|
||||||
completed: 0,
|
completed: 0,
|
||||||
planned: 100,
|
planned: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const totalCompleted = workItems.reduce(
|
setProject(projects_Details);
|
||||||
(sum, i) => sum + (i.workItem?.completedWork || 0),
|
}, [projects_Details]);
|
||||||
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(() => {
|
useEffect(() => {
|
||||||
// const project = getCachedData("projectInfo");
|
const totalCompleted = ProjectTaskList?.reduce(
|
||||||
setProject( projects_Details );
|
(sum, i) => sum + (i?.workItem?.completedWork || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
if (!projects_Details || !forBuilding?.id || !floor?.id || !workArea?.id) return;
|
const totalPlanned = ProjectTaskList?.reduce(
|
||||||
const building = projects_Details.buildings?.find((b) => b.id === forBuilding.id);
|
(sum, i) => sum + (i?.workItem?.plannedWork || 0),
|
||||||
const floors = building?.floors?.find((f) => f.id === floor.id);
|
0
|
||||||
const workAreas = floor?.workAreas?.find((wa) => wa.id === workArea.id);
|
);
|
||||||
setWorkItems(workAreas?.workItems || []);
|
setWorkAreaStatus({ completed: totalCompleted, planned: totalPlanned });
|
||||||
}, [workArea, floor, floor,loading]);
|
}, [ProjectTaskList]);
|
||||||
|
|
||||||
const HanldeDeleteActivity = async (workItemId) => {
|
const HanldeDeleteActivity = async (workItemId) => {
|
||||||
try {
|
try {
|
||||||
const updatedProject = { ...Project.data };
|
const updatedProject = { ...Project.data };
|
||||||
const response = await ProjectRepository.deleteProjectTask(workItemId);
|
await ProjectRepository.deleteProjectTask(workItemId);
|
||||||
|
|
||||||
const newProject = {
|
const newProject = {
|
||||||
...updatedProject,
|
...updatedProject,
|
||||||
buildings: updatedProject?.buildings.map((building) =>
|
buildings: updatedProject?.buildings.map((building) => ({
|
||||||
building?.id === building?.id
|
|
||||||
? {
|
|
||||||
...building,
|
...building,
|
||||||
floors: building?.floors?.map((floor) =>
|
floors: building?.floors.map((floor) => ({
|
||||||
floor.id === floor?.id
|
|
||||||
? {
|
|
||||||
...floor,
|
...floor,
|
||||||
workAreas: floor.workAreas.map((workArea) =>
|
workAreas: floor.workAreas.map((wa) =>
|
||||||
workArea.id === workArea?.id
|
wa.id === workArea.id
|
||||||
? {
|
? {
|
||||||
...workArea,
|
...wa,
|
||||||
workItems: workArea.workItems.filter(
|
workItems: wa.workItems.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
String(item?.workItem?.id ?? item?.id) !==
|
String(item?.workItem?.id ?? item?.id) !==
|
||||||
String(workItemId)
|
String(workItemId)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: workArea
|
: wa
|
||||||
),
|
|
||||||
}
|
|
||||||
: floor
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: building
|
|
||||||
),
|
),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
cacheData("projectInfo", {
|
cacheData("projectInfo", {
|
||||||
@ -104,131 +82,93 @@ const WorkArea = ( {workArea, floor, forBuilding} ) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
dispatch(refreshData(true));
|
dispatch(refreshData(true));
|
||||||
|
|
||||||
showToast("Activity Deleted Successfully", "success");
|
showToast("Activity Deleted Successfully", "success");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message =
|
const message =
|
||||||
error.response?.data?.message ||
|
error.response?.data?.message || error.message || "An error occurred";
|
||||||
error.message ||
|
|
||||||
"An unexpected error occurred";
|
|
||||||
showToast(message, "error");
|
showToast(message, "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const toggleButtons = document.querySelectorAll(".accordion-button");
|
const collapseElement = document.getElementById(`collapse-${workArea.id}`);
|
||||||
|
|
||||||
toggleButtons.forEach((btn) => {
|
const handleShown = () => setIsExpandedArea(true);
|
||||||
const icon = btn.querySelector(".toggle-icon");
|
const handleHidden = () => setIsExpandedArea(false);
|
||||||
|
|
||||||
btn.addEventListener("click", () => {
|
collapseElement?.addEventListener("shown.bs.collapse", handleShown);
|
||||||
setTimeout(() => {
|
collapseElement?.addEventListener("hidden.bs.collapse", handleHidden);
|
||||||
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 () => {
|
return () => {
|
||||||
toggleButtons.forEach((btn) => {
|
collapseElement?.removeEventListener("shown.bs.collapse", handleShown);
|
||||||
btn.removeEventListener("click", () => {});
|
collapseElement?.removeEventListener("hidden.bs.collapse", handleHidden);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, [workArea.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={workArea.id}>
|
<React.Fragment key={workArea.id}>
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="4" className="p-0">
|
<td colSpan="4" className="p-0">
|
||||||
<div
|
<div className="accordion border-none" id={`accordion-${workArea.id}`}>
|
||||||
className="accordion border-none"
|
|
||||||
id={`accordion-${workArea.id}`}
|
|
||||||
>
|
|
||||||
<div className="accordion-item background border-0">
|
<div className="accordion-item background border-0">
|
||||||
{/* Accordion Header */}
|
<p className="accordion-header mb-0" id={`heading-${workArea.id}`}>
|
||||||
<p
|
|
||||||
className="accordion-header mb-0"
|
|
||||||
id={`heading-${workArea.id}`}
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
className={`accordion-button text-start px-2 py-2 custom-accordion-btn ${
|
className="accordion-button text-start px-2 py-2 custom-accordion-btn collapsed"
|
||||||
workItems && workItems.length > 0 ? "collapsed" : "disabled"
|
|
||||||
}`}
|
|
||||||
type="button"
|
type="button"
|
||||||
data-bs-toggle={
|
data-bs-toggle="collapse"
|
||||||
workItems && workItems.length > 0 ? "collapse" : ""
|
data-bs-target={`#collapse-${workArea.id}`}
|
||||||
}
|
|
||||||
data-bs-target={
|
|
||||||
workItems && workItems.length > 0
|
|
||||||
? `#collapse-${workArea.id}`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-controls={`collapse-${workArea.id}`}
|
aria-controls={`collapse-${workArea.id}`}
|
||||||
disabled={!(workItems && workItems.length > 0)}
|
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className={`bx me-2 toggle-icon ${
|
className={`bx me-2 toggle-icon ${
|
||||||
workItems && workItems.length > 0
|
IsExpandedArea ? "bx-minus-circle" : "bx-plus-circle"
|
||||||
? "bx-plus-circle"
|
|
||||||
: "bx-block"
|
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
fontSize: "1.2rem",
|
fontSize: "1.2rem",
|
||||||
color:
|
color: "black",
|
||||||
workItems && workItems.length > 0 ? "" : "transparent",
|
|
||||||
}}
|
}}
|
||||||
></i>
|
></i>
|
||||||
|
|
||||||
<div className="d-flex justify-content-start row w-100 align-items-center">
|
<div className="d-flex justify-content-start row w-100 align-items-center">
|
||||||
<div className="d-flex col-5">
|
<div className="d-flex col-5">
|
||||||
<span className="fw-semibold text-primary small">
|
<span className="fw-semibold text-primary small">Floor:</span>
|
||||||
Floor:
|
|
||||||
</span>
|
|
||||||
<span className="fw-normal text-darkgreen small px-2">
|
<span className="fw-normal text-darkgreen small px-2">
|
||||||
{floor.floorName}
|
{floor.floorName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-start col-5">
|
<div className="text-start col-5">
|
||||||
<span className="fw-semibold text-primary small">
|
<span className="fw-semibold text-primary small">Work Area:</span>
|
||||||
Work Area:
|
|
||||||
</span>
|
|
||||||
<span className="fw-normal text-darkgreen small px-2">
|
<span className="fw-normal text-darkgreen small px-2">
|
||||||
{workArea.areaName}
|
{workArea.areaName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{workArea?.workItems?.length > 0 && (
|
{ProjectTaskList?.length > 0 && (
|
||||||
<div className="col-2">
|
<div className="col-2">
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
completedWork={workAreaStatus.completed}
|
completedWork={workAreaStatus.completed}
|
||||||
plannedWork={workAreaStatus.planned}
|
plannedWork={workAreaStatus.planned}
|
||||||
className="m-0 text-info"
|
className="m-0 text-info"
|
||||||
></ProgressBar>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Accordion Body */}
|
|
||||||
{workItems && workItems.length > 0 && (
|
|
||||||
<div
|
<div
|
||||||
id={`collapse-${workArea.id}`}
|
id={`collapse-${workArea.id}`}
|
||||||
className="accordion-collapse collapse"
|
className="accordion-collapse collapse"
|
||||||
aria-labelledby={`heading-${workArea.id}`}
|
aria-labelledby={`heading-${workArea.id}`}
|
||||||
>
|
>
|
||||||
<div className="accordion-body px-1">
|
<div className="accordion-body px-1">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="text-center py-2 text-muted">Loading activities...</div>
|
||||||
|
) : ProjectTaskList?.length > 0 ? (
|
||||||
<table className="table table-sm mx-1">
|
<table className="table table-sm mx-1">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="infra-activity-table-header-first">
|
<th className="infra-activity-table-header-first">Activity</th>
|
||||||
Activity
|
|
||||||
</th>
|
|
||||||
<th className="infra-activity-table-header d-sm-table-cell d-md-none">
|
<th className="infra-activity-table-header d-sm-table-cell d-md-none">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
@ -241,11 +181,8 @@ const WorkArea = ( {workArea, floor, forBuilding} ) =>
|
|||||||
<th className="infra-activity-table-header d-none d-md-table-cell">
|
<th className="infra-activity-table-header d-none d-md-table-cell">
|
||||||
Today's Planned
|
Today's Planned
|
||||||
</th>
|
</th>
|
||||||
<th className="infra-activity-table-header">
|
<th className="infra-activity-table-header">Progress</th>
|
||||||
Progress
|
{(ManageInfra || (!projectId && ManageAndAssignTak)) && (
|
||||||
</th>
|
|
||||||
{(ManageInfra ||
|
|
||||||
(!projectId && ManageAndAssignTak)) && (
|
|
||||||
<th className="infra-activity-table-header text-end">
|
<th className="infra-activity-table-header text-end">
|
||||||
<span className="px-2">Actions</span>
|
<span className="px-2">Actions</span>
|
||||||
</th>
|
</th>
|
||||||
@ -253,7 +190,7 @@ const WorkArea = ( {workArea, floor, forBuilding} ) =>
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="table-border-bottom-0">
|
<tbody className="table-border-bottom-0">
|
||||||
{workArea.workItems.map((workItem) => (
|
{ProjectTaskList.map((workItem) => (
|
||||||
<WorkItem
|
<WorkItem
|
||||||
key={workItem.workItemId}
|
key={workItem.workItemId}
|
||||||
workItem={workItem}
|
workItem={workItem}
|
||||||
@ -265,14 +202,19 @@ const WorkArea = ( {workArea, floor, forBuilding} ) =>
|
|||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
) : (
|
||||||
|
<div className="text-center text-muted py-3">
|
||||||
|
No activities available for this work area.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WorkArea;
|
export default WorkArea;
|
||||||
|
@ -79,8 +79,7 @@ const WorkItem = ({
|
|||||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const showModal1 = () => setShowModal(true);
|
|
||||||
const closeModal1 = () => setShowModal(false);
|
|
||||||
const showModalDelete = () => setShowModal2(true);
|
const showModalDelete = () => setShowModal2(true);
|
||||||
const closeModalDelete = () => setShowModal2(false);
|
const closeModalDelete = () => setShowModal2(false);
|
||||||
|
|
||||||
@ -105,21 +104,15 @@ const WorkItem = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<div
|
<GlobalModel isOpen={showModal} size="lg" closeModal={()=>setShowModal(false)}>
|
||||||
className={`modal fade ${showModal ? "show" : ""}`}
|
|
||||||
tabIndex="-1"
|
|
||||||
role="dialog"
|
|
||||||
style={{ display: showModal ? "block" : "none" }}
|
|
||||||
aria-hidden={!showModal}
|
|
||||||
>
|
|
||||||
<EditActivityModal
|
<EditActivityModal
|
||||||
workItem={workItem}
|
workItem={workItem}
|
||||||
workArea={forWorkArea}
|
workArea={forWorkArea}
|
||||||
building={forBuilding}
|
building={forBuilding}
|
||||||
floor={forFloor}
|
floor={forFloor}
|
||||||
onClose={closeModal1}
|
onClose={()=>setShowModal(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showModal2 && (
|
{showModal2 && (
|
||||||
@ -169,7 +162,7 @@ const WorkItem = ({
|
|||||||
: "NA"}
|
: "NA"}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Category - visible on medium and above */}
|
|
||||||
<td className="text-center table-cell-small d-none d-md-table-cell">
|
<td className="text-center table-cell-small d-none d-md-table-cell">
|
||||||
<span className="fw-light">
|
<span className="fw-light">
|
||||||
{hasWorkItem
|
{hasWorkItem
|
||||||
@ -255,7 +248,7 @@ const WorkItem = ({
|
|||||||
<i
|
<i
|
||||||
className="bx bxs-edit text-secondary cursor-pointer"
|
className="bx bxs-edit text-secondary cursor-pointer"
|
||||||
title="Edit"
|
title="Edit"
|
||||||
onClick={showModal1}
|
onClick={()=>setShowModal(true)}
|
||||||
role="button"
|
role="button"
|
||||||
></i>
|
></i>
|
||||||
<i
|
<i
|
||||||
@ -297,7 +290,7 @@ const WorkItem = ({
|
|||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className="dropdown-item d-flex align-items-center"
|
className="dropdown-item d-flex align-items-center"
|
||||||
onClick={showModal1}
|
onClick={()=>setShowModal(true) }
|
||||||
>
|
>
|
||||||
<i className="bx bxs-edit text-secondary me-2"></i> Edit
|
<i className="bx bxs-edit text-secondary me-2"></i> Edit
|
||||||
</a>
|
</a>
|
||||||
|
@ -17,42 +17,39 @@ import {
|
|||||||
clearCacheKey,
|
clearCacheKey,
|
||||||
getCachedData,
|
getCachedData,
|
||||||
} from "../../slices/apiDataManager";
|
} from "../../slices/apiDataManager";
|
||||||
import { useProjectDetails } from "../../hooks/useProjects";
|
import { useProjectDetails, useProjectInfra } from "../../hooks/useProjects";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { refreshData } from "../../slices/localVariablesSlice";
|
import { refreshData } from "../../slices/localVariablesSlice";
|
||||||
import eventBus from "../../services/eventBus";
|
import eventBus from "../../services/eventBus";
|
||||||
|
import {useParams} from "react-router-dom";
|
||||||
|
import GlobalModel from "../common/GlobalModel";
|
||||||
|
|
||||||
const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
|
||||||
|
{
|
||||||
|
const {projectId} = useParams()
|
||||||
const reloadedData = useSelector((store) => store.localVariables.reload);
|
const reloadedData = useSelector((store) => store.localVariables.reload);
|
||||||
const [ expandedBuildings, setExpandedBuildings ] = useState( [] );
|
const [ expandedBuildings, setExpandedBuildings ] = useState( [] );
|
||||||
|
const {projectInfra,isLoading,error} = useProjectInfra(projectId)
|
||||||
const { projects_Details, refetch, loading } = useProjectDetails(data?.id);
|
const { projects_Details, refetch, loading } = useProjectDetails(data?.id);
|
||||||
const [ project, setProject ] = useState( projects_Details );
|
const [ project, setProject ] = useState( projects_Details );
|
||||||
const [modalConfig, setModalConfig] = useState({ type: null, data: null });
|
const [modalConfig, setModalConfig] = useState({ type: null, data: null });
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
||||||
|
|
||||||
const [isBuildingModalOpen, setIsBuildingModalOpen] = useState(false);
|
const [isBuildingModalOpen, setIsBuildingModalOpen] = useState(false);
|
||||||
const [isFloorModalOpen, setIsFloorModalOpen] = useState(false);
|
const [showModalFloor, setshowModalFloor] = useState(false);
|
||||||
const [isWorkAreaModelOpen, setIsWorkAreaModalOpen] = useState(false);
|
const [isWorkAreaModelOpen, setIsWorkAreaModalOpen] = useState(false);
|
||||||
const [isTaskModelOpen, setIsTaskModalOpen] = useState(false);
|
const [isTaskModelOpen, setIsTaskModalOpen] = useState(false);
|
||||||
const [isAssignRoleModal, setIsAssingRoleModal] = useState(false);
|
const [isAssignRoleModal, setIsAssingRoleModal] = useState(false);
|
||||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||||
const [clearFormTrigger, setClearFormTrigger] = useState(false);
|
const [clearFormTrigger, setClearFormTrigger] = useState(false);
|
||||||
const [CurrentBuilding, setCurrentBuilding] = useState("");
|
const [CurrentBuilding, setCurrentBuilding] = useState("");
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModalBuilding, setshowModalBuilding] = useState(false);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setProject(projects_Details);
|
setProject(projectInfra);
|
||||||
}, [data, projects_Details]);
|
}, [data, projects_Details]);
|
||||||
|
|
||||||
const openFloorModel = (projectData) => {
|
|
||||||
setIsFloorModalOpen(true);
|
|
||||||
};
|
|
||||||
const closeFloorModel = () => {
|
|
||||||
setIsFloorModalOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openAssignModel = (assignData) => {
|
const openAssignModel = (assignData) => {
|
||||||
setCurrentBuilding(assignData);
|
setCurrentBuilding(assignData);
|
||||||
setIsAssingRoleModal(true);
|
setIsAssingRoleModal(true);
|
||||||
@ -65,29 +62,6 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
setIsBuildingModalOpen(false);
|
setIsBuildingModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBuildingModelFormSubmit = (buildingmodel) => {
|
|
||||||
if (buildingmodel.id == "" || buildingmodel.id == 0)
|
|
||||||
delete buildingmodel.id;
|
|
||||||
let data = [
|
|
||||||
{
|
|
||||||
building: buildingmodel,
|
|
||||||
floor: null,
|
|
||||||
workArea: null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
submitData(data);
|
|
||||||
};
|
|
||||||
const handleFloorModelFormSubmit = (updatedFloor) => {
|
|
||||||
if (updatedFloor.id == "") delete updatedFloor.id;
|
|
||||||
submitData([
|
|
||||||
{
|
|
||||||
building: null,
|
|
||||||
floor: updatedFloor,
|
|
||||||
workArea: null,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openWorkAreaModel = (projectData) => {
|
const openWorkAreaModel = (projectData) => {
|
||||||
setIsWorkAreaModalOpen(true);
|
setIsWorkAreaModalOpen(true);
|
||||||
};
|
};
|
||||||
@ -322,23 +296,20 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
|
|
||||||
const modalElement = document.getElementById("building-model");
|
const modalElement = document.getElementById("building-model");
|
||||||
if (modalElement) {
|
if (modalElement) {
|
||||||
modalElement.classList.remove("show"); // Remove modal visibility class
|
modalElement.classList.remove("show");
|
||||||
modalElement.style.display = "none"; // Hide the modal element
|
modalElement.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.remove("modal-open"); // Remove modal-open class from body
|
document.body.classList.remove("modal-open");
|
||||||
|
|
||||||
// Remove the modal backdrop
|
|
||||||
const backdropElement = document.querySelector(".modal-backdrop");
|
const backdropElement = document.querySelector(".modal-backdrop");
|
||||||
if (backdropElement) {
|
if (backdropElement) {
|
||||||
backdropElement.classList.remove("modal-backdrop"); // Remove backdrop class
|
backdropElement.classList.remove("modal-backdrop");
|
||||||
backdropElement.style.display = "none"; // Hide the backdrop element
|
backdropElement.style.display = "none";
|
||||||
}
|
}
|
||||||
document.body.style.overflow = "auto";
|
document.body.style.overflow = "auto";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShow = () => setShowModal(true);
|
|
||||||
const handleClose = () => setShowModal(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (reloadedData) {
|
if (reloadedData) {
|
||||||
refetch();
|
refetch();
|
||||||
@ -352,39 +323,21 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
|
||||||
className={`modal fade ${showModal ? "show" : ""}`}
|
{showModalBuilding && <GlobalModel isOpen={showModalBuilding} size="md" closeModal={() => setshowModalBuilding( false )}>
|
||||||
tabIndex="-1"
|
|
||||||
role="dialog"
|
|
||||||
style={{ display: showModal ? "block" : "none" }}
|
|
||||||
aria-hidden={!showModal}
|
|
||||||
>
|
|
||||||
<BuildingModel
|
<BuildingModel
|
||||||
project={project}
|
project={projectInfra}
|
||||||
onClose={handleClose}
|
onClose={() => setshowModalBuilding( false )}
|
||||||
onSubmit={handleBuildingModelFormSubmit}
|
|
||||||
clearTrigger={clearFormTrigger}
|
|
||||||
onClearComplete={() => setClearFormTrigger(false)}
|
|
||||||
></BuildingModel>
|
|
||||||
</div>
|
|
||||||
{isFloorModalOpen && (
|
|
||||||
<div
|
|
||||||
className="modal fade show"
|
|
||||||
id="floor-model"
|
|
||||||
tabIndex="-1"
|
|
||||||
role="dialog"
|
|
||||||
style={{ display: "block" }}
|
|
||||||
aria-hidden="false"
|
|
||||||
>
|
|
||||||
<FloorModel
|
|
||||||
project={project}
|
|
||||||
onClose={closeFloorModel}
|
|
||||||
onSubmit={handleFloorModelFormSubmit}
|
|
||||||
clearTrigger={clearFormTrigger}
|
|
||||||
onClearComplete={() => setClearFormTrigger(false)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</GlobalModel>}
|
||||||
)}
|
{showModalFloor && <GlobalModel isOpen={showModalFloor} size="md" closeModal={()=>setshowModalFloor(false)}>
|
||||||
|
<FloorModel
|
||||||
|
project={projectInfra}
|
||||||
|
onClose={()=>setshowModalFloor(false)}
|
||||||
|
/>
|
||||||
|
</GlobalModel>}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{isWorkAreaModelOpen && (
|
{isWorkAreaModelOpen && (
|
||||||
<div
|
<div
|
||||||
@ -398,8 +351,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
<WorkAreaModel
|
<WorkAreaModel
|
||||||
project={project}
|
project={project}
|
||||||
onClose={closeWorkAreaModel}
|
onClose={closeWorkAreaModel}
|
||||||
onSubmit={handleWorkAreaModelFormSubmit}
|
|
||||||
clearTrigger={clearFormTrigger}
|
|
||||||
onClearComplete={() => setClearFormTrigger(false)}
|
onClearComplete={() => setClearFormTrigger(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -418,7 +370,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
project={project}
|
project={project}
|
||||||
onClose={closeTaskModel}
|
onClose={closeTaskModel}
|
||||||
onSubmit={handleTaskModelFormSubmit}
|
onSubmit={handleTaskModelFormSubmit}
|
||||||
clearTrigger={clearFormTrigger}
|
|
||||||
onClearComplete={() => setClearFormTrigger(false)}
|
onClearComplete={() => setClearFormTrigger(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -441,7 +393,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="link-button link-button-sm m-1 "
|
className="link-button link-button-sm m-1 "
|
||||||
onClick={handleShow}
|
onClick={()=>setshowModalBuilding(true)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-plus-circle me-2"></i>
|
<i className="bx bx-plus-circle me-2"></i>
|
||||||
Manage Building
|
Manage Building
|
||||||
@ -449,7 +401,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="link-button m-1"
|
className="link-button m-1"
|
||||||
onClick={() => openFloorModel()}
|
onClick={()=>setshowModalFloor(true)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-plus-circle me-2"></i>
|
<i className="bx bx-plus-circle me-2"></i>
|
||||||
Manage Floors
|
Manage Floors
|
||||||
@ -473,15 +425,16 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="row ">
|
<div className="row ">
|
||||||
{loading && <p>Loading....</p>}
|
{isLoading && <p>Loading....</p>}
|
||||||
{project && project.buildings?.length > 0 && (
|
{projectInfra && projectInfra?.length > 0 && (
|
||||||
<InfraTable
|
<InfraTable
|
||||||
buildings={project?.buildings}
|
buildings={projectInfra}
|
||||||
projectId={project.id}
|
projectId={projectId}
|
||||||
handleFloor={submitData}
|
handleFloor={submitData}
|
||||||
signalRHandler ={signalRHandler}
|
signalRHandler ={signalRHandler}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!isLoading && projectInfra?.length == 0 && <div className="mt-5"><p>No Infra Avaiable</p></div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,6 @@ const ProjectModal = ({modalConfig,closeModal}) => {
|
|||||||
></button>
|
></button>
|
||||||
<div className="text-center mb-2"></div>
|
<div className="text-center mb-2"></div>
|
||||||
|
|
||||||
{/* Modal Component */}
|
|
||||||
|
|
||||||
{modalConfig?.type === "assignRole" && <AssignRole assignData={modalConfig?.data} onClose={closeModal} />}
|
{modalConfig?.type === "assignRole" && <AssignRole assignData={modalConfig?.data} onClose={closeModal} />}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
|||||||
className={`nav-link ${activePill === "profile" ? "active" : ""}`}
|
className={`nav-link ${activePill === "profile" ? "active" : ""}`}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault(); // Prevent page reload
|
e.preventDefault();
|
||||||
onPillClick("profile");
|
onPillClick("profile");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -29,7 +29,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
|||||||
className={`nav-link ${activePill === "teams" ? "active" : ""}`}
|
className={`nav-link ${activePill === "teams" ? "active" : ""}`}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault(); // Prevent page reload
|
e.preventDefault();
|
||||||
onPillClick("teams");
|
onPillClick("teams");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -41,27 +41,14 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
|||||||
className={`nav-link ${activePill === "infra" ? "active" : ""}`}
|
className={`nav-link ${activePill === "infra" ? "active" : ""}`}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault(); // Prevent page reload
|
e.preventDefault();
|
||||||
onPillClick("infra");
|
onPillClick("infra");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="bx bx-grid-alt bx-sm me-1_5"></i> <span className="d-none d-md-inline">Infrastructure</span>
|
<i className="bx bx-grid-alt bx-sm me-1_5"></i> <span className="d-none d-md-inline">Infrastructure</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{/* <li className="nav-item">
|
|
||||||
<a
|
|
||||||
className={`nav-link ${
|
|
||||||
activePill === "workplan" ? "active" : ""
|
|
||||||
}`}
|
|
||||||
href="#"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault(); // Prevent page reload
|
|
||||||
onPillClick("workplan");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="bx bx-link bx-sm me-1_5"></i> Work Plan
|
|
||||||
</a>
|
|
||||||
</li> */}
|
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a
|
<a
|
||||||
className={`nav-link ${
|
className={`nav-link ${
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {useEmployeesByProjectAllocated, useProjects} from "../../hooks/useProjects";
|
import {useEmployeesByProjectAllocated, useProjects} from "../../hooks/useProjects";
|
||||||
|
|
||||||
const ProjectOverview = ({project}) =>
|
const ProjectOverview = ({project}) =>{
|
||||||
{
|
|
||||||
const {projects} = useProjects()
|
const {projects} = useProjects()
|
||||||
|
|
||||||
const project_detail = projects.find( ( pro ) => pro.id == project )
|
const project_detail = projects.find( ( pro ) => pro.id == project )
|
||||||
return (
|
return (
|
||||||
<div className="card mb-6">
|
<div className="card mb-6">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import MapUsers from "./MapUsers";
|
import MapUsers from "./MapUsers";
|
||||||
import { Link, NavLink, useNavigate } from "react-router-dom";
|
import { Link, NavLink, useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
import showToast from "../../services/toastService";
|
import showToast from "../../services/toastService";
|
||||||
import Avatar from "../common/Avatar";
|
import Avatar from "../common/Avatar";
|
||||||
@ -16,7 +16,9 @@ import ConfirmModal from "../common/ConfirmModal";
|
|||||||
import eventBus from "../../services/eventBus";
|
import eventBus from "../../services/eventBus";
|
||||||
import {useEmployeesByProjectAllocated, useManageProjectAllocation} from "../../hooks/useProjects";
|
import {useEmployeesByProjectAllocated, useManageProjectAllocation} from "../../hooks/useProjects";
|
||||||
|
|
||||||
const Teams = ({ project }) => {
|
const Teams = () =>
|
||||||
|
{
|
||||||
|
const {projectId} = useParams()
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { data, loading } = useMaster();
|
const { data, loading } = useMaster();
|
||||||
@ -35,7 +37,7 @@ const Teams = ({ project }) => {
|
|||||||
|
|
||||||
const HasAssignUserPermission = useHasUserPermission( ASSIGN_TO_PROJECT );
|
const HasAssignUserPermission = useHasUserPermission( ASSIGN_TO_PROJECT );
|
||||||
const [ IsDeleteModal, setIsDeleteModal ] = useState( false )
|
const [ IsDeleteModal, setIsDeleteModal ] = useState( false )
|
||||||
const {projectEmployees, loading:employeeLodaing, refetch} = useEmployeesByProjectAllocated( project.id )
|
const {projectEmployees, loading:employeeLodaing, refetch} = useEmployeesByProjectAllocated( projectId )
|
||||||
const {
|
const {
|
||||||
mutate: submitAllocations,
|
mutate: submitAllocations,
|
||||||
isPending,
|
isPending,
|
||||||
@ -216,11 +218,11 @@ const {
|
|||||||
|
|
||||||
const handler = useCallback(
|
const handler = useCallback(
|
||||||
(msg) => {
|
(msg) => {
|
||||||
if (msg.projectIds.some((item) => item === project.id)) {
|
if (msg.projectIds.some((item) => item === projectId)) {
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[project.id, refetch]
|
[projectId, refetch]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -251,7 +253,7 @@ const {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<MapUsers
|
<MapUsers
|
||||||
projectId={project?.id}
|
projectId={projectId}
|
||||||
onClose={onModelClose}
|
onClose={onModelClose}
|
||||||
empJobRoles={empJobRoles}
|
empJobRoles={empJobRoles}
|
||||||
onSubmit={handleEmpAlicationFormSubmit}
|
onSubmit={handleEmpAlicationFormSubmit}
|
||||||
|
@ -236,7 +236,11 @@ export const useEmployeesByProjectAllocated = (selectedProject) =>
|
|||||||
const res = await ProjectRepository.getProjectAllocation( selectedProject );
|
const res = await ProjectRepository.getProjectAllocation( selectedProject );
|
||||||
return res.data || res
|
return res.data || res
|
||||||
},
|
},
|
||||||
enabled:!!selectedProject
|
enabled: !!selectedProject,
|
||||||
|
onError: ( error ) =>
|
||||||
|
{
|
||||||
|
showToast(error.message || "Error while Fetching project Allocated Employees", "error");
|
||||||
|
}
|
||||||
} )
|
} )
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -256,7 +260,11 @@ export const useProjectDetails = ( projectId,isAuto = true ) =>
|
|||||||
const res = await ProjectRepository.getProjectByprojectId( projectId );
|
const res = await ProjectRepository.getProjectByprojectId( projectId );
|
||||||
return res.data || res;
|
return res.data || res;
|
||||||
},
|
},
|
||||||
enabled:!!projectId && isAuto
|
enabled: !!projectId && isAuto,
|
||||||
|
onError: ( error ) =>
|
||||||
|
{
|
||||||
|
showToast(error.message || "Error while Fetching project Details", "error");
|
||||||
|
}
|
||||||
} )
|
} )
|
||||||
return { projects_Details, loading:isLoading, error, refetch };
|
return { projects_Details, loading:isLoading, error, refetch };
|
||||||
}
|
}
|
||||||
@ -270,7 +278,11 @@ export const useProjectsByEmployee = (employeeId) =>
|
|||||||
const res = await ProjectRepository.getProjectsByEmployee( employeeId );
|
const res = await ProjectRepository.getProjectsByEmployee( employeeId );
|
||||||
return res.data || res;
|
return res.data || res;
|
||||||
},
|
},
|
||||||
enabled: !!employeeId
|
enabled: !!employeeId,
|
||||||
|
onError: ( error ) =>
|
||||||
|
{
|
||||||
|
showToast(error.message || "Error while Fetching project Employee", "error");
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return {projectList, loading:isLoading,error,refetch }
|
return {projectList, loading:isLoading,error,refetch }
|
||||||
}
|
}
|
||||||
@ -284,12 +296,56 @@ export const useProjectName = () =>
|
|||||||
const res = await ProjectRepository.projectNameList();
|
const res = await ProjectRepository.projectNameList();
|
||||||
return res.data || res;
|
return res.data || res;
|
||||||
},
|
},
|
||||||
|
onError: ( error ) =>
|
||||||
|
{
|
||||||
|
showToast(error.message || "Error while Fetching project Name", "error");
|
||||||
|
}
|
||||||
} )
|
} )
|
||||||
return {projectNames:data,loading:isLoading,Error:error,refetch}
|
return {projectNames:data,loading:isLoading,Error:error,refetch}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const useProjectInfra = (projectId) => {
|
||||||
|
const {
|
||||||
|
data: projectInfra,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["ProjectInfra", projectId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await ProjectRepository.getProjectInfraByproject(projectId);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
enabled: !!projectId ,
|
||||||
|
onError: (error) => {
|
||||||
|
showToast(error.message || "Error while fetching project infra", "error");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { projectInfra, isLoading, error };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const useProjectTasks = (workAreaId,IsExpandedArea=false) =>
|
||||||
|
{
|
||||||
|
const { data:ProjectTaskList,isLoading,error } = useQuery( {
|
||||||
|
queryKey: [ "WorkItems",workAreaId ],
|
||||||
|
queryFn: async () =>
|
||||||
|
{
|
||||||
|
const res = await ProjectRepository.getProjectTasksByWorkArea(workAreaId);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
enabled: !!workAreaId && !!IsExpandedArea,
|
||||||
|
onError: ( error ) =>
|
||||||
|
{
|
||||||
|
showToast(error.message || "Error while Fetching project Tasks", "error");
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
return {ProjectTaskList,isLoading,error}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -- Mutation-------------------------------
|
// -- Mutation-------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -371,21 +427,22 @@ export const useUpdateProject = ({ onSuccessCallback }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useManageProjectInfra = () => {
|
export const useManageProjectInfra = ( {onSuccessCallback} ) =>
|
||||||
|
{
|
||||||
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({infraObject,projectId}) => {
|
mutationFn: async ( {infraObject, projectId} ) =>
|
||||||
|
{
|
||||||
return await ProjectRepository.manageProjectInfra(infraObject);
|
return await ProjectRepository.manageProjectInfra(infraObject);
|
||||||
},
|
},
|
||||||
onSuccess: ( response, variables ) =>
|
onSuccess: ( data, variables ) =>
|
||||||
{
|
{
|
||||||
const { projectId } = variables;
|
const { projectId } = variables;
|
||||||
showToast( "Details updated successfully.", "success" );
|
queryClient.invalidateQueries(["ProjectInfra", projectId]);
|
||||||
|
if (onSuccessCallback) onSuccessCallback(data,variables);
|
||||||
queryClient.invalidateQueries(["projectinfo", projectId]);
|
|
||||||
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
showToast(error.message || "Failed to update task details", "error");
|
showToast(error.message || "Failed to update Project Infra", "error");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -433,3 +490,27 @@ export const useManageProjectAllocation = ({
|
|||||||
isError,
|
isError,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useManageTask = ({onSuccessCallback}) =>
|
||||||
|
{
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation( {
|
||||||
|
mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ),
|
||||||
|
onSuccess: ( data, variables ) =>
|
||||||
|
{
|
||||||
|
queryClient.invalidateQueries(["WorkItems"])
|
||||||
|
showToast( 'Activity Updated Successfully', 'success' );
|
||||||
|
if (onSuccessCallback) onSuccessCallback(data);
|
||||||
|
},
|
||||||
|
onError: (error) =>
|
||||||
|
{
|
||||||
|
const message =
|
||||||
|
error?.response?.data?.message || error.message || 'Error occurred during API call';
|
||||||
|
showToast(message, 'error');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -78,14 +78,10 @@ const ProjectDetails = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
||||||
{/* About User */}
|
<AboutProject ></AboutProject>
|
||||||
<AboutProject data={projects_Details}></AboutProject>
|
|
||||||
{/* About User */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
||||||
{/* Profile Overview */}
|
|
||||||
<ProjectOverview project={projectId} />
|
<ProjectOverview project={projectId} />
|
||||||
{/* Profile Overview */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -95,7 +91,7 @@ const ProjectDetails = () => {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-lg-12 col-xl-12">
|
<div className="col-lg-12 col-xl-12">
|
||||||
{/* Teams */}
|
{/* Teams */}
|
||||||
<Teams project={projects_Details}></Teams>
|
<Teams ></Teams>
|
||||||
{/* Teams */}
|
{/* Teams */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -135,26 +131,12 @@ const ProjectDetails = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setProjectId(projectId));
|
dispatch(setProjectId(projectId));
|
||||||
// setProject(projects_Details);
|
|
||||||
// setProjectDetails(projects_Details);
|
|
||||||
}, [projects_Details, projectId]);
|
}, [projects_Details, projectId]);
|
||||||
|
|
||||||
const handler = useCallback(
|
const handler = useCallback(
|
||||||
(msg) => {
|
(msg) => {
|
||||||
if (msg.keyword === "Update_Project" && project.id === msg.response.id) {
|
if (msg.keyword === "Update_Project" && project.id === msg.response.id) {
|
||||||
// clearCacheKey("projectInfo")
|
|
||||||
// ProjectRepository.getProjectByprojectId(projectId)
|
|
||||||
// .then((response) => {
|
|
||||||
// setProjectDetails(response.data);
|
|
||||||
// setProject(response.data);
|
|
||||||
// cacheData("projectInfo", { projectId, data: response.data });
|
|
||||||
// setLoading(false);
|
|
||||||
// })
|
|
||||||
// .catch((error) => {
|
|
||||||
// console.error(error);
|
|
||||||
// setError("Failed to fetch data.");
|
|
||||||
// setLoading(false);
|
|
||||||
// });
|
|
||||||
refetch()
|
refetch()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,11 @@ const ProjectRepository = {
|
|||||||
deleteProject: ( id ) => api.delete( `/projects/${ id }` ),
|
deleteProject: ( id ) => api.delete( `/projects/${ id }` ),
|
||||||
getProjectsByEmployee: ( id ) => api.get( `/api/project/assigned-projects/${ id }` ),
|
getProjectsByEmployee: ( id ) => api.get( `/api/project/assigned-projects/${ id }` ),
|
||||||
updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data),
|
updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data),
|
||||||
projectNameList:()=>api.get("/api/project/list/basic")
|
projectNameList: () => api.get( "/api/project/list/basic" ),
|
||||||
|
|
||||||
|
getProjectDetails:(id)=>api.get(`/api/project/details/${id}`),
|
||||||
|
getProjectInfraByproject: ( id ) => api.get( `/api/project/infra-details/${ id }` ),
|
||||||
|
getProjectTasksByWorkArea:(id)=>api.get(`/api/project/tasks/${id}`)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TasksRepository = {
|
export const TasksRepository = {
|
||||||
|
@ -11,9 +11,9 @@ export const VIEW_PROJECTS = "6ea44136-987e-44ba-9e5d-1cf8f5837ebc"
|
|||||||
|
|
||||||
export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566"
|
export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566"
|
||||||
|
|
||||||
export const MANAGE_PROJECT_INFRA = "f2aee20a-b754-4537-8166-f9507b44585b"
|
export const MANAGE_PROJECT_INFRA = "cf2825ad-453b-46aa-91d9-27c124d63373"
|
||||||
|
|
||||||
export const VIEW_PROJECT_INFRA = "c7b68e33-72f0-474f-bd96-77636427ecc8"
|
export const VIEW_PROJECT_INFRA = "8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"
|
||||||
|
|
||||||
export const REGULARIZE_ATTENDANCE ="57802c4a-00aa-4a1f-a048-fd2f70dd44b6"
|
export const REGULARIZE_ATTENDANCE ="57802c4a-00aa-4a1f-a048-fd2f70dd44b6"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user