diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx index 7b26d94dc..c3955d286 100644 --- a/interface/src/SignIn.tsx +++ b/interface/src/SignIn.tsx @@ -18,7 +18,7 @@ import { PROJECT_NAME } from 'env'; import { useI18nContext } from 'i18n/i18n-react'; import type { SignInRequest } from 'types'; import { onEnterCallback, updateValue } from 'utils'; -import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators'; +import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators'; const SignIn = memo(() => { const authenticationContext = useContext(AuthenticationContext); @@ -74,7 +74,7 @@ const SignIn = memo(() => { await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest); await signIn(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); setProcessing(false); } }, [signInRequest, signIn, LL]); diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index 170c5e888..b40e4aa25 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -28,7 +28,7 @@ import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import type { EntityItem } from './types'; @@ -136,7 +136,7 @@ const CustomEntitiesDialog = ({ } onSave(processedItem); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [validator, editItem, onSave]); @@ -215,7 +215,7 @@ const CustomEntitiesDialog = ({ name="value" label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)} type="string" - value={editItem.value as string} + value={editItem.value} variant="outlined" onChange={updateFormValue} fullWidth @@ -260,7 +260,7 @@ const CustomEntitiesDialog = ({ margin="normal" sx={{ width: '11ch' }} type="string" - value={editItem.device_id as string} + value={editItem.device_id} onChange={updateFormValue} slotProps={{ input: { @@ -280,7 +280,7 @@ const CustomEntitiesDialog = ({ margin="normal" sx={{ width: '11ch' }} type="string" - value={editItem.type_id as string} + value={editItem.type_id} onChange={updateFormValue} slotProps={{ input: { @@ -381,7 +381,7 @@ const CustomEntitiesDialog = ({ fieldErrors={fieldErrors || {}} name="factor" label={LL.BITMASK()} - value={editItem.factor as string} + value={editItem.factor} sx={{ width: '11ch' }} variant="outlined" onChange={updateFormValue} diff --git a/interface/src/app/main/DevicesDialog.tsx b/interface/src/app/main/DevicesDialog.tsx index 5afa96666..fd2770e7d 100644 --- a/interface/src/app/main/DevicesDialog.tsx +++ b/interface/src/app/main/DevicesDialog.tsx @@ -24,7 +24,7 @@ import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { DeviceValueUOM, DeviceValueUOM_s } from './types'; import type { DeviceValue } from './types'; @@ -67,7 +67,7 @@ const DevicesDialog = ({ await validate(validator, editItem); onSave(editItem); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [validator, editItem, onSave]); diff --git a/interface/src/app/main/SchedulerDialog.tsx b/interface/src/app/main/SchedulerDialog.tsx index 8277da6e3..27713de86 100644 --- a/interface/src/app/main/SchedulerDialog.tsx +++ b/interface/src/app/main/SchedulerDialog.tsx @@ -26,7 +26,7 @@ import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { ScheduleFlag } from './types'; import type { ScheduleItem } from './types'; @@ -120,7 +120,7 @@ const SchedulerDialog = ({ await validate(validator, itemToSave); onSave(itemToSave); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [validator, onSave] diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx index 10b49fbf8..ffc5a4485 100644 --- a/interface/src/app/main/SensorsAnalogDialog.tsx +++ b/interface/src/app/main/SensorsAnalogDialog.tsx @@ -23,7 +23,7 @@ import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types'; import type { AnalogSensor } from './types'; @@ -172,7 +172,7 @@ const SensorsAnalogDialog = ({ await validate(validator, editItem); onSave(editItem); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [validator, editItem, onSave]); diff --git a/interface/src/app/main/SensorsTemperatureDialog.tsx b/interface/src/app/main/SensorsTemperatureDialog.tsx index 31cdad43b..670b34244 100644 --- a/interface/src/app/main/SensorsTemperatureDialog.tsx +++ b/interface/src/app/main/SensorsTemperatureDialog.tsx @@ -21,7 +21,7 @@ import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import type { TemperatureSensor } from './types'; @@ -84,7 +84,7 @@ const SensorsTemperatureDialog = ({ await validate(validator, editItem); onSave(editItem); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [validator, editItem, onSave]); diff --git a/interface/src/app/settings/APSettings.tsx b/interface/src/app/settings/APSettings.tsx index 593d0e45d..7d8c8875c 100644 --- a/interface/src/app/settings/APSettings.tsx +++ b/interface/src/app/settings/APSettings.tsx @@ -21,7 +21,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import type { APSettingsType } from 'types'; import { APProvisionMode } from 'types'; import { numberValue, updateValueDirty, useRest } from 'utils'; -import { createAPSettingsValidator, validate } from 'validators'; +import { ValidationError, createAPSettingsValidator, validate } from 'validators'; export const isAPEnabled = ({ provision_mode }: APSettingsType) => provision_mode === APProvisionMode.AP_MODE_ALWAYS || @@ -86,7 +86,7 @@ const APSettings = () => { await validate(createAPSettingsValidator(data), data); await saveData(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [data, saveData]); diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx index 68519d7b5..cae0b1e1b 100644 --- a/interface/src/app/settings/ApplicationSettings.tsx +++ b/interface/src/app/settings/ApplicationSettings.tsx @@ -34,7 +34,7 @@ import { } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValueDirty, useRest } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app'; import { BOARD_PROFILES } from '../main/types'; @@ -154,7 +154,7 @@ const ApplicationSettings = () => { setFieldErrors(undefined); await validate(createSettingsValidator(data), data); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } finally { await saveData(); } diff --git a/interface/src/app/settings/MqttSettings.tsx b/interface/src/app/settings/MqttSettings.tsx index a2ae1c236..417fa8190 100644 --- a/interface/src/app/settings/MqttSettings.tsx +++ b/interface/src/app/settings/MqttSettings.tsx @@ -31,7 +31,7 @@ import { import { useI18nContext } from 'i18n/i18n-react'; import type { MqttSettingsType } from 'types'; import { numberValue, updateValueDirty, useRest } from 'utils'; -import { createMqttSettingsValidator, validate } from 'validators'; +import { ValidationError, createMqttSettingsValidator, validate } from 'validators'; import { callAction } from '../../api/app'; @@ -94,7 +94,7 @@ const MqttSettings = () => { await validate(createMqttSettingsValidator(data), data); await saveData(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [data, saveData]); diff --git a/interface/src/app/settings/NTPSettings.tsx b/interface/src/app/settings/NTPSettings.tsx index 9b36e417d..ff517a266 100644 --- a/interface/src/app/settings/NTPSettings.tsx +++ b/interface/src/app/settings/NTPSettings.tsx @@ -36,7 +36,7 @@ import { import { useI18nContext } from 'i18n/i18n-react'; import type { NTPSettingsType, Time } from 'types'; import { formatLocalDateTime, updateValueDirty, useRest } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp'; import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ'; @@ -133,7 +133,7 @@ const NTPSettings = () => { await validate(NTP_SETTINGS_VALIDATOR, data); await saveData(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [data, saveData]); diff --git a/interface/src/app/settings/network/NetworkSettings.tsx b/interface/src/app/settings/network/NetworkSettings.tsx index 92651c654..5f96aa594 100644 --- a/interface/src/app/settings/network/NetworkSettings.tsx +++ b/interface/src/app/settings/network/NetworkSettings.tsx @@ -40,7 +40,7 @@ import { import { useI18nContext } from 'i18n/i18n-react'; import type { NetworkSettingsType } from 'types'; import { updateValueDirty, useRest } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; import { createNetworkSettingsValidator } from 'validators/network'; import SystemMonitor from '../../status/SystemMonitor'; @@ -116,7 +116,7 @@ const NetworkSettings = () => { await validate(createNetworkSettingsValidator(data), data); await saveData(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } deselectNetwork(); }, [data, saveData, deselectNetwork]); diff --git a/interface/src/app/settings/security/SecuritySettings.tsx b/interface/src/app/settings/security/SecuritySettings.tsx index b894a0e0a..117a84467 100644 --- a/interface/src/app/settings/security/SecuritySettings.tsx +++ b/interface/src/app/settings/security/SecuritySettings.tsx @@ -19,7 +19,7 @@ import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; import type { SecuritySettingsType } from 'types'; import { updateValueDirty, useRest } from 'utils'; -import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators'; +import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators'; const SecuritySettings = () => { const { LL } = useI18nContext(); @@ -58,7 +58,7 @@ const SecuritySettings = () => { await saveData(); await authenticatedContext.refresh(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } }, [data, saveData, authenticatedContext]); diff --git a/interface/src/app/settings/security/User.tsx b/interface/src/app/settings/security/User.tsx index 68f740647..bf1ce8862 100644 --- a/interface/src/app/settings/security/User.tsx +++ b/interface/src/app/settings/security/User.tsx @@ -24,7 +24,7 @@ import { import { useI18nContext } from 'i18n/i18n-react'; import type { UserType } from 'types'; import { updateValue } from 'utils'; -import { validate } from 'validators'; +import { ValidationError, validate } from 'validators'; interface UserFormProps { creating: boolean; @@ -69,7 +69,7 @@ const User: FC = ({ await validate(validator, user); onDoneEditing(); } catch (error) { - setFieldErrors(error as ValidateFieldsError); + setFieldErrors((error as ValidationError).fieldErrors); } } }, [user, validator, onDoneEditing]); diff --git a/interface/src/validators/shared.ts b/interface/src/validators/shared.ts index d1132d184..a0da4beb3 100644 --- a/interface/src/validators/shared.ts +++ b/interface/src/validators/shared.ts @@ -1,6 +1,20 @@ -import type { InternalRuleItem, ValidateOption } from 'async-validator'; +import type { + InternalRuleItem, + ValidateFieldsError, + ValidateOption +} from 'async-validator'; import type Schema from 'async-validator'; +export class ValidationError extends Error { + readonly fieldErrors: ValidateFieldsError; + + constructor(fieldErrors: ValidateFieldsError) { + super('Validation failed'); + this.name = 'ValidationError'; + this.fieldErrors = fieldErrors; + } +} + export const validate = ( validator: Schema, source: Partial, @@ -8,7 +22,7 @@ export const validate = ( ): Promise => new Promise((resolve, reject) => { void validator.validate(source, options ?? {}, (errors, fieldErrors) => { - errors ? reject(fieldErrors as Error) : resolve(source as T); + errors ? reject(new ValidationError(fieldErrors)) : resolve(source as T); }); });