Pramod Mahajan 80d64c87c6 Improve safety and sorting for building dropdown
- Added optional chaining to safely access project.buildings and prevent errors if undefined.
- Filtered out buildings with invalid or missing 'name' properties.
- Implemented safe sorting using localeCompare, with a fallback to empty string for undefined 'name' values.
- Added fallback message "No buildings found" when there are no valid buildings in the list.
2025-05-03 11:07:57 +05:30

246 lines
7.8 KiB
JavaScript

import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { getCachedData } from "../../../slices/apiDataManager";
import showToast from "../../../services/toastService";
// Zod validation schema
const floorSchema = z.object({
buildingId: z.string().min(1, "Building is required"),
id: z.string().min(1, "Floor is required").optional(),
floorName: z.string().min(1, "Floor Name is required"),
});
// Default model
const defaultModel = {
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 || []);
// Initialize the form with React Hook Form
const {
register,
handleSubmit,
setValue,
getValues,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(floorSchema),
defaultValues: defaultModel,
});
useEffect(() => {
if (clearTrigger) {
reset(defaultModel);
onClearComplete();
}
}, [clearTrigger, onClearComplete, reset]);
// Handle building selection change
const handleBuildigChange = (e) => {
const buildingId = e.target.value;
const building = buildings.find((b) => b.id === Number(buildingId));
if (building) {
setSelectedBuilding(building);
setFormData({
id: "",
floorName: "",
buildingId: building.id,
});
setValue("buildingId", building.id); // Set value for validation
setValue("id", "0"); // Reset floorId when changing building
} else {
setSelectedBuilding({});
setFormData({
id: "",
floorName: "",
buildingId: "0",
});
setValue("buildingId", "0");
}
};
// Handle floor selection change
const handleFloorChange = (e) => {
const id = e.target.value;
const floor = selectedBuilding.floors.find((b) => b.id === Number(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", "");
}
};
// Handle form submission
const onFormSubmit = (data) => {
onSubmit(data);
reset({
floorName: "",
});
if (data.id !== "0") {
showToast("Floor updated successfully.", "success");
} else {
showToast("Floor created successfully.", "success");
}
};
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>
<select
id="buildingId"
className="select2 form-select form-select-sm"
aria-label="Select Building"
{...register("buildingId")}
onChange={handleBuildigChange}
>
<option value="0">Select Building</option>
{buildings?.length > 0 &&
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}
</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" && (
<div className="col-12 col-md-12">
<label className="form-label">Select Floor</label>
<select
id="id"
className="select2 form-select form-select-sm"
aria-label="Select Floor"
{...register("id")}
onChange={handleFloorChange}
>
<option value="0">Add New Floor</option>
{/* {selectedBuilding?.floors?.map((floor) => (
<option key={floor.id} value={floor.id}>
{floor.floorName}
</option>
) )} */}
{selectedBuilding &&
selectedBuilding.floors?.length > 0 &&
[...selectedBuilding.floors]
.filter((floor) => floor?.floorName)
.sort((a, b) => {
const nameA = a.floorName || "";
const nameB = b.floorName || "";
return nameA?.localeCompare(nameB);
})
.map((floor) => (
<option key={floor.id} value={floor.id}>
{floor.floorName}
</option>
))}
{selectedBuilding?.floors?.length === 0 && (
<option disabled>No floors found</option>
)}
</select>
{errors.id && (
<p className="text-danger">{errors.id.message}</p>
)}
</div>
)}
{formData.buildingId !== "0" && (
<div className="col-12 col-md-12">
<label className="form-label" htmlFor="floorName">
{formData.id !== "0" ? "Modify " : "Enter "} Floor Name
</label>
<input
type="text"
id="floorName"
className="form-control form-control-sm me-2"
placeholder="Floor Name"
{...register("floorName")}
/>
{errors.floorName && (
<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 !== ""
? "Edit Floor"
: "Add Floor"}
</button>
<button
type="button"
className="btn btn-sm btn-label-secondary"
data-bs-dismiss="modal"
aria-label="Close"
onClick={onClose}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default FloorModel;