From 2c1a01bb6ac50b5fd4ca6fdd167a9ab741ab6405 Mon Sep 17 00:00:00 2001 From: shanika Jayawardane Date: Fri, 11 Jul 2025 07:44:02 -0600 Subject: [PATCH 1/5] add the All select option to monitor maintenance window for all monitors same as status pages --- .../Components/MonitorList/index.jsx | 107 ++++++++++++++++++ .../Maintenance/CreateMaintenance/index.jsx | 55 ++++++++- 2 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx diff --git a/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx new file mode 100644 index 000000000..36d6594dc --- /dev/null +++ b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx @@ -0,0 +1,107 @@ +// Components +import { Stack, Typography } from "@mui/material"; +import ReorderRoundedIcon from "@mui/icons-material/ReorderRounded"; +import DeleteIcon from "@mui/icons-material/Delete"; + +// Utils +import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; +import { useTheme } from "@emotion/react"; +import Checkbox from "../../../../../Components/Inputs/Checkbox"; +const MonitorListItem = ({ + monitor, + innerRef, + draggableProps, + dragHandleProps, + onDelete, +}) => { + const theme = useTheme(); + return ( + + + {monitor.name} + { + onDelete(monitor); + }} + /> + + ); +}; + +const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => { + const onDelete = (monitorToDelete) => { + const newMonitors = selectedMonitors.filter( + (monitor) => monitor._id !== monitorToDelete._id + ); + setSelectedMonitors(newMonitors); + }; + const reorder = (list, startIndex, endIndex) => { + const result = Array.from(list); + const [removed] = result.splice(startIndex, 1); + result.splice(endIndex, 0, removed); + + return result; + }; + + const onDragEnd = (result) => { + // dropped outside the list + if (!result.destination) { + return; + } + + const reorderedMonitors = reorder( + selectedMonitors, + result.source.index, + result.destination.index + ); + + setSelectedMonitors(reorderedMonitors); + }; + + return ( + + + {(provided, snapshot) => ( + + {selectedMonitors?.map((monitor, index) => ( + + {(provided, snapshot) => ( + + )} + + ))} + {provided.placeholder} + + )} + + + ); +}; + +export default MonitorList; diff --git a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx index e71c7112c..c4e839e02 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -9,6 +9,8 @@ import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { MobileTimePicker } from "@mui/x-date-pickers/MobileTimePicker"; import { maintenanceWindowValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; +import MonitorList from "./Components/MonitorList"; +import Checkbox from "../../../Components/Inputs/Checkbox"; import dayjs from "dayjs"; import Select from "../../../Components/Inputs/Select"; @@ -129,6 +131,7 @@ const CreateMaintenance = () => { monitors: [], }); const [errors, setErrors] = useState({}); + const [selectedMonitors, setSelectedMonitors] = useState([]); useEffect(() => { const fetchMonitors = async () => { @@ -165,6 +168,7 @@ const CreateMaintenance = () => { durationUnit, monitors: monitor ? [monitor] : [], }); + setSelectedMonitors(monitor ? [monitor] : []); } catch (error) { createToast({ body: "Failed to fetch data" }); logger.error("Failed to fetch monitors", error); @@ -175,6 +179,11 @@ const CreateMaintenance = () => { fetchMonitors(); }, [user]); + // Sync form.monitors with selectedMonitors + useEffect(() => { + setForm((prev) => ({ ...prev, monitors: selectedMonitors })); + }, [selectedMonitors]); + const handleSearch = (value) => { setSearch(value); }; @@ -212,6 +221,33 @@ const CreateMaintenance = () => { }); }; + const handleMonitorsChange = (selected) => { + if (selected.some((m) => m._id === "__all__")) { + if (selectedMonitors.length !== monitors.length) { + setSelectedMonitors(monitors); + } else { + setSelectedMonitors([]); + } + } else { + // Ensure all are objects + const selectedObjs = selected + .map((sel) => + typeof sel === "string" ? monitors.find((m) => m._id === sel) : sel + ) + .filter(Boolean); + setSelectedMonitors(selectedObjs); + } + const { error } = maintenanceWindowValidation.validate( + { monitors: selected }, + { abortEarly: false } + ); + setErrors((prev) => { + return buildErrors(prev, "monitors", error); + }); + }; + + const allSelected = selectedMonitors.length === monitors.length && monitors.length > 0; + const handleSubmit = async () => { if (hasValidationErrors(form, maintenanceWindowValidation, setErrors)) return; // Build timestamp for maintenance window from startDate and startTime @@ -267,6 +303,10 @@ const CreateMaintenance = () => { } }; + // Add Select All option to the dropdown + const selectAllOption = { _id: "__all__", name: "Select All" }; + const monitorOptions = [selectAllOption, ...monitors]; + return ( { label={t("addMonitors")} multiple={true} isAdorned={false} - options={monitors ? monitors : []} + options={monitorOptions} filteredBy="name" secondaryLabel={"type"} inputValue={search} - value={form.monitors} - handleInputChange={handleSearch} - handleChange={handleSelectMonitors} + value={selectedMonitors} + handleInputChange={setSearch} + handleChange={handleMonitorsChange} error={errors["monitors"]} - disabled={maintenanceWindowId !== undefined} + disabled={form.isAllMonitors} + /> + + Date: Fri, 11 Jul 2025 07:59:12 -0600 Subject: [PATCH 2/5] add the translation to selectAll --- client/src/Pages/Maintenance/CreateMaintenance/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx index c4e839e02..0be00e8da 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -304,7 +304,7 @@ const CreateMaintenance = () => { }; // Add Select All option to the dropdown - const selectAllOption = { _id: "__all__", name: "Select All" }; + const selectAllOption = { _id: "__all__", name: t("selectAll") }; const monitorOptions = [selectAllOption, ...monitors]; return ( From 39d666df6b51c7c779adc1f27b0af794ba8d4f92 Mon Sep 17 00:00:00 2001 From: shanika Jayawardane Date: Sat, 12 Jul 2025 15:13:24 -0600 Subject: [PATCH 3/5] add the checkboxes for dropdown list and remove searchbox list inside it --- .../Maintenance/CreateMaintenance/index.jsx | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx index 0be00e8da..19cb5b16d 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -222,23 +222,14 @@ const CreateMaintenance = () => { }; const handleMonitorsChange = (selected) => { - if (selected.some((m) => m._id === "__all__")) { - if (selectedMonitors.length !== monitors.length) { - setSelectedMonitors(monitors); - } else { - setSelectedMonitors([]); - } - } else { - // Ensure all are objects - const selectedObjs = selected - .map((sel) => - typeof sel === "string" ? monitors.find((m) => m._id === sel) : sel - ) - .filter(Boolean); - setSelectedMonitors(selectedObjs); - } + // Ensure all are objects + const selectedObjs = selected + .map((sel) => (typeof sel === "string" ? monitors.find((m) => m._id === sel) : sel)) + .filter(Boolean); + setSelectedMonitors(selectedObjs); + const { error } = maintenanceWindowValidation.validate( - { monitors: selected }, + { monitors: selectedObjs }, { abortEarly: false } ); setErrors((prev) => { @@ -246,8 +237,6 @@ const CreateMaintenance = () => { }); }; - const allSelected = selectedMonitors.length === monitors.length && monitors.length > 0; - const handleSubmit = async () => { if (hasValidationErrors(form, maintenanceWindowValidation, setErrors)) return; // Build timestamp for maintenance window from startDate and startTime @@ -303,10 +292,6 @@ const CreateMaintenance = () => { } }; - // Add Select All option to the dropdown - const selectAllOption = { _id: "__all__", name: t("selectAll") }; - const monitorOptions = [selectAllOption, ...monitors]; - return ( { id={"monitors"} label={t("addMonitors")} multiple={true} - isAdorned={false} - options={monitorOptions} + isAdorned={true} + options={monitors} filteredBy="name" secondaryLabel={"type"} inputValue={search} From 870cd6407ec81e8369047251351b1c14f528d04b Mon Sep 17 00:00:00 2001 From: shanika Jayawardane Date: Mon, 14 Jul 2025 23:44:50 -0600 Subject: [PATCH 4/5] remove the drag and drop fuction for list and remove dublicate code --- .../Components/MonitorList/index.jsx | 97 ++++--------------- .../Maintenance/CreateMaintenance/index.jsx | 24 ++--- 2 files changed, 27 insertions(+), 94 deletions(-) diff --git a/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx index 36d6594dc..e2265c9e0 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx @@ -1,41 +1,21 @@ // Components import { Stack, Typography } from "@mui/material"; -import ReorderRoundedIcon from "@mui/icons-material/ReorderRounded"; import DeleteIcon from "@mui/icons-material/Delete"; - -// Utils -import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; import { useTheme } from "@emotion/react"; -import Checkbox from "../../../../../Components/Inputs/Checkbox"; -const MonitorListItem = ({ - monitor, - innerRef, - draggableProps, - dragHandleProps, - onDelete, -}) => { + +const MonitorListItem = ({ monitor, onDelete }) => { const theme = useTheme(); return ( - - {monitor.name} + {monitor.name} { - onDelete(monitor); - }} + sx={{ cursor: "pointer" }} + onClick={() => onDelete(monitor)} /> ); @@ -48,59 +28,22 @@ const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => { ); setSelectedMonitors(newMonitors); }; - const reorder = (list, startIndex, endIndex) => { - const result = Array.from(list); - const [removed] = result.splice(startIndex, 1); - result.splice(endIndex, 0, removed); - return result; - }; - - const onDragEnd = (result) => { - // dropped outside the list - if (!result.destination) { - return; - } - - const reorderedMonitors = reorder( - selectedMonitors, - result.source.index, - result.destination.index - ); - - setSelectedMonitors(reorderedMonitors); - }; + const theme = useTheme(); return ( - - - {(provided, snapshot) => ( - - {selectedMonitors?.map((monitor, index) => ( - - {(provided, snapshot) => ( - - )} - - ))} - {provided.placeholder} - - )} - - + + {selectedMonitors?.map((monitor) => ( + + ))} + ); }; diff --git a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx index 19cb5b16d..519cb0818 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -131,7 +131,6 @@ const CreateMaintenance = () => { monitors: [], }); const [errors, setErrors] = useState({}); - const [selectedMonitors, setSelectedMonitors] = useState([]); useEffect(() => { const fetchMonitors = async () => { @@ -168,7 +167,6 @@ const CreateMaintenance = () => { durationUnit, monitors: monitor ? [monitor] : [], }); - setSelectedMonitors(monitor ? [monitor] : []); } catch (error) { createToast({ body: "Failed to fetch data" }); logger.error("Failed to fetch monitors", error); @@ -179,11 +177,6 @@ const CreateMaintenance = () => { fetchMonitors(); }, [user]); - // Sync form.monitors with selectedMonitors - useEffect(() => { - setForm((prev) => ({ ...prev, monitors: selectedMonitors })); - }, [selectedMonitors]); - const handleSearch = (value) => { setSearch(value); }; @@ -222,14 +215,9 @@ const CreateMaintenance = () => { }; const handleMonitorsChange = (selected) => { - // Ensure all are objects - const selectedObjs = selected - .map((sel) => (typeof sel === "string" ? monitors.find((m) => m._id === sel) : sel)) - .filter(Boolean); - setSelectedMonitors(selectedObjs); - + setForm((prev) => ({ ...prev, monitors: selected })); const { error } = maintenanceWindowValidation.validate( - { monitors: selectedObjs }, + { monitors: selected }, { abortEarly: false } ); setErrors((prev) => { @@ -555,15 +543,17 @@ const CreateMaintenance = () => { filteredBy="name" secondaryLabel={"type"} inputValue={search} - value={selectedMonitors} + value={form.monitors} handleInputChange={setSearch} handleChange={handleMonitorsChange} error={errors["monitors"]} disabled={form.isAllMonitors} /> + setForm((prev) => ({ ...prev, monitors })) + } /> From 1cbebf322b8c85735d866df1ed7c4650e4866bbb Mon Sep 17 00:00:00 2001 From: shanika Jayawardane Date: Wed, 16 Jul 2025 11:06:05 -0600 Subject: [PATCH 5/5] remove unwanted code --- .../Components/MonitorList/index.jsx | 19 +++++++++++++++++++ .../Maintenance/CreateMaintenance/index.jsx | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx index e2265c9e0..c3a4681a1 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/Components/MonitorList/index.jsx @@ -2,6 +2,7 @@ import { Stack, Typography } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import { useTheme } from "@emotion/react"; +import PropTypes from "prop-types"; const MonitorListItem = ({ monitor, onDelete }) => { const theme = useTheme(); @@ -21,6 +22,14 @@ const MonitorListItem = ({ monitor, onDelete }) => { ); }; +MonitorListItem.propTypes = { + monitor: PropTypes.shape({ + _id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + }).isRequired, + onDelete: PropTypes.func.isRequired, +}; + const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => { const onDelete = (monitorToDelete) => { const newMonitors = selectedMonitors.filter( @@ -47,4 +56,14 @@ const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => { ); }; +MonitorList.propTypes = { + selectedMonitors: PropTypes.arrayOf( + PropTypes.shape({ + _id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + }) + ).isRequired, + setSelectedMonitors: PropTypes.func.isRequired, +}; + export default MonitorList; diff --git a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx index 519cb0818..9d1b060c2 100644 --- a/client/src/Pages/Maintenance/CreateMaintenance/index.jsx +++ b/client/src/Pages/Maintenance/CreateMaintenance/index.jsx @@ -547,7 +547,6 @@ const CreateMaintenance = () => { handleInputChange={setSearch} handleChange={handleMonitorsChange} error={errors["monitors"]} - disabled={form.isAllMonitors} />