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