integrated reactquery for project infra

This commit is contained in:
Pramod Mahajan 2025-07-02 13:20:17 +05:30
parent 2f110b7ead
commit 52c5e3d2d2
7 changed files with 414 additions and 1003 deletions

View File

@ -47,7 +47,7 @@ const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
"success" "success"
); );
reset({ Id: "0", name: "", description: "" }); reset({ Id: "0", name: "", description: "" });
onClose?.(); // onClose?.();
}, },
}); });

View File

@ -14,7 +14,6 @@ const floorSchema = z.object({
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 defaultValues = { const defaultValues = {
id: "0", id: "0",
floorName: "", floorName: "",
@ -49,7 +48,7 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
"success" "success"
); );
reset({ Id: "0", name: "", description: "" }); reset({ Id: "0", name: "", description: "" });
onClose?.(); // onClose?.();
}, },
}); });
@ -121,7 +120,7 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
))} ))}
</select> </select>
{errors.buildingId && ( {errors.buildingId && (
<p className="text-danger">{errors.buildingId.message}</p> <p className="danger-text">{errors.buildingId.message}</p>
)} )}
</div> </div>
@ -157,7 +156,7 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
placeholder="Floor Name" placeholder="Floor Name"
/> />
{errors.floorName && ( {errors.floorName && (
<p className="text-danger">{errors.floorName.message}</p> <p className="danger-text">{errors.floorName.message}</p>
)} )}
</div> </div>
</> </>

View File

@ -1,11 +1,12 @@
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 { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { import {
useActivitiesMaster, useActivitiesMaster,
useWorkCategoriesMaster, useWorkCategoriesMaster,
} from "../../../hooks/masterHook/useMaster"; } from "../../../hooks/masterHook/useMaster";
import { useManageTask } from "../../../hooks/useProjects";
const taskSchema = z.object({ const taskSchema = z.object({
buildingID: z.string().min(1, "Building is required"), buildingID: z.string().min(1, "Building is required"),
@ -14,236 +15,127 @@ const taskSchema = z.object({
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 or more"),
comment:z.string(), comment: z.string(),
}); });
const defaultModel = { const defaultModel = {
id: null, id: null,
buildingID: "", // Changed from "0" buildingID: "",
floorId: "", // Changed from "0" floorId: "",
workAreaId: "", // Changed from "0" workAreaId: "",
activityID: "", // Changed from null activityID: "",
workCategoryId: "", // Kept as empty workCategoryId: "",
plannedWork: 0, plannedWork: 0,
completedWork: 0, completedWork: 0,
comment: "" comment: "",
}; };
const TaskModel = ({ project, onSubmit, onClose }) => {
const { activities, loading: activityLoading } = useActivitiesMaster();
const { categories, categoryLoading } = useWorkCategoriesMaster();
const TaskModel = ({
project,
onSubmit,
clearTrigger,
onClearComplete,
onClose,
}) => {
const [formData, setFormData] = useState(defaultModel);
const [selectedBuilding, setSelectedBuilding] = useState(null);
const [selectedFloor, setSelectedFloor] = useState(null);
const [selectedWorkArea, setSelectedWorkArea] = useState(null);
const [selectedActivity, setSelectedActivity] = useState(null);
const [selectedCategory, setSelectedCategory] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const [activityData, setActivityData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const { activities, loading, error } = useActivitiesMaster();
const { categories, categoryLoading, categoryError } =
useWorkCategoriesMaster();
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, watch,
reset,
setValue, setValue,
reset,
formState: { errors },
} = useForm({ } = useForm({
resolver: zodResolver(taskSchema), resolver: zodResolver(taskSchema),
defaultValues: defaultModel, defaultValues: defaultModel,
}); });
const [isSubmitting, setIsSubmitting] = useState(false);
const [activityData, setActivityData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const watchBuildingId = watch("buildingID");
const watchFloorId = watch("floorId");
const watchWorkAreaId = watch("workAreaId");
const watchActivityId = watch("activityID");
const watchCategoryId = watch("workCategoryId");
const selectedBuilding = project?.find((b) => b.id === watchBuildingId);
const selectedFloor = selectedBuilding?.floors?.find(
(f) => f.id === watchFloorId
);
const selectedWorkArea = selectedFloor?.workAreas?.find(
(w) => w.id === watchWorkAreaId
);
const selectedActivity = activityData?.find((a) => a.id === watchActivityId);
const selectedCategory = categoryData?.find((c) => c.id === watchCategoryId);
const { mutate: CreateTask, isPending } = useManageTask({
onSuccessCallback: () => onClose?.(),
});
useEffect(() => { useEffect(() => {
resetForm(); reset(defaultModel);
}, []); }, []);
const handleBuildingChange = (e) => {
const { value } = e.target;
const building = project.buildings.find((b) => b.id === String(value));
setSelectedBuilding(building);
setSelectedFloor(null);
setSelectedWorkArea(null);
setSelectedActivity(null);
reset({
...defaultModel,
buildingID: value,
});
};
const handleFloorChange = (e) => {
const { value } = e.target;
const floor = selectedBuilding.floors.find((b) => b.id === String(value));
setSelectedFloor(floor);
setSelectedWorkArea(null);
setSelectedActivity(null);
reset((prev) => ({
...prev,
floorId: value,
workAreaId: "",
activityID: "",
workCategoryId: categoryData?.[0]?.id?.toString() ?? "",
}));
};
const handleWorkAreaChange = (e) => {
const { value } = e.target;
const workArea = selectedFloor.workAreas.find(
(b) => b.id === String(value)
);
setSelectedWorkArea(workArea);
reset((prev) => ({
...prev,
workAreaId: String(value),
}));
};
const handleActivityChange = (e) => {
const { value } = e.target;
const activity = activityData.find((b) => b.id === String(value));
setSelectedActivity(activity);
reset((prev) => ({
...prev,
activityID: String(value),
}));
};
const handleCategoryChange = (e) => {
const { value } = e.target;
const category = categoryData.find((b) => b.id === String(value));
setSelectedCategory(category);
reset((prev) => ({
...prev,
workCategoryId: String(value),
}));
};
const onSubmitForm = async (data) => {
setIsSubmitting(true);
await onSubmit(data);
setValue("plannedWork", 0);
setValue("completedWork", 0);
setValue("activityID", 0);
setValue("workCategoryId", categoryData?.[0]?.id?.toString() ?? "");
setIsSubmitting(false);
};
const resetForm = () => {
setFormData(defaultModel);
setSelectedBuilding(null);
setSelectedFloor(null);
setSelectedWorkArea(null);
setSelectedActivity(null);
setSelectedCategory(categoryData?.[0]?.id?.toString() ?? "");
reset(defaultModel);
};
useEffect(() => { useEffect(() => {
if (!loading && Array.isArray(activities) && activities.length > 0) { if (Array.isArray(activities) && activities.length > 0) {
setActivityData(activities); setActivityData(activities);
} }
}, [activities, loading]); }, [activities]);
useEffect(() => { useEffect(() => {
if ( if (Array.isArray(categories) && categories.length > 0) {
!categoryLoading && const sorted = [...categories].sort((a, b) =>
Array.isArray(categories) && (a?.name || "").localeCompare(b?.name || "")
categories.length > 0 );
) { setCategoryData(sorted);
const newCategories = categories?.slice()?.sort((a, b) => { setValue("workCategoryId", sorted?.[0]?.id?.toString() ?? "");
const nameA = a?.name || "";
const nameB = b?.name || "";
return nameA?.localeCompare(nameB);
});
setCategoryData(newCategories);
setSelectedCategory(newCategories[0])
} }
}, [categories, categoryLoading]); }, [categories]);
const onSubmitForm = async (data) => {
const payload = [data];
CreateTask(payload);
};
return ( return (
<div className="modal-dialog modal-lg modal-simple modal-edit-user"> <form className="row g-2" 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)}> <div className="col-6">
{/* Select Building */} <label className="form-label">Select Building</label>
<div className="col-6 col-md-6">
<label className="form-label" htmlFor="buildingID">
Select Building
</label>
<select <select
id="buildingID"
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("buildingID")} {...register("buildingID")}
onChange={handleBuildingChange}
> >
<option value="">Select Building</option> <option value="">Select Building</option>
{project.buildings {project
?.filter((building) => building?.name) // Ensure valid name ?.filter((b) => b?.buildingName)
?.sort((a, b) => a.name?.localeCompare(b.name)) ?.sort((a, b) => a?.buildingName.localeCompare(b.buildingName))
?.map((building) => ( ?.map((b) => (
<option key={building.id} value={building.id}> <option key={b.id} value={b.id}>
{building.name} {b.buildingName}
</option> </option>
))} ))}
{project.buildings?.filter((building) => building?.name)
.length === 0 && (
<option disabled>No buildings found</option>
)}
</select> </select>
{errors.buildingID && ( {errors.buildingID && (
<p className="danger-text">{errors.buildingID.message}</p> <p className="danger-text">{errors.buildingID.message}</p>
)} )}
</div> </div>
{/* Select Floor */}
{selectedBuilding && ( {selectedBuilding && (
<div className="col-6 col-md-6"> <div className="col-6">
<label className="form-label" htmlFor="floorId"> <label className="form-label">Select Floor</label>
Select Floor
</label>
<select <select
id="floorId"
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("floorId")} {...register("floorId")}
onChange={handleFloorChange}
> >
<option value="">Select Floor</option> <option value="">Select Floor</option>
{selectedBuilding.floors {selectedBuilding.floors
?.filter( ?.sort((a, b) => a.floorName.localeCompare(b.floorName))
(floor) => ?.map((f) => (
floor?.floorName && Array.isArray(floor.workAreas) <option key={f.id} value={f.id}>
) {f.floorName}
?.sort((a, b) => a.floorName?.localeCompare(b.floorName))
?.map((floor) => (
<option key={floor.id} value={floor.id}>
{floor.floorName} - ({floor.workAreas.length} Work
Areas)
</option> </option>
))} ))}
{selectedBuilding.floors?.filter(
(floor) =>
floor?.floorName && Array.isArray(floor.workAreas)
).length === 0 && <option disabled>No floors found</option>}
</select> </select>
{errors.floorId && ( {errors.floorId && (
<p className="danger-text">{errors.floorId.message}</p> <p className="danger-text">{errors.floorId.message}</p>
@ -252,31 +144,20 @@ const TaskModel = ({
)} )}
{selectedFloor && ( {selectedFloor && (
<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>
<select <select
id="workAreaId"
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("workAreaId")} {...register("workAreaId")}
onChange={handleWorkAreaChange}
> >
<option value="">Select Work Area</option> <option value="">Select Work Area</option>
{selectedFloor.workAreas {selectedFloor.workAreas
?.filter((workArea) => workArea?.areaName) ?.sort((a, b) => a.areaName.localeCompare(b.areaName))
?.sort((a, b) => a.areaName?.localeCompare(b.areaName)) ?.map((w) => (
?.map((workArea) => ( <option key={w.id} value={w.id}>
<option key={workArea.id} value={workArea.id}> {w.areaName}
{workArea.areaName}
</option> </option>
))} ))}
{selectedFloor.workAreas?.filter(
(workArea) => workArea?.areaName
).length === 0 && (
<option disabled>No work areas found</option>
)}
</select> </select>
{errors.workAreaId && ( {errors.workAreaId && (
<p className="danger-text">{errors.workAreaId.message}</p> <p className="danger-text">{errors.workAreaId.message}</p>
@ -285,36 +166,19 @@ const TaskModel = ({
)} )}
{selectedWorkArea && ( {selectedWorkArea && (
<div className="col-12 col-md-12"> <div className="col-12">
<label className="form-label">Select Activity</label> <label className="form-label">Select Activity</label>
<select <select
id="activityID"
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("activityID")} {...register("activityID")}
onChange={handleActivityChange}
> >
<option value="">Select Activity</option> <option value="">Select Activity</option>
{activityData && {activityData.map((a) => (
activityData.length > 0 && <option key={a.id} value={a.id}>
activityData {a.activityName}
?.slice()
?.sort((a, b) => {
const nameA = a?.activityName || "";
const nameB = b?.activityName || "";
return nameA?.localeCompare(nameB);
})
?.map((activity) => (
<option key={activity.id} value={activity.id}>
{activity.activityName ||
`Unnamed (id: ${activity.id})`}
</option> </option>
))} ))}
{!loading && activities.length === 0 && (
<option disabled>No activities available</option>
)}
{loading && <option disabled>Loading...</option>}
</select> </select>
{errors.activityID && ( {errors.activityID && (
<p className="danger-text">{errors.activityID.message}</p> <p className="danger-text">{errors.activityID.message}</p>
)} )}
@ -322,109 +186,81 @@ const TaskModel = ({
)} )}
{selectedWorkArea && ( {selectedWorkArea && (
<div className="col-12 col-md-12"> <div className="col-12">
<label className="form-label">Select Work Category</label> <label className="form-label">Select Work Category</label>
<select <select
id="workCategoryId"
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("workCategoryId")} {...register("workCategoryId")}
onChange={handleCategoryChange}
> >
{categoryData && {categoryData.map((c) => (
categoryData.length > 0 && <option key={c.id} value={c.id}>
categoryData {c.name}
?.map((category) => (
<option key={category.id} value={category.id}>
{category.name || `Unnamed (id: ${category.id})`}
</option> </option>
))} ))}
{!categoryLoading && categories.length === 0 && (
<option disabled>No activities available</option>
)}
{categoryLoading && <option disabled>Loading...</option>}
</select> </select>
{errors.workCategoryId && ( {errors.workCategoryId && (
<p className="danger-text"> <p className="danger-text">{errors.workCategoryId.message}</p>
{errors.workCategoryId.message}
</p>
)} )}
</div> </div>
)} )}
{selectedActivity && selectedCategory && ( {selectedActivity && selectedCategory && (
<div className="col-5 col-md-5"> <>
<label className="form-label" htmlFor="plannedWork"> <div className="col-5">
{formData.id !== "0" ? "Modify " : "Enter "} Planned Work <label className="form-label">Planned Work</label>
</label>
<input <input
{...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" {...register("plannedWork", { valueAsNumber: true })}
/> />
{errors.plannedWork && ( {errors.plannedWork && (
<p className="danger-text">{errors.plannedWork.message}</p> <p className="danger-text">{errors.plannedWork.message}</p>
)} )}
</div> </div>
)} <div className="col-5">
<label className="form-label">Completed Work</label>
{selectedActivity && selectedCategory && (
<div className="col-5 col-md-5">
<label className="form-label" htmlFor="completedWork">
{formData.id !== "0" ? "Modify " : "Enter "} Completed Work
</label>
<input <input
{...register("completedWork", { valueAsNumber: true })}
type="number" type="number"
className="form-control form-control-sm me-2" className="form-control form-control-sm"
placeholder="Completed Work" {...register("completedWork", { valueAsNumber: true })}
/> />
{errors.completedWork && ( {errors.completedWork && (
<p className="danger-text"> <p className="danger-text">{errors.completedWork.message}</p>
{errors.completedWork.message}
</p>
)} )}
</div> </div>
)} <div className="col-2">
<label className="form-label">Unit</label>
{selectedActivity && selectedCategory && (
<div className="col-2 col-md-2">
<label className="form-label" htmlFor="unit">
Unit
</label>
<input <input
type="text" 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>
</>
)} )}
{selectedActivity && selectedCategory && ( {selectedActivity && selectedCategory && (
<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"
htmlFor="descriptionTextarea"
>
Comment
</label>
<textarea <textarea
{...register("comment")}
className="form-control" className="form-control"
id="descriptionTextarea"
rows="2" rows="2"
{...register("comment")}
/> />
{errors.comment && ( {errors.comment && (
<div className="danger-text"> <p className="danger-text">{errors.comment.message}</p>
{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"> <button
{isSubmitting ? "Please Wait.." : "Add Task"} type="submit"
className="btn btn-sm btn-primary me-3"
disabled={isSubmitting}
>
{isSubmitting ? "Please Wait..." : "Add Task"}
</button> </button>
<button <button
type="button" type="button"
@ -435,10 +271,6 @@ const TaskModel = ({
</button> </button>
</div> </div>
</form> </form>
</div>
</div>
</div>
</div>
); );
}; };

View File

@ -1,39 +1,29 @@
import React, { useState, useEffect } from "react"; import React, { useEffect, useState } from "react";
import { set, 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 showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import { useManageProjectInfra } from "../../../hooks/useProjects";
import { useSelector } from "react-redux";
// Zod schema for form validation
const workAreaSchema = z.object({ const workAreaSchema = z.object({
id: z.string().nonempty("Floor is required"), id: z.string().optional(),
buildingId: z.string().min(1, "Building is required"),
buildingId: z.string().nonempty("Building is required"), floorId: z.string().min(1, "Floor is required"),
floorId: z.string().nonempty("Floor is required"), areaName: z.string().min(3, "Work Area Name must be at least 3 characters"),
areaName: z
.string()
.nonempty("Work Area Name is required")
.min(3, "Name must be at least 3 characters long"),
}); });
// Default form data
const defaultModel = { const defaultModel = {
id: "0", id: "0",
areaName: "", buildingId: "0",
floorId: "0", floorId: "0",
areaName: "",
}; };
const WorkAreaModel = ({ const WorkAreaModel = ({ project, onSubmit, onClose }) => {
project,
onSubmit,
clearTrigger,
onClearComplete,
onClose,
}) => {
const [selectedBuilding, setSelectedBuilding] = useState(null); const [selectedBuilding, setSelectedBuilding] = useState(null);
const [selectedFloor, setSelectedFloor] = useState(null); const [selectedFloor, setSelectedFloor] = useState(null);
const [selectdWorkArea, setWorkArea] = useState(); const selectedProject = useSelector((store)=>store.localVariables.projectId)
const { const {
register, register,
handleSubmit, handleSubmit,
@ -42,259 +32,169 @@ const WorkAreaModel = ({
reset, reset,
watch, watch,
} = useForm({ } = useForm({
resolver: zodResolver(workAreaSchema), // Use Zod resolver for validation resolver: zodResolver(workAreaSchema),
defaultValues: defaultModel, defaultValues: defaultModel,
}); });
const floorId = watch("floorId"); // Watch the floorId for conditional rendering const watchBuildingId = watch("buildingId");
const watchFloorId = watch("floorId");
const watchWorkAreaId = watch("id");
const { mutate: ManageWorkArea, isPending } = useManageProjectInfra({
onSuccessCallback: (data, variables) => {
showToast(
watchWorkAreaId != "0"
? "Wrok Area updated Successfully"
: "Work Area created Successfully",
"success"
);
reset({ id: "0", buildingId: "0", areaName: "", floorId: "0" });
// onClose?.();
},
});
useEffect(() => { useEffect(() => {
if (clearTrigger) { const building = project?.find((b) => b.id === watchBuildingId);
reset(defaultModel); // Reset form to initial state setSelectedBuilding(building || null);
setSelectedBuilding(null);
setSelectedFloor(null);
onClearComplete();
}
}, [clearTrigger, onClearComplete, reset]);
const handleWorkAreaChange = (e) => { if (building) {
const { value } = e.target; const floor = building.floors?.find((f) => f.id === watchFloorId);
setSelectedFloor(floor || null);
if (value === "0") {
setValue("id", "0"); // Create New Work Area
setValue("areaName", ""); setValue("areaName", "");
setWorkArea(String(0));
} else {
const workArea = selectedFloor?.workAreas.find(
(b) => b.id === String(value)
);
if (workArea) {
setValue("id", String(workArea.id)); // Set id as a string
setValue("areaName", workArea.areaName);
setWorkArea(String(workArea.id));
}
}
};
const handleFloorChange = (e) => {
const { value } = e.target;
const floor = selectedBuilding?.floors.find((b) => b.id === String(value));
if (floor) {
setSelectedFloor(floor);
setValue("floorId", floor.id); // Update floorId
setValue("id", "0"); // Reset Work Area ID for new area creation
setValue("areaName", ""); // Reset Work Area Name when changing floor
} else { } else {
setSelectedFloor(null); setSelectedFloor(null);
setValue("floorId", "0"); setValue("floorId", "0");
setValue("id", "0"); // Reset Work Area ID setValue("areaName", "");
setValue("areaName", ""); // Reset Work Area Name }
}, [watchBuildingId, watchFloorId]);
const handleWrokAreaChange = (e) => {
const workAreaId = e.target.value;
setValue("id", workAreaId);
const area = selectedFloor?.workAreas.find((w) => w.id === workAreaId);
if (area) {
setValue("areaName", area.areaName);
} else {
setValue("areaName", "");
} }
}; };
const handleBuildingChange = (e) => { useEffect(() => {
const { value } = e.target; reset(defaultModel);
const building = project?.buildings.find((b) => b.id === String(value)); }, []);
setSelectedBuilding(building);
setSelectedFloor(null); // Reset selected floor on building change
reset(defaultModel); // Reset the form when a new building is selected
};
const onSubmitForm = (data) => { const onSubmitForm = ( data ) =>
let WorkArea = { {
id: data.id == "0" ? null : data.id, const payload = {
id: data.id === "0" ? null : data.id,
areaName: data.areaName, areaName: data.areaName,
floorId: data.floorId, floorId: data.floorId,
buildingId: data.buildingId, buildingId: data.buildingId,
}; };
onSubmit(WorkArea); let infraObject = [
{
building: null,
floor: null,
workArea: payload,
},
];
reset({ ManageWorkArea({ infraObject, projectId: selectedProject });
id: "0",
areaName: "",
});
if (WorkArea.id !== null) {
showToast("WorkArea updated successfully.", "success");
} else {
showToast("WorkArea created successfully.", "success");
}
};
const handleCancel = () => {
reset(defaultModel);
setSelectedFloor(null);
setSelectedBuilding(null);
onClose();
}; };
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 Work Area</h5> <h5 className="mb-1">Manage Work Area</h5>
</div> </div>
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}> <div className="col-12 col-sm-6">
{/* Building Selection */} <label className="form-label">Select Building</label>
<div className="col-6 col-md-6">
<label className="form-label" htmlFor="buildingId">
Select Building
</label>
<select <select
id="buildingId"
name="buildingId"
className="select2 form-select form-select-sm"
{...register("buildingId")} {...register("buildingId")}
onChange={handleBuildingChange} className="form-select form-select-sm"
> >
<option value="0">Select Building</option> <option value="0">Select Building</option>
{project?.buildings {project?.map((b) => (
?.filter((building) => building?.name) <option key={b.id} value={b.id}>
?.sort((a, b) => { {b.buildingName}
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>
))} ))}
{project?.buildings?.filter((building) => building?.name)
.length === 0 && (
<option disabled>No buildings found</option>
)}
</select> </select>
{errors.buildingId && <span>{errors.buildingId.message}</span>} {errors.buildingId && (
<p className="danger-text">{errors.buildingId.message}</p>
)}
</div> </div>
{/* Floor Selection */} {watchBuildingId !== "0" && (
{selectedBuilding && selectedBuilding.buildingId !== "0" && ( <div className="col-12 col-sm-6">
<div className="col-6 col-md-6"> <label className="form-label">Select Floor</label>
<label className="form-label" htmlFor="floorId">
Select Floor
</label>
<select <select
id="floorId"
name="floorId"
className="select2 form-select form-select-sm"
{...register("floorId")} {...register("floorId")}
onChange={handleFloorChange} className="form-select form-select-sm"
> >
<option value="0">Select Floor</option> <option value="0">
{selectedBuilding.floors {selectedBuilding?.floor?.length > 0
?.filter((floor) => floor?.floorName) ? "NO Floor Found"
?.sort((a, b) => { : "Select Floor"}
const nameA = a.floorName || ""; </option>
const nameB = b.floorName || "";
return nameA?.localeCompare(nameB); {selectedBuilding?.floors?.map((f) => (
}) <option key={f.id} value={f.id}>
?.map((floor) => ( {f.floorName}
<option key={floor.id} value={floor.id}>
{floor.floorName}
</option> </option>
))} ))}
{selectedBuilding.floors?.filter(
(floor) => floor?.floorName
).length === 0 && <option disabled>No floors found</option>}
</select> </select>
{errors.floorId && <span>{errors.floorId.message}</span>} {errors.floorId && (
<p className="danger-text">{errors.floorId.message}</p>
)}
</div> </div>
)} )}
{watchFloorId !== "0" && (
{/* Work Area Selection or Creation */}
{floorId !== "0" && (
<> <>
<div className="col-12 col-md-12"> <div className="col-12">
<label className="form-label">Select Work Area</label> <label className="form-label">Select Work Area</label>
<select <select
id="workAreaId"
name="workAreaId"
className="select2 form-select form-select-sm"
{...register("id")} {...register("id")}
onChange={handleWorkAreaChange} className="form-select form-select-sm"
onChange={handleWrokAreaChange}
> >
<option value="0">Create New Work Area</option> <option value="0">Create New Work Area</option>
{selectedFloor?.workAreas {selectedFloor?.workAreas?.length > 0 &&
?.filter((workArea) => workArea?.areaName) selectedFloor?.workAreas?.map((w) => (
?.sort((a, b) => { <option key={w.id} value={w.id}>
const nameA = a.areaName || ""; {w.areaName}
const nameB = b.areaName || "";
return nameA?.localeCompare(nameB);
})
?.map((workArea) => (
<option key={workArea.id} value={workArea.id}>
{workArea.areaName}
</option> </option>
))} ))}
{selectedFloor?.workAreas?.filter(
(workArea) => workArea?.areaName
).length === 0 && (
<option disabled>No work areas found</option>
)}
</select> </select>
{errors.id && <span>{errors.id.message}</span>}
</div> </div>
{/* Work Area Name Input */} <div className="col-12">
<div className="col-12 col-md-12"> <label className="form-label">
<label className="form-label" htmlFor="areaName"> {watchWorkAreaId === "0"
{watch("id") === "0"
? "Enter Work Area Name" ? "Enter Work Area Name"
: "Modify Work Area Name"} : "Edit Work Area Name"}
</label> </label>
<input <input
type="text" type="text"
id="areaName"
name="areaName"
className="form-control form-control-sm" className="form-control form-control-sm"
placeholder="Work Area" placeholder="Work Area"
{...register("areaName")} {...register("areaName")}
/> />
{errors.areaName && ( {errors.areaName && (
<span className="danger-text"> <p className="danger-text">{errors.areaName.message}</p>
{errors.areaName.message}
</span>
)} )}
</div> </div>
{/* Submit and Cancel Buttons */}
<div className="col-12 text-center"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3" disabled={isPending}>
type="submit" {isPending ? "Please Wait.." : watchWorkAreaId === "0" ? "Add Work Area" : "Update Work Area"}
className="btn btn-sm btn-primary me-3"
>
{watch("id") === "0" ? "Add Work Area" : "Edit Work Area"}
</button> </button>
<button <button type="button" className="btn btn-sm btn-label-secondary" disabled={isPending} onClick={onClose}>
type="button"
className="btn btn-sm btn-label-secondary"
onClick={handleCancel}
data-bs-dismiss="modal"
aria-label="Close"
>
Cancel Cancel
</button> </button>
</div> </div>
</> </>
)} )}
</form> </form>
</div>
</div>
</div>
</div>
); );
}; };

View File

@ -8,7 +8,6 @@ import TaskModel from "./Infrastructure/TaskModel";
import ProjectRepository, { import ProjectRepository, {
TasksRepository, TasksRepository,
} from "../../repositories/ProjectRepository"; } from "../../repositories/ProjectRepository";
import ProjectModal from "./ProjectModal";
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 InfraTable from "./Infrastructure/InfraTable"; import InfraTable from "./Infrastructure/InfraTable";
@ -32,17 +31,10 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
const {projectInfra,isLoading,error} = useProjectInfra(projectId) 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 [isModalOpen, setIsModalOpen] = useState(false);
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
const [isBuildingModalOpen, setIsBuildingModalOpen] = useState(false);
const [showModalFloor, setshowModalFloor] = useState(false); const [showModalFloor, setshowModalFloor] = useState(false);
const [isWorkAreaModelOpen, setIsWorkAreaModalOpen] = useState(false); const [showModalWorkArea, setshowModalWorkArea] = useState(false);
const [isTaskModelOpen, setIsTaskModalOpen] = useState(false); const [showModalTask, setshowModalTask] = useState(false);
const [isAssignRoleModal, setIsAssingRoleModal] = useState(false);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [clearFormTrigger, setClearFormTrigger] = useState(false);
const [CurrentBuilding, setCurrentBuilding] = useState("");
const [showModalBuilding, setshowModalBuilding] = useState(false); const [showModalBuilding, setshowModalBuilding] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -50,266 +42,6 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
setProject(projectInfra); setProject(projectInfra);
}, [data, projects_Details]); }, [data, projects_Details]);
const openAssignModel = (assignData) => {
setCurrentBuilding(assignData);
setIsAssingRoleModal(true);
};
const openBuildingModel = (projectData) => {
setIsBuildingModalOpen(true);
};
const closeBuildingModel = () => {
setIsBuildingModalOpen(false);
};
const openWorkAreaModel = (projectData) => {
setIsWorkAreaModalOpen(true);
};
const closeWorkAreaModel = () => {
setIsWorkAreaModalOpen(false);
};
const handleWorkAreaModelFormSubmit = (updatedModel) => {
if (updatedModel.id == "") delete updatedModel.id;
submitData([
{
building: null,
floor: null,
workArea: updatedModel,
},
]);
};
const openTaskModel = (projectData) => {
setIsTaskModalOpen(true);
};
const closeTaskModel = () => {
setIsTaskModalOpen(false);
};
const handleTaskModelFormSubmit = (updatedModel) => {
if (updatedModel.id == "") updatedModel.id = null;
const updatedProject = { ...project };
ProjectRepository.manageProjectTasks([updatedModel])
.then((response) => {
onDataChange("task-change");
showToast("Details updated successfully.", "success");
// setClearFormTrigger( true );
if (response?.data[0]) {
const { workItemId, workItem } = response.data[0];
const updatedBuildings = updatedProject.buildings.map((building) =>
building.id == updatedModel.buildingID
? {
...building,
floors: building.floors.map((floor) =>
floor.id == updatedModel.floorId
? {
...floor,
workAreas: floor.workAreas.map((workArea) =>
workArea.id === workItem?.workAreaId
? {
...workArea,
workItems: workArea.workItems.some(
(existingItem) =>
existingItem.workItemId ===
workItem.workItemId
)
? [...workArea.workItems] // Create a new array to trigger re-render
: [...workArea.workItems, workItem],
}
: workArea
),
}
: floor
),
}
: building
);
updatedProject.buildings = updatedBuildings;
// workItem update, but having local state issue there for needed to calling api
clearCacheKey("projectInfo");
refetch();
cacheData("projectInfo", {
projectId: updatedProject.id,
data: updatedProject,
});
setProject(updatedProject);
// closeTaskModel()
}
})
.catch((error) => {
showToast(error.message, "error");
});
};
const submitData = async (infraObject) => {
try {
let response = await ProjectRepository.manageProjectInfra(infraObject);
const entity = response.data;
const updatedProject = { ...project };
// Handle the building data
if (entity.building) {
const { id, name, description } = entity.building;
const updatedBuildings = updatedProject?.buildings?.map((building) =>
building.id === id ? { ...building, name, description } : building
);
// Add building if it doesn't exist
if (!updatedProject.buildings.some((building) => building.id === id)) {
updatedBuildings.push({
id: id,
name,
description,
floors: [],
});
}
updatedProject.buildings = updatedBuildings;
// Update the cache for buildings
cacheData("projectInfo", {
projectId: updatedProject.id,
data: updatedProject,
});
setProject((prevProject) => ({
...prevProject,
buildings: updatedBuildings,
}));
// closeBuildingModel()
}
// Handle the floor data
else if (entity.floor) {
const { buildingId, id, floorName } = entity.floor;
const updatedBuildings = updatedProject?.buildings?.map((building) =>
building.id == buildingId
? {
...building,
floors: building.floors
.map((floor) =>
floor.id === id
? {
...floor,
floorName, // Update the floor name only
// Keep other properties as they are (including workArea)
}
: floor
)
// Add the new floor if it doesn't already exist
.concat(
!building.floors.some((floor) => floor.id === id)
? [{ id: id, floorName, workAreas: [] }] // New floor added with workArea set to null
: []
),
}
: building
);
updatedProject.buildings = updatedBuildings;
// Cache the updated project
cacheData("projectInfo", {
projectId: updatedProject.id,
data: updatedProject,
});
setProject(updatedProject);
// closeFloorModel()
}
// Handle the work area data
else if (entity.workArea) {
let buildingId = infraObject[0].workArea.buildingId;
const { floorId, areaName, id } = entity.workArea;
// Check if the workArea exists, otherwise create a new one
const updatedBuildings = updatedProject.buildings.map((building) =>
building.id == buildingId
? {
...building,
floors: building.floors.map((floor) =>
floor.id == floorId
? {
...floor,
workAreas: floor.workAreas.some(
(workArea) => workArea.id === id
)
? floor.workAreas.map((workArea) =>
workArea.id === id
? { ...workArea, areaName }
: workArea
)
: [
...floor.workAreas,
{ id, areaName, workItems: [] },
],
}
: floor
),
}
: building
);
updatedProject.buildings = updatedBuildings;
// Update the cache for work areas
cacheData("projectInfo", {
projectId: updatedProject.id,
data: updatedProject,
});
setProject(updatedProject);
// closeWorkAreaModel()
}
// Handle the task (workItem) data
else {
console.error("Unsupported data type for submitData", entity);
}
} catch (Err) {
showToast("Somthing wrong", "error");
}
};
const toggleBuilding = (id) => {
setExpandedBuildings((prev) =>
prev.includes(id) ? prev.filter((bid) => bid !== id) : [...prev, id]
);
};
const handleModalData = (type, modaldata) => {
setModalConfig({ type: type, data: modaldata });
};
const openModal = () => {
const modalElement = document.getElementById("building-model");
const modal = new Modal(modalElement, {
backdrop: false,
keyboard: true,
focus: true,
});
modal.show();
};
const closeModal = () => {
setIsModalOpen(false);
setModalConfig(null);
const modalElement = document.getElementById("building-model");
if (modalElement) {
modalElement.classList.remove("show");
modalElement.style.display = "none";
}
document.body.classList.remove("modal-open");
const backdropElement = document.querySelector(".modal-backdrop");
if (backdropElement) {
backdropElement.classList.remove("modal-backdrop");
backdropElement.style.display = "none";
}
document.body.style.overflow = "auto";
};
useEffect(() => { useEffect(() => {
if (reloadedData) { if (reloadedData) {
refetch(); refetch();
@ -323,7 +55,6 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
return ( return (
<> <>
{showModalBuilding && <GlobalModel isOpen={showModalBuilding} size="md" closeModal={() => setshowModalBuilding( false )}> {showModalBuilding && <GlobalModel isOpen={showModalBuilding} size="md" closeModal={() => setshowModalBuilding( false )}>
<BuildingModel <BuildingModel
project={projectInfra} project={projectInfra}
@ -336,50 +67,18 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
onClose={()=>setshowModalFloor(false)} onClose={()=>setshowModalFloor(false)}
/> />
</GlobalModel>} </GlobalModel>}
{showModalWorkArea && <GlobalModel isOpen={showModalWorkArea} size="lg" closeModal={()=>setshowModalWorkArea(false)} >
{isWorkAreaModelOpen && (
<div
className="modal fade show"
id="work-area-model"
tabIndex="-1"
role="dialog"
style={{ display: "block" }}
aria-hidden="false"
>
<WorkAreaModel <WorkAreaModel
project={project} project={projectInfra}
onClose={closeWorkAreaModel} onClose={()=>setshowModalWorkArea(false)}
onClearComplete={() => setClearFormTrigger(false)}
/> />
</div> </GlobalModel>}
)} {showModalTask && ( <GlobalModel isOpen={showModalTask} size="lg" closeModal={()=>setshowModalTask(false)}>
{isTaskModelOpen && (
<div
className="modal fade show"
id="task-model"
tabIndex="-1"
role="dialog"
style={{ display: "block" }}
aria-hidden="false"
>
<TaskModel <TaskModel
project={project} project={projectInfra}
onClose={closeTaskModel} onClose={()=>setshowModalTask(false)}
onSubmit={handleTaskModelFormSubmit}
onClearComplete={() => setClearFormTrigger(false)}
/> />
</div> </GlobalModel>)}
)}
{isModalOpen && (
<ProjectModal modalConfig={modalConfig} closeModal={closeModal} />
)}
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4"> <div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4">
<div className="card"> <div className="card">
<div className="card-body" style={{ padding: "0.5rem" }}> <div className="card-body" style={{ padding: "0.5rem" }}>
@ -409,7 +108,7 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
<button <button
type="button" type="button"
className="link-button m-1" className="link-button m-1"
onClick={() => openWorkAreaModel()} onClick={() => setshowModalWorkArea(true)}
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
Manage Work Areas Manage Work Areas
@ -417,7 +116,7 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
<button <button
type="button" type="button"
className="link-button m-1" className="link-button m-1"
onClick={() => openTaskModel()} onClick={()=>setshowModalTask(true)}
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
Create Tasks Create Tasks
@ -430,7 +129,7 @@ const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
<InfraTable <InfraTable
buildings={projectInfra} buildings={projectInfra}
projectId={projectId} projectId={projectId}
handleFloor={submitData} // handleFloor={submitData}
signalRHandler ={signalRHandler} signalRHandler ={signalRHandler}
/> />
)} )}

View File

@ -346,7 +346,7 @@ export const useProjectTasks = (workAreaId,IsExpandedArea=false) =>
} }
// -- Mutation------------------------------- // -- -------------Mutation-------------------------------
@ -396,11 +396,9 @@ export const useUpdateProject = ({ onSuccessCallback }) => {
onSuccess: (data, variables) => { onSuccess: (data, variables) => {
const { projectId } = variables; const { projectId } = variables;
// Invalidate queries
queryClient.invalidateQueries(["ProjectsList"]); queryClient.invalidateQueries(["ProjectsList"]);
queryClient.invalidateQueries(["projectinfo", projectId]); queryClient.invalidateQueries(["projectinfo", projectId]);
// Emit update event
eventBus.emit("project", { eventBus.emit("project", {
keyword: "Update_Project", keyword: "Update_Project",
response: data, response: data,
@ -499,8 +497,15 @@ export const useManageTask = ({onSuccessCallback}) =>
mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ), mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ),
onSuccess: ( data, variables ) => onSuccess: ( data, variables ) =>
{ {
queryClient.invalidateQueries(["WorkItems"]) debugger
if ( variables[ 0 ]?.id )
{
showToast( 'Activity Updated Successfully', 'success' ); showToast( 'Activity Updated Successfully', 'success' );
} else
{
showToast( 'Activity Created Successfully', 'success' );
}
queryClient.invalidateQueries(["WorkItems"])
if (onSuccessCallback) onSuccessCallback(data); if (onSuccessCallback) onSuccessCallback(data);
}, },
onError: (error) => onError: (error) =>
@ -508,7 +513,6 @@ export const useManageTask = ({onSuccessCallback}) =>
const message = const message =
error?.response?.data?.message || error.message || 'Error occurred during API call'; error?.response?.data?.message || error.message || 'Error occurred during API call';
showToast(message, 'error'); showToast(message, 'error');
} }
}) })

View File

@ -127,7 +127,7 @@ const EmployeeList = () => {
modalElement.classList.remove("show"); modalElement.classList.remove("show");
modalElement.style.display = "none"; modalElement.style.display = "none";
document.body.classList.remove("modal-open"); document.body.classList.remove("modal-open");
document.querySelector(".modal-backdrop")?.remove(); // Use optional chaining for safety document.querySelector(".modal-backdrop")?.remove();
} }
setShowModal(false); setShowModal(false);
clearCacheKey("employeeProfile"); clearCacheKey("employeeProfile");
@ -152,32 +152,9 @@ const EmployeeList = () => {
setEmployeeList(sorted); setEmployeeList(sorted);
setFilteredData(sorted); setFilteredData(sorted);
} }
}, [loading, employees, selectedProject, showAllEmployees]); }, [loading, employees, selectedProjectId, showAllEmployees]);
// const suspendEmployee = (id) => {
// setemployeeLodaing(true);
// EmployeeRepository.deleteEmployee(id)
// .then((response) => {
// showToast("Employee deleted successfully.", "success");
// clearCacheKey("employeeListByProject");
// clearCacheKey("allEmployeeList");
// clearCacheKey("allInactiveEmployeeList");
// clearCacheKey("employeeProfile");
// setEmployeeList([]);
// recallEmployeeData(showInactive);
// setemployeeLodaing(false);
// setIsDeleteModalOpen(false);
// })
// .catch((error) => {
// const message =
// error.response?.data?.message ||
// error.message ||
// "An unexpected error occurred";
// showToast(message, "error");
// setemployeeLodaing(false);
// setIsDeleteModalOpen(false);
// });
// };
const handleConfigData = (config) => { const handleConfigData = (config) => {
setModelConfig(config); setModelConfig(config);