diff --git a/src/lib/errors.ts b/src/lib/errors.ts new file mode 100644 index 000000000..29e07dad4 --- /dev/null +++ b/src/lib/errors.ts @@ -0,0 +1,13 @@ +import { isSeamHttpApiError } from '@seamapi/http/connect' + +export function getErrorMessage(error?: Error | null | undefined): string { + if (isSeamHttpApiError(error)) { + return error.message + } + + return t.anUnknownErrorOccurred +} + +const t = { + anUnknownErrorOccurred: 'An unknown error occurred', +} diff --git a/src/lib/ui/thermostat/ClimatePreset.tsx b/src/lib/ui/thermostat/ClimatePreset.tsx index 9309e6e4f..2d501dd04 100644 --- a/src/lib/ui/thermostat/ClimatePreset.tsx +++ b/src/lib/ui/thermostat/ClimatePreset.tsx @@ -8,6 +8,7 @@ import { } from 'react' import { Controller, useForm, type UseFormReturn } from 'react-hook-form' +import { getErrorMessage } from 'lib/errors.js' import type { FanModeSetting, HvacModeSetting, @@ -21,6 +22,7 @@ import { Button } from 'lib/ui/Button.js' import { FormField } from 'lib/ui/FormField.js' import { InputLabel } from 'lib/ui/InputLabel.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' +import { Snackbar } from 'lib/ui/Snackbar/Snackbar.js' import { TextField } from 'lib/ui/TextField/TextField.js' import { ClimateModeMenu } from 'lib/ui/thermostat/ClimateModeMenu.js' import { FanModeMenu } from 'lib/ui/thermostat/FanModeMenu.js' @@ -266,11 +268,14 @@ interface CreateFormProps { } function CreateForm({ device, onComplete }: CreateFormProps): JSX.Element { - const mutation = useCreateThermostatClimatePreset() + const { mutate, isError, error, isPending } = + useCreateThermostatClimatePreset() + + const errorMessage = getErrorMessage(error) const onSubmit = useCallback( (values: PresetFormProps['defaultValues']) => { - mutation.mutate( + mutate( { climate_preset_key: values.key, device_id: device.device_id, @@ -285,24 +290,33 @@ function CreateForm({ device, onComplete }: CreateFormProps): JSX.Element { { onSuccess: onComplete } ) }, - [device, mutation, onComplete] + [device, mutate, onComplete] ) return ( - + <> + + + + ) } @@ -317,11 +331,13 @@ function UpdateForm({ onComplete, preset, }: UpdateFormProps): JSX.Element { - const mutation = useUpdateThermostatClimatePreset() + const { mutate, isError, error, isPending } = + useUpdateThermostatClimatePreset() + const defaultValues = useMemo( () => ({ - coolPoint: preset.cooling_set_point_fahrenheit ?? 60, - heatPoint: preset.heating_set_point_fahrenheit ?? 80, + coolPoint: preset.cooling_set_point_fahrenheit, + heatPoint: preset.heating_set_point_fahrenheit, name: preset.display_name, hvacMode: preset.hvac_mode_setting, fanMode: preset.fan_mode_setting, @@ -332,7 +348,7 @@ function UpdateForm({ const onSubmit = useCallback( (values: PresetFormProps['defaultValues']) => { - mutation.mutate( + mutate( { climate_preset_key: values.key, device_id: device.device_id, @@ -347,16 +363,27 @@ function UpdateForm({ { onSuccess: onComplete } ) }, - [device, mutation, onComplete] + [device, mutate, onComplete] ) + const errorMessage = getErrorMessage(error) + return ( - + <> + + + + ) } @@ -370,4 +397,5 @@ const t = { delete: 'Delete', save: 'Save', crateNewPreset: 'Create New Climate Preset', + unknownErrorOccured: 'An unknown error occurred.', } diff --git a/src/lib/ui/thermostat/ClimatePresets.tsx b/src/lib/ui/thermostat/ClimatePresets.tsx index 1ec7dfbb4..576caa77d 100644 --- a/src/lib/ui/thermostat/ClimatePresets.tsx +++ b/src/lib/ui/thermostat/ClimatePresets.tsx @@ -1,6 +1,7 @@ import classNames from 'classnames' import { type HTMLAttributes, type ReactNode, useState } from 'react' +import { getErrorMessage } from 'lib/errors.js' import { AddIcon } from 'lib/icons/Add.js' import { EditIcon } from 'lib/icons/Edit.js' import { FanIcon } from 'lib/icons/Fan.js' @@ -18,6 +19,7 @@ import { IconButton } from 'lib/ui/IconButton.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' import { Popover } from 'lib/ui/Popover/Popover.js' import { PopoverContentPrompt } from 'lib/ui/Popover/PopoverContentPrompt.js' +import { Snackbar } from 'lib/ui/Snackbar/Snackbar.js' import { Spinner } from 'lib/ui/Spinner/Spinner.js' import { ClimatePreset } from 'lib/ui/thermostat/ClimatePreset.js' @@ -40,7 +42,11 @@ export function ClimatePresets(props: ClimatePresetsManagement): JSX.Element { climatePresetKeySelectedForDeletion, setClimatePresetKeySelectedForDeletion, ] = useState(null) - const deleteMutation = useDeleteThermostatClimatePreset() + + const { mutate, isError, error, isPending } = + useDeleteThermostatClimatePreset() + + const errorMessage = getErrorMessage(error) if ( selectedClimatePreset != null || @@ -62,52 +68,61 @@ export function ClimatePresets(props: ClimatePresetsManagement): JSX.Element { } return ( -
- -
- - -
- {device.properties.available_climate_presets.map((preset) => ( - { - setSelectedClimatePreset(preset) - }} - onClickDelete={() => { - setClimatePresetKeySelectedForDeletion( - preset.climate_preset_key - ) - deleteMutation.mutate({ - climate_preset_key: preset.climate_preset_key, - device_id: device.device_id, - }) - }} - temperatureUnit={props.temperatureUnit} - preset={preset} - key={preset.climate_preset_key} - deletionLoading={ - deleteMutation.isPending && - climatePresetKeySelectedForDeletion === - preset.climate_preset_key - } - disabled={ - deleteMutation.isPending && - climatePresetKeySelectedForDeletion !== - preset.climate_preset_key - } - /> - ))} + <> + + +
+ +
+ + +
+ {device.properties.available_climate_presets.map((preset) => ( + { + setSelectedClimatePreset(preset) + }} + onClickDelete={() => { + setClimatePresetKeySelectedForDeletion( + preset.climate_preset_key + ) + mutate({ + climate_preset_key: preset.climate_preset_key, + device_id: device.device_id, + }) + }} + temperatureUnit={props.temperatureUnit} + preset={preset} + key={preset.climate_preset_key} + deletionLoading={ + isPending && + climatePresetKeySelectedForDeletion === + preset.climate_preset_key + } + disabled={ + isPending && + climatePresetKeySelectedForDeletion !== + preset.climate_preset_key + } + /> + ))} +
-
+ ) } @@ -232,4 +247,5 @@ const t = { createNew: 'Create New', delete: 'Delete', edit: 'Edit', + climatePresetNotFound: 'Climate Preset not found.', }