Adding dropdown across all places.

This commit is contained in:
Kartik Sharma 2025-12-06 14:49:23 +05:30
parent 48ff718da4
commit 72b533226a
4 changed files with 483 additions and 374 deletions

View File

@ -144,21 +144,31 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
name="id" name="id"
control={control} control={control}
rules={{ required: "Floor is required" }} rules={{ required: "Floor is required" }}
render={({ field }) => ( render={({ field }) => {
<SelectField // Prepare options
label="" // Label is already above const floorOptions = [
placeholder="Select Floor"
options={[
{ id: "0", name: "Add New Floor" }, { id: "0", name: "Add New Floor" },
...(selectedBuilding?.floors ...(selectedBuilding?.floors
?.filter((f) => f.floorName) ?.filter((f) => f.floorName)
.sort((a, b) => a.floorName.localeCompare(b.floorName)) .sort((a, b) => a.floorName.localeCompare(b.floorName))
.map((f) => ({ id: f.id, name: f.floorName })) ?? []), .map((f) => ({ id: f.id, name: f.floorName })) ?? []),
]} ];
value={field.value || ""}
onChange={(value) => { return (
field.onChange(value); <SelectField
handleFloorChange?.(value); label=""
placeholder="Select Floor"
options={floorOptions}
value={field.value || "0"} // default to "0"
onChange={(val) => {
field.onChange(val); // update react-hook-form
if (val === "0") {
setValue("floorName", ""); // clear for new floor
} else {
const floor = selectedBuilding?.floors?.find(f => f.id === val);
setValue("floorName", floor?.floorName || "");
}
}} }}
required required
noOptionsMessage={() => noOptionsMessage={() =>
@ -168,7 +178,8 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
} }
className="m-0 form-select-sm w-100" className="m-0 form-select-sm w-100"
/> />
)} );
}}
/> />
{errors.id && ( {errors.id && (

View File

@ -12,6 +12,8 @@ import { useManageTask, useProjectAssignedOrganizationsName, useProjectAssigned
import showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { useSelectedProject } from "../../../slices/apiDataManager"; import { useSelectedProject } from "../../../slices/apiDataManager";
import { AppFormController, AppFormProvider } from "../../../hooks/appHooks/useAppForm";
import SelectField from "../../common/Forms/SelectField";
const taskSchema = z.object({ const taskSchema = z.object({
buildingID: z.string().min(1, "Building is required"), buildingID: z.string().min(1, "Building is required"),
@ -76,19 +78,13 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
setValue("activityID", ""); setValue("activityID", "");
}; };
const methods = useForm({
const {
register,
handleSubmit,
watch,
setValue,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(taskSchema),
defaultValues: defaultModel, defaultValues: defaultModel,
resolver: zodResolver(taskSchema),
}); });
const { register, control, watch, handleSubmit, reset, setValue, formState: { errors } } = methods;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [activityData, setActivityData] = useState([]); const [activityData, setActivityData] = useState([]);
const [categoryData, setCategoryData] = useState([]); const [categoryData, setCategoryData] = useState([]);
@ -148,152 +144,217 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
}; };
return ( return (
<AppFormProvider {...methods}>
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}> <form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
<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>
{/* Select Building */}
<div className="col-6 text-start"> <div className="col-6 text-start">
<Label className="form-label" required>Select Building</Label> <AppFormController
<select name="buildingID"
className="form-select form-select-sm" control={control}
{...register("buildingID")} render={({ field }) => (
> <SelectField
<option value="">Select Building</option> label="Select Building"
{project options={project ?? []}
?.filter((b) => b?.buildingName) placeholder="Select Building"
?.sort((a, b) => a?.buildingName.localeCompare(b.buildingName)) required
?.map((b) => ( labelKey="buildingName"
<option key={b.id} value={b.id}> valueKey="id"
{b.buildingName} value={field.value}
</option> onChange={(value) => {
))} field.onChange(value);
</select> setValue("floorId", "");
setValue("workAreaId", "");
setSelectedService("");
setSelectedGroup("");
}}
className="m-0"
/>
)}
/>
{errors.buildingID && ( {errors.buildingID && (
<p className="danger-text">{errors.buildingID.message}</p> <small className="danger-text">{errors.buildingID.message}</small>
)} )}
</div> </div>
{/* Select Floor */}
{selectedBuilding && ( {selectedBuilding && (
<div className="col-6 text-start"> <div className="col-6 text-start">
<Label className="form-label" required>Select Floor</Label> <AppFormController
<select name="floorId"
className="form-select form-select-sm" control={control}
{...register("floorId")} render={({ field }) => (
> <SelectField
<option value="">Select Floor</option> label="Select Floor"
{selectedBuilding.floors options={selectedBuilding?.floors ?? []}
?.sort((a, b) => a.floorName.localeCompare(b.floorName)) placeholder={
?.map((f) => ( selectedBuilding?.floors?.length > 0
<option key={f.id} value={f.id}> ? "Select Floor"
{f.floorName} : "No Floor Found"
</option> }
))} required
</select> labelKey="floorName"
valueKey="id"
value={field.value}
onChange={(value) => {
field.onChange(value);
setValue("workAreaId", "");
}}
className="m-0"
/>
)}
/>
{errors.floorId && ( {errors.floorId && (
<p className="danger-text">{errors.floorId.message}</p> <small className="danger-text">{errors.floorId.message}</small>
)} )}
</div> </div>
)} )}
{/* Work Area Selection */} {/* Select Work Area */}
{selectedFloor && ( {selectedFloor && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<Label className="form-label" required>Select Work Area</Label> <AppFormController
<select name="workAreaId"
className="form-select form-select-sm" control={control}
{...register("workAreaId")} render={({ field }) => (
> <SelectField
<option value="">Select Work Area</option> label="Select Work Area"
{selectedFloor.workAreas options={selectedFloor?.workAreas ?? []}
?.sort((a, b) => a.areaName.localeCompare(b.areaName)) placeholder="Select Work Area"
?.map((w) => ( required
<option key={w.id} value={w.id}> labelKey="areaName"
{w.areaName} valueKey="id"
</option> value={field.value}
))} onChange={(value) => {
</select> field.onChange(value);
setSelectedService("");
setSelectedGroup("");
}}
className="m-0"
/>
)}
/>
{errors.workAreaId && ( {errors.workAreaId && (
<p className="danger-text">{errors.workAreaId.message}</p> <small className="danger-text">{errors.workAreaId.message}</small>
)} )}
</div> </div>
)} )}
{/* Services Selection */} {/* Select Service */}
{selectedWorkArea && ( {selectedWorkArea && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<Label className="form-label" required>Select Service</Label> <AppFormController
<select name="serviceId"
className="form-select form-select-sm" control={control}
{...register("serviceId")} render={({ field }) => (
value={selectedService} <SelectField
// onChange={handleServiceChange} label="Select Service"
onChange={(e) => { options={assignedServices ?? []}
handleServiceChange(e); placeholder="Select Service"
setValue("serviceId", e.target.value); required
labelKey="name"
valueKey="id"
value={field.value}
onChange={(value) => {
field.onChange(value);
setSelectedService(value);
setSelectedGroup("");
setValue("activityGroupId", "");
setValue("activityID", "");
}} }}
> isLoading={servicesLoading}
<option value="">Select Service</option> className="m-0"
{servicesLoading && <option>Loading...</option>} />
{assignedServices?.map((service) => ( )}
<option key={service.id} value={service.id}> />
{service.name} {errors.serviceId && (
</option> <small className="danger-text">{errors.serviceId.message}</small>
))} )}
</select>
</div> </div>
)} )}
{/* Activity Group (Organization) Selection */} {/* Select Activity Group */}
{selectedService && ( {selectedService && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<Label className="form-label" required>Select Activity Group</Label> <AppFormController
<select name="activityGroupId"
className="form-select form-select-sm" control={control}
{...register("activityGroupId")} render={({ field }) => (
value={selectedGroup} <SelectField
onChange={handleGroupChange} label="Select Activity Group"
> options={groups ?? []}
<option value="">Select Group</option> placeholder="Select Activity Group"
{groupsLoading && <option>Loading...</option>} required
{groups?.map((g) => ( labelKey="name"
<option key={g.id} value={g.id}>{g.name}</option> valueKey="id"
))} value={field.value}
</select> onChange={(value) => {
{errors.activityGroupId && <p className="danger-text">{errors.activityGroupId.message}</p>} field.onChange(value);
setSelectedGroup(value);
setValue("activityID", "");
}}
isLoading={groupsLoading}
className="m-0"
/>
)}
/>
{errors.activityGroupId && (
<small className="danger-text">{errors.activityGroupId.message}</small>
)}
</div> </div>
)} )}
{/* Activity Selection */} {/* Select Activity */}
{selectedGroup && ( {selectedGroup && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<Label className="form-label" required>Select Activity</Label> <AppFormController
<select className="form-select form-select-sm" {...register("activityID")}> name="activityID"
<option value="">Select Activity</option> control={control}
{activitiesLoading && <option>Loading...</option>} render={({ field }) => (
{activities?.map((a) => ( <SelectField
<option key={a.id} value={a.id}>{a.activityName}</option> label="Select Activity"
))} options={activities ?? []}
</select> placeholder="Select Activity"
{errors.activityID && <p className="danger-text">{errors.activityID.message}</p>} required
labelKey="activityName"
valueKey="id"
value={field.value}
onChange={field.onChange}
isLoading={activitiesLoading}
className="m-0"
/>
)}
/>
{errors.activityID && (
<small className="danger-text">{errors.activityID.message}</small>
)}
</div> </div>
)} )}
{watchActivityId && ( {watchActivityId && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<label className="form-label">Select Work Category</label> <AppFormController
<select name="workCategoryId"
className="form-select form-select-sm" control={control}
{...register("workCategoryId")} render={({ field }) => (
> <SelectField
{categoryData.map((c) => ( label="Select Work Category"
<option key={c.id} value={c.id}> options={categoryData ?? []}
{c.name} placeholder="Select Work Category"
</option> required
))} labelKey="name"
</select> valueKey="id"
value={field.value}
onChange={field.onChange}
className="m-0"
/>
)}
/>
{errors.workCategoryId && ( {errors.workCategoryId && (
<p className="danger-text">{errors.workCategoryId.message}</p> <small className="danger-text">{errors.workCategoryId.message}</small>
)} )}
</div> </div>
)} )}
@ -304,7 +365,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
<Label className="form-label" required>Planned Work</Label> <Label className="form-label" required>Planned Work</Label>
<input <input
type="number" type="number"
className="form-control form-control-sm" className="form-control "
{...register("plannedWork", { valueAsNumber: true })} {...register("plannedWork", { valueAsNumber: true })}
/> />
{errors.plannedWork && ( {errors.plannedWork && (
@ -315,7 +376,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
<label className="form-label">Completed Work</label> <label className="form-label">Completed Work</label>
<input <input
type="number" type="number"
className="form-control form-control-sm" className="form-control "
{...register("completedWork", { valueAsNumber: true })} {...register("completedWork", { valueAsNumber: true })}
/> />
{errors.completedWork && ( {errors.completedWork && (
@ -326,7 +387,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
<label className="form-label">Unit</label> <label className="form-label">Unit</label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
disabled disabled
value={selectedActivity?.unitOfMeasurement || ""} value={selectedActivity?.unitOfMeasurement || ""}
/> />
@ -366,6 +427,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
</button> </button>
</div> </div>
</form> </form>
</AppFormProvider>
); );
}; };

View File

@ -6,6 +6,8 @@ import showToast from "../../../services/toastService";
import { useManageProjectInfra } from "../../../hooks/useProjects"; import { useManageProjectInfra } from "../../../hooks/useProjects";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { AppFormController, AppFormProvider } from "../../../hooks/appHooks/useAppForm";
import SelectField from "../../common/Forms/SelectField";
const workAreaSchema = z.object({ const workAreaSchema = z.object({
id: z.string().optional(), id: z.string().optional(),
@ -26,19 +28,14 @@ const defaultModel = {
const WorkAreaModel = ({ project, onSubmit, onClose }) => { const WorkAreaModel = ({ project, onSubmit, onClose }) => {
const [selectedBuilding, setSelectedBuilding] = useState(null); const [selectedBuilding, setSelectedBuilding] = useState(null);
const [selectedFloor, setSelectedFloor] = useState(null); const [selectedFloor, setSelectedFloor] = useState(null);
const selectedProject = useSelector((store) => store.localVariables.projectId) const selectedProject = useSelector((store) => store.localVariables.projectId);
const { const methods = useForm({
register,
handleSubmit,
formState: { errors },
setValue,
reset,
watch,
} = useForm({
resolver: zodResolver(workAreaSchema),
defaultValues: defaultModel, defaultValues: defaultModel,
resolver: zodResolver(workAreaSchema),
}); });
const { register, control, watch, handleSubmit, reset, setValue, formState: { errors } } = methods;
const watchBuildingId = watch("buildingId"); const watchBuildingId = watch("buildingId");
const watchFloorId = watch("floorId"); const watchFloorId = watch("floorId");
const watchWorkAreaId = watch("id"); const watchWorkAreaId = watch("id");
@ -104,69 +101,91 @@ const WorkAreaModel = ({ project, onSubmit, onClose }) => {
}; };
return ( return (
<AppFormProvider {...methods}>
<form className="row g-2 p-2 p-md-1" onSubmit={handleSubmit(onSubmitForm)}> <form className="row g-2 p-2 p-md-1" onSubmit={handleSubmit(onSubmitForm)}>
<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>
<div className="col-12 col-sm-6 text-start"> <div className="col-12 col-sm-6 text-start">
<Label className="form-label" required>Select Building</Label> <AppFormController
<select name="buildingId"
{...register("buildingId")} control={control}
className="form-select form-select-sm" render={({ field }) => (
> <SelectField
<option value="0">Select Building</option> label="Select Building"
{project?.map((b) => ( options={project ?? []}
<option key={b.id} value={b.id}> placeholder="Select Building"
{b.buildingName} required
</option> labelKey="buildingName"
))} valueKey="id"
</select> value={field.value}
onChange={field.onChange}
className="m-0"
/>
)}
/>
{errors.buildingId && ( {errors.buildingId && (
<p className="danger-text">{errors.buildingId.message}</p> <small className="danger-text">{errors.buildingId.message}</small>
)} )}
</div> </div>
{watchBuildingId !== "0" && ( {watchBuildingId !== "0" && (
<div className="col-12 col-sm-6 text-start"> <div className="col-12 col-sm-6 text-start">
<Label className="form-label" required>Select Floor</Label> <AppFormController
<select name="floorId"
{...register("floorId")} control={control}
className="form-select form-select-sm" render={({ field }) => (
> <SelectField
<option value="0"> label="Select Floor"
{selectedBuilding?.floor?.length > 0 options={selectedBuilding?.floors ?? []}
? "NO Floor Found" placeholder={
: "Select Floor"} selectedBuilding?.floors?.length > 0
</option> ? "Select Floor"
: "No Floor Found"
{selectedBuilding?.floors?.map((f) => ( }
<option key={f.id} value={f.id}> required
{f.floorName} labelKey="floorName"
</option> valueKey="id"
))} value={field.value}
</select> onChange={(value) => {
field.onChange(value);
setValue("areaName", ""); // reset Work Area name when floor changes
}}
className="m-0"
/>
)}
/>
{errors.floorId && ( {errors.floorId && (
<p className="danger-text">{errors.floorId.message}</p> <small className="danger-text">{errors.floorId.message}</small>
)} )}
</div> </div>
)} )}
{watchFloorId !== "0" && ( {watchFloorId !== "0" && (
<> <>
<div className="col-12 text-start"> <div className="col-12 text-start">
<label className="form-label">Select Work Area</label> <AppFormController
<select name="id"
{...register("id")} control={control}
className="form-select form-select-sm" render={({ field }) => (
onChange={handleWrokAreaChange} <SelectField
> label="Select Work Area"
<option value="0">Create New Work Area</option> options={selectedFloor?.workAreas ?? []}
{selectedFloor?.workAreas?.length > 0 && placeholder="Create New Work Area"
selectedFloor?.workAreas?.map((w) => ( required={false}
<option key={w.id} value={w.id}> labelKey="areaName"
{w.areaName} valueKey="id"
</option> value={field.value}
))} onChange={(value) => {
</select> field.onChange(value);
handleWrokAreaChange({ target: { value } }); // preserve your existing handler
}}
className="m-0"
/>
)}
/>
</div> </div>
<div className="col-12 text-start"> <div className="col-12 text-start">
@ -177,7 +196,7 @@ const WorkAreaModel = ({ project, onSubmit, onClose }) => {
</Label> </Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control"
placeholder="Work Area" placeholder="Work Area"
{...register("areaName")} {...register("areaName")}
/> />
@ -197,6 +216,7 @@ const WorkAreaModel = ({ project, onSubmit, onClose }) => {
</div> </div>
</form> </form>
</AppFormProvider>
); );
}; };

View File

@ -30,6 +30,9 @@ import { useParams } from "react-router-dom";
import GlobalModel from "../common/GlobalModel"; import GlobalModel from "../common/GlobalModel";
import { setService } from "../../slices/globalVariablesSlice"; import { setService } from "../../slices/globalVariablesSlice";
import { SpinnerLoader } from "../common/Loader"; import { SpinnerLoader } from "../common/Loader";
import { useForm } from "react-hook-form";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => { const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
const projectId = useSelectedProject(); const projectId = useSelectedProject();
@ -52,6 +55,12 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
const { data: assignedServices, isLoading: servicesLoading } = const { data: assignedServices, isLoading: servicesLoading } =
useProjectAssignedServices(projectId); useProjectAssignedServices(projectId);
const { control } = useForm({
defaultValues: {
serviceId: selectedService || "",
},
});
useEffect(() => { useEffect(() => {
setProject(projectInfra); setProject(projectInfra);
}, [data, projects_Details]); }, [data, projects_Details]);
@ -60,6 +69,11 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
setProject(response); setProject(response);
}; };
const handleServiceChange = (serviceId) => {
dispatch(setService(serviceId));
};
return ( return (
<> <>
{showModalBuilding && ( {showModalBuilding && (
@ -115,37 +129,39 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
<div className="card-body" style={{ padding: "0.5rem" }}> <div className="card-body" style={{ padding: "0.5rem" }}>
<div className="align-items-center"> <div className="align-items-center">
<div className="row "> <div className="row ">
<div <div className="col-md-4 col-12 dataTables_length text-start py-2 px-2">
className="dataTables_length text-start py-2 px-6 col-md-4 col-12" <div className="ms-4 mt-n1">
id="DataTables_Table_0_length" {!servicesLoading && assignedServices?.length > 0 && (
> assignedServices.length > 1 ? (
{!servicesLoading && <AppFormController
assignedServices?.length > 0 && name="serviceId"
(assignedServices.length > 1 ? ( control={control}
<label> render={({ field }) => (
<select <SelectField
name="DataTables_Table_0_length" label="Select Service"
aria-controls="DataTables_Table_0" options={[{ id: "", name: "All Projects" }, ...(assignedServices ?? [])]}
className="form-select form-select-sm" placeholder="Choose a Service"
aria-label="Select Service" required
value={selectedService} labelKey="name"
onChange={(e) => dispatch(setService(e.target.value))} valueKey="id"
> value={field.value}
<option value="">All Services</option> onChange={(val) => {
{assignedServices.map((service) => ( field.onChange(val);
<option key={service.id} value={service.id}> handleServiceChange(val);
{service.name} }}
</option> isLoading={servicesLoading}
))} />
</select> )}
</label> />
) : ( ) : (
<h5>{assignedServices[0].name}</h5> <h5>{assignedServices[0].name}</h5>
))} )
)}
</div>
</div> </div>
{/* Buttons Section (aligned to right) */} {/* Buttons Section (aligned to right) */}
<div className="col-md-8 col-12 text-end mb-1"> <div className="col-md-8 col-12 text-end mt-4">
{ManageInfra && ( {ManageInfra && (
<> <>
<button <button