diff --git a/client/src/Components/inputs/TimePicker.tsx b/client/src/Components/inputs/TimePicker.tsx index 251332fe1..b82df1f15 100644 --- a/client/src/Components/inputs/TimePicker.tsx +++ b/client/src/Components/inputs/TimePicker.tsx @@ -3,6 +3,7 @@ import type { MobileTimePickerProps } from "@mui/x-date-pickers/MobileTimePicker import type { Dayjs } from "dayjs"; import { useTheme } from "@mui/material/styles"; import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; import { FieldLabel } from "./FieldLabel"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers"; @@ -10,11 +11,15 @@ import { LocalizationProvider } from "@mui/x-date-pickers"; interface TimePickerComponentProps extends Omit, "label"> { fieldLabel?: string; required?: boolean; + error?: boolean; + helperText?: string; } export const TimePickerComponent = ({ fieldLabel, required, + error, + helperText, ...props }: TimePickerComponentProps) => { const theme = useTheme(); @@ -33,12 +38,12 @@ export const TimePickerComponent = ({ px: theme.spacing(5), }, "& fieldset": { - borderColor: theme.palette.divider, + borderColor: error ? theme.palette.error.main : theme.palette.divider, borderRadius: theme.shape.borderRadius, }, "&:not(:has(.Mui-disabled)):not(:has(.Mui-error)) .MuiOutlinedInput-root:not(:has(input:focus)):hover fieldset": { - borderColor: theme.palette.divider, + borderColor: error ? theme.palette.error.main : theme.palette.divider, }, }, }, @@ -48,14 +53,18 @@ export const TimePickerComponent = ({ ); - if (fieldLabel) { - return ( - - {fieldLabel} - {picker} - - ); - } - - return picker; + return ( + + {fieldLabel && {fieldLabel}} + {picker} + {helperText && ( + + {helperText} + + )} + + ); }; diff --git a/client/src/Components/inputs/forms/FormTimePickerField.tsx b/client/src/Components/inputs/forms/FormTimePickerField.tsx new file mode 100644 index 000000000..2db830a50 --- /dev/null +++ b/client/src/Components/inputs/forms/FormTimePickerField.tsx @@ -0,0 +1,43 @@ +import { + Controller, + useFormContext, + type FieldPath, + type FieldValues, +} from "react-hook-form"; +import dayjs from "dayjs"; +import { TimePicker } from "@/Components/inputs"; + +interface FormTimePickerFieldProps { + name: FieldPath; + fieldLabel?: string; + format?: string; + onValueChange?: (value: string) => void; // e.g. () => trigger("startDate") +} + +export const FormTimePickerField = ({ + name, + fieldLabel, + format = "HH:mm", + onValueChange, +}: FormTimePickerFieldProps) => { + const { control } = useFormContext(); + return ( + ( + { + const next = time ? time.format(format) : ""; + field.onChange(next); + onValueChange?.(next); + }} + error={!!fieldState.error} + helperText={fieldState.error?.message} + /> + )} + /> + ); +}; diff --git a/client/src/Pages/Maintenance/create/index.tsx b/client/src/Pages/Maintenance/create/index.tsx index 20a0b461e..d01249e02 100644 --- a/client/src/Pages/Maintenance/create/index.tsx +++ b/client/src/Pages/Maintenance/create/index.tsx @@ -1,18 +1,8 @@ import Stack from "@mui/material/Stack"; -import MenuItem from "@mui/material/MenuItem"; import { logger } from "@/Utils/logger"; -import Typography from "@mui/material/Typography"; -import IconButton from "@mui/material/IconButton"; import { SPACING, LAYOUT } from "@/Utils/Theme/constants"; import { BasePage, ConfigBox } from "@/Components/design-elements"; -import { - TextField, - Select, - DatePicker, - TimePicker, - Button, - Autocomplete, -} from "@/Components/inputs"; +import { Button } from "@/Components/inputs"; import { useTheme } from "@mui/material"; import { useTranslation } from "react-i18next"; @@ -25,10 +15,15 @@ import { useGet, usePost, usePatch } from "@/Hooks/UseApi"; import { mutate } from "swr"; import { useParams, useNavigate } from "react-router-dom"; import type { Monitor } from "@/Types/Monitor"; -import { useForm, Controller } from "react-hook-form"; +import { useForm, FormProvider } from "react-hook-form"; import { useEffect } from "react"; import { zodResolver } from "@hookform/resolvers/zod/dist/zod.js"; -import { Trash2 } from "lucide-react"; +import { FormTextField } from "@/Components/inputs/forms/FormTextField"; +import { FormSelectField } from "@/Components/inputs/forms/FormSelectField"; +import { FormDatePickerField } from "@/Components/inputs/forms/FormDatePickerField"; +import { FormTimePickerField } from "@/Components/inputs/forms/FormTimePickerField"; +import { FormMultiSelectField } from "@/Components/inputs/forms/FormMultiSelectField"; +import { FormNumberField } from "@/Components/inputs/forms/FormNumberField"; const CreateMaintenanceWindowPage = () => { const theme = useTheme(); @@ -57,7 +52,7 @@ const CreateMaintenanceWindowPage = () => { defaultValues: defaults, }); - const { control, handleSubmit, trigger } = form; + const { handleSubmit, trigger } = form; useEffect(() => { if (existingMaintenanceWindow) { form.reset(defaults); @@ -114,255 +109,116 @@ const CreateMaintenanceWindowPage = () => { }; return ( - - - ( - - )} - /> - ( - - )} - /> - - } - /> - - ( - { - field.onChange(date ? date.format("YYYY-MM-DD") : ""); - trigger("startDate"); - }} - error={!!fieldState.error} - helperText={fieldState.error?.message} - /> - )} - /> - - } - /> - - ( - { - field.onChange(time ? time.format("HH:mm") : ""); - trigger("startDate"); - }} - /> - )} - /> - - ( - { - const val = e.target.value; - field.onChange(val === "" ? 0 : Number(val)); - }} - error={!!fieldState.error} - helperText={fieldState.error?.message ?? ""} - sx={{ width: 120 }} - /> + + + + - ( - - )} + ({ + value: option.id, + label: option.name, + }))} + defaultValue={defaults.repeat} /> - - } - /> - { - const monitorsList = monitors ?? []; - const selectedMonitors = field.value - .map((id: string) => monitorsList.find((m) => m.id === id)) - .filter((m): m is Monitor => m !== undefined); - - return ( - - option.name} - value={selectedMonitors} - onChange={(_, newValue) => { - field.onChange(newValue.map((m: Monitor) => m.id)); - }} - fieldLabel={t( - "pages.maintenanceWindow.form.startTime.monitors.option.addMonitors.label" - )} - renderInput={(params) => ( - - )} - /> - {selectedMonitors.length > 0 && ( - - {selectedMonitors.map((monitor) => ( - - {monitor.name} - { - field.onChange( - field.value.filter((id: string) => id !== monitor.id) - ); - }} - aria-label="Remove monitor" - > - - - - ))} - + } + /> + + trigger("startDate")} + /> + + } + /> + + trigger("startDate")} + /> + + - ); - }} - /> - } - /> - - - - + + + + ); };