Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/lib/errors.ts
Original file line number Diff line number Diff line change
@@ -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',
}
84 changes: 56 additions & 28 deletions src/lib/ui/thermostat/ClimatePreset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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'
Expand Down Expand Up @@ -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,
Expand All @@ -285,24 +290,33 @@ function CreateForm({ device, onComplete }: CreateFormProps): JSX.Element {
{ onSuccess: onComplete }
)
},
[device, mutation, onComplete]
[device, mutate, onComplete]
)

return (
<PresetForm
defaultValues={{
key: '',
coolPoint: 60,
heatPoint: 80,
name: '',
hvacMode: 'off',
fanMode: 'auto',
}}
device={device}
loading={mutation.isPending}
onSubmit={onSubmit}
withKeyField
/>
<>
<Snackbar
message={errorMessage}
variant='error'
visible={isError}
automaticVisibility
/>

<PresetForm
defaultValues={{
key: '',
coolPoint: 60,
heatPoint: 80,
name: '',
hvacMode: 'off',
fanMode: 'auto',
}}
device={device}
loading={isPending}
onSubmit={onSubmit}
withKeyField
/>
</>
)
}

Expand All @@ -317,11 +331,13 @@ function UpdateForm({
onComplete,
preset,
}: UpdateFormProps): JSX.Element {
const mutation = useUpdateThermostatClimatePreset()
const { mutate, isError, error, isPending } =
useUpdateThermostatClimatePreset()

const defaultValues = useMemo<PresetFormProps['defaultValues']>(
() => ({
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,
Expand All @@ -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,
Expand All @@ -347,16 +363,27 @@ function UpdateForm({
{ onSuccess: onComplete }
)
},
[device, mutation, onComplete]
[device, mutate, onComplete]
)

const errorMessage = getErrorMessage(error)

return (
<PresetForm
defaultValues={defaultValues}
device={device}
loading={mutation.isPending}
onSubmit={onSubmit}
/>
<>
<Snackbar
message={errorMessage}
variant='error'
visible={isError}
automaticVisibility
/>

<PresetForm
defaultValues={defaultValues}
device={device}
loading={isPending}
onSubmit={onSubmit}
/>
</>
)
}

Expand All @@ -370,4 +397,5 @@ const t = {
delete: 'Delete',
save: 'Save',
crateNewPreset: 'Create New Climate Preset',
unknownErrorOccured: 'An unknown error occurred.',
}
106 changes: 61 additions & 45 deletions src/lib/ui/thermostat/ClimatePresets.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'

Expand All @@ -40,7 +42,11 @@ export function ClimatePresets(props: ClimatePresetsManagement): JSX.Element {
climatePresetKeySelectedForDeletion,
setClimatePresetKeySelectedForDeletion,
] = useState<ThermostatClimatePreset['climate_preset_key'] | null>(null)
const deleteMutation = useDeleteThermostatClimatePreset()

const { mutate, isError, error, isPending } =
useDeleteThermostatClimatePreset()

const errorMessage = getErrorMessage(error)

if (
selectedClimatePreset != null ||
Expand All @@ -62,52 +68,61 @@ export function ClimatePresets(props: ClimatePresetsManagement): JSX.Element {
}

return (
<div className='seam-thermostat-climate-presets'>
<ContentHeader title={t.title} onBack={onBack} />
<div className='seam-thermostat-climate-presets-body'>
<Button
onClick={() => {
setSelectedClimatePreset(CreateNewPresetSymbol)
}}
className='seam-climate-presets-add-button'
>
<AddIcon />
{t.createNew}
</Button>

<div className='seam-thermostat-climate-presets-cards'>
{device.properties.available_climate_presets.map((preset) => (
<PresetCard
onClickEdit={() => {
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
}
/>
))}
<>
<Snackbar
message={errorMessage}
variant='error'
visible={isError}
automaticVisibility
/>

<div className='seam-thermostat-climate-presets'>
<ContentHeader title={t.title} onBack={onBack} />
<div className='seam-thermostat-climate-presets-body'>
<Button
onClick={() => {
setSelectedClimatePreset(CreateNewPresetSymbol)
}}
className='seam-climate-presets-add-button'
>
<AddIcon />
{t.createNew}
</Button>

<div className='seam-thermostat-climate-presets-cards'>
{device.properties.available_climate_presets.map((preset) => (
<PresetCard
onClickEdit={() => {
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
}
/>
))}
</div>
</div>
</div>
</div>
</>
)
}

Expand Down Expand Up @@ -232,4 +247,5 @@ const t = {
createNew: 'Create New',
delete: 'Delete',
edit: 'Edit',
climatePresetNotFound: 'Climate Preset not found.',
}
Loading