diff --git a/client/src/Pages/Settings/index.tsx b/client/src/Pages/Settings/index.tsx index c1b3ecdfa..c389c6159 100644 --- a/client/src/Pages/Settings/index.tsx +++ b/client/src/Pages/Settings/index.tsx @@ -1,9 +1,15 @@ import { BasePage, ConfigBox } from "@/Components/v2/design-elements"; import { Autocomplete, Select } from "@/Components/v2/inputs"; import { Stack, useTheme, MenuItem, type SelectChangeEvent } from "@mui/material"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; import { useTranslation } from "react-i18next"; import { useSelector, useDispatch } from "react-redux"; import DummyChart from "@/Pages/Settings/DummyChart"; +import { useGet } from "@/Hooks/UseApi"; +import { useSettingsForm } from "@/Hooks/useSettingsForm"; +import type { SettingsFormData } from "@/Validation/settings"; import { setTimezone, @@ -25,6 +31,23 @@ export const SettingsPage = () => { const theme = useTheme(); const { t, i18n } = useTranslation(); const dispatch = useDispatch(); + + // Fetch settings data from API + const { data: fetchedSettings } = useGet("/settings"); + + // Initialize form with schema and defaults + const { schema, defaults } = useSettingsForm({ data: fetchedSettings?.settings }); + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: defaults, + }); + + // Reset form when defaults change + useEffect(() => { + form.reset(defaults); + }, [defaults, form]); + const { timezone: selectedTimezoneId, mode, @@ -61,7 +84,7 @@ export const SettingsPage = () => { const languages = Object.keys(i18n.options.resources || {}); return ( - + - z.preprocess( - (val) => - val === "" || val === null || val === undefined ? undefined : String(val).trim(), - z.string().optional() - ); - -// Helper for threshold fields -const thresholdField = (min: number, max: number, unit: string) => - z.preprocess( - (val) => (val === "" || val === null || val === undefined ? undefined : val), - z.coerce - .number() - .int() - .min(min, `Min ${min}${unit}`) - .max(max, `Max ${max}${unit}`) - .optional() - ); - export const settingsSchema = z.object({ systemEmailIgnoreTLS: z.boolean(), systemEmailRequireTLS: z.boolean(), systemEmailRejectUnauthorized: z.boolean(), - systemEmailConnectionHost: optionalString(), + systemEmailConnectionHost: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), systemEmailSecure: z.boolean().optional(), systemEmailPool: z.boolean().optional(), showURL: z.boolean().optional(), - checkTTL: z.coerce - .number() - .int() - .min(1, "Please enter a value") - .max(365, "Maximum 365 days"), - pagespeedApiKey: optionalString(), - systemEmailHost: z.preprocess( - (val) => - val === "" || val === null || val === undefined ? undefined : String(val).trim(), - z - .string() - .regex(/^[a-zA-Z0-9.-]+$/, "Invalid hostname or IP address") - .optional() - ), - systemEmailPort: z.preprocess( - (val) => (val === "" || val === null || val === undefined ? undefined : val), - z.coerce - .number() - .int() - .min(1, "Port must be at least 1") - .max(65535, "Port must be at most 65535") - .optional() - ), - systemEmailAddress: z.preprocess((val) => { - if (val === "" || val === null || val === undefined) return undefined; - return String(val).toLowerCase().trim(); - }, z.string().email("Please enter a valid email address").optional()), - systemEmailUser: optionalString(), - systemEmailPassword: optionalString(), - systemEmailTLSServername: optionalString(), + checkTTL: z.number().int().min(1, "Please enter a value").max(365, "Maximum 365 days"), + pagespeedApiKey: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailHost: z + .string() + .regex(/^[a-zA-Z0-9.-]*$/, "Invalid hostname or IP address") + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailPort: z.number().int().min(1).max(65535).optional(), + systemEmailAddress: z + .string() + .email("Please enter a valid email address") + .or(z.literal("")) + .transform((val) => (val === "" ? undefined : val.toLowerCase().trim())) + .optional(), + systemEmailUser: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailPassword: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), + systemEmailTLSServername: z + .string() + .transform((val) => (val.trim() === "" ? undefined : val.trim())) + .optional(), globalThresholds: z .object({ - cpu: thresholdField(0, 100, "%"), - memory: thresholdField(0, 100, "%"), - disk: thresholdField(0, 100, "%"), - temperature: thresholdField(0, 150, "°C"), + cpu: z.number().int().min(0).max(100).optional(), + memory: z.number().int().min(0).max(100).optional(), + disk: z.number().int().min(0).max(100).optional(), + temperature: z.number().int().min(0).max(150).optional(), }) .optional(), });