@seamapi/react 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +3 -3
  2. package/dist/elements.js +2917 -2829
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +60 -0
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.min.css +1 -1
  7. package/dist/index.min.css.map +1 -1
  8. package/lib/seam/access-codes/use-access-codes.js +11 -1
  9. package/lib/seam/access-codes/use-access-codes.js.map +1 -1
  10. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.d.ts +6 -0
  11. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js +21 -0
  12. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js.map +1 -0
  13. package/lib/seam/components/SupportedDeviceTable/use-device-models.js +12 -1
  14. package/lib/seam/components/SupportedDeviceTable/use-device-models.js.map +1 -1
  15. package/lib/seam/components/SupportedDeviceTable/use-manufacturers.js +12 -1
  16. package/lib/seam/components/SupportedDeviceTable/use-manufacturers.js.map +1 -1
  17. package/lib/seam/components/index.d.ts +1 -0
  18. package/lib/seam/components/index.js +1 -0
  19. package/lib/seam/components/index.js.map +1 -1
  20. package/lib/seam/devices/use-devices.js +0 -1
  21. package/lib/seam/devices/use-devices.js.map +1 -1
  22. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js +0 -1
  23. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js.map +1 -1
  24. package/lib/seam/thermostats/use-update-fan-mode.d.ts +6 -0
  25. package/lib/seam/thermostats/use-update-fan-mode.js +42 -0
  26. package/lib/seam/thermostats/use-update-fan-mode.js.map +1 -0
  27. package/lib/seam/thermostats/use-update-thermostat.d.ts +6 -0
  28. package/lib/seam/thermostats/use-update-thermostat.js +50 -0
  29. package/lib/seam/thermostats/use-update-thermostat.js.map +1 -0
  30. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.d.ts +6 -1
  31. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js +39 -6
  32. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js.map +1 -1
  33. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.d.ts +13 -0
  34. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js +37 -0
  35. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js.map +1 -0
  36. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.d.ts +10 -0
  37. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js +24 -0
  38. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js.map +1 -0
  39. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.d.ts +1 -2
  40. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js +1 -2
  41. package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js.map +1 -1
  42. package/lib/ui/thermostat/ClimateSettingControlGroup.d.ts +9 -0
  43. package/lib/ui/thermostat/ClimateSettingControlGroup.js +8 -0
  44. package/lib/ui/thermostat/ClimateSettingControlGroup.js.map +1 -0
  45. package/lib/ui/thermostat/TemperatureControlGroup.d.ts +6 -7
  46. package/lib/ui/thermostat/TemperatureControlGroup.js +1 -1
  47. package/lib/ui/thermostat/TemperatureControlGroup.js.map +1 -1
  48. package/lib/version.d.ts +1 -1
  49. package/lib/version.js +1 -1
  50. package/package.json +2 -2
  51. package/src/lib/seam/access-codes/use-access-codes.ts +15 -1
  52. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.element.ts +9 -0
  53. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.tsx +65 -0
  54. package/src/lib/seam/components/SupportedDeviceTable/use-device-models.ts +15 -1
  55. package/src/lib/seam/components/SupportedDeviceTable/use-manufacturers.ts +15 -1
  56. package/src/lib/seam/components/elements.ts +1 -0
  57. package/src/lib/seam/components/index.ts +1 -0
  58. package/src/lib/seam/devices/use-devices.ts +0 -1
  59. package/src/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.ts +0 -1
  60. package/src/lib/seam/thermostats/use-update-fan-mode.ts +81 -0
  61. package/src/lib/seam/thermostats/use-update-thermostat.ts +92 -0
  62. package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.tsx +77 -15
  63. package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.tsx +103 -0
  64. package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.tsx +79 -0
  65. package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.tsx +0 -3
  66. package/src/lib/ui/thermostat/ClimateSettingControlGroup.tsx +32 -0
  67. package/src/lib/ui/thermostat/TemperatureControlGroup.tsx +11 -11
  68. package/src/lib/version.ts +1 -1
  69. package/src/styles/_climate-setting-schedule-form.scss +54 -0
  70. package/src/styles/_main.scss +2 -0
  71. package/src/styles/_supported-device-table-manufacturer-keys.scss +19 -0
  72. package/src/styles/_thermostat.scss +15 -0
@@ -1,4 +1,4 @@
1
- import { useQuery } from '@tanstack/react-query'
1
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
2
2
  import type {
3
3
  AccessCode,
4
4
  AccessCodesListRequest,
@@ -19,6 +19,8 @@ export function useAccessCodes(
19
19
  typeof params === 'string' ? { device_id: params } : params
20
20
 
21
21
  const { client } = useSeamClient()
22
+ const queryClient = useQueryClient()
23
+
22
24
  const { data, ...rest } = useQuery<
23
25
  AccessCodesListResponse['access_codes'],
24
26
  SeamError
@@ -29,6 +31,18 @@ export function useAccessCodes(
29
31
  if (client == null) return []
30
32
  return await client.accessCodes.list(normalizedParams)
31
33
  },
34
+ onSuccess: (accessCodes) => {
35
+ for (const accessCode of accessCodes) {
36
+ queryClient.setQueryData(
37
+ [
38
+ 'access_codes',
39
+ 'get',
40
+ { access_code_id: accessCode.access_code_id },
41
+ ],
42
+ accessCode
43
+ )
44
+ }
45
+ },
32
46
  })
33
47
 
34
48
  return { ...rest, accessCodes: data }
@@ -0,0 +1,9 @@
1
+ import type { ElementProps } from 'lib/element.js'
2
+
3
+ import type { SupportedDeviceTableManufacturerKeysProps } from './SupportedDeviceTableManufacturerKeys.js'
4
+
5
+ export const name = 'seam-supported-device-table-manufacturer-keys'
6
+
7
+ export const props: ElementProps<SupportedDeviceTableManufacturerKeysProps> = {}
8
+
9
+ export { SupportedDeviceTableManufacturerKeys as Component } from './SupportedDeviceTableManufacturerKeys.js'
@@ -0,0 +1,65 @@
1
+ import type { Manufacturer } from '@seamapi/types/devicedb'
2
+ import classNames from 'classnames'
3
+
4
+ import { useComponentTelemetry } from 'lib/telemetry/index.js'
5
+
6
+ import { CopyIcon } from 'lib/icons/Copy.js'
7
+ import {
8
+ type CommonProps,
9
+ withRequiredCommonProps,
10
+ } from 'lib/seam/components/common-props.js'
11
+ import { copyToClipboard } from 'lib/ui/clipboard.js'
12
+ import { MenuItem } from 'lib/ui/Menu/MenuItem.js'
13
+
14
+ import { useManufacturers } from './use-manufacturers.js'
15
+
16
+ export interface SupportedDeviceTableManufacturerKeysProps
17
+ extends CommonProps {}
18
+
19
+ export const NestedSupportedDeviceTableManufacturerKeys =
20
+ withRequiredCommonProps(SupportedDeviceTableManufacturerKeys)
21
+
22
+ export function SupportedDeviceTableManufacturerKeys({
23
+ className,
24
+ }: SupportedDeviceTableManufacturerKeysProps = {}): JSX.Element {
25
+ useComponentTelemetry('SupportedDeviceTableManufacturerKeys')
26
+
27
+ const { manufacturers } = useManufacturers()
28
+
29
+ return (
30
+ <div
31
+ className={classNames(
32
+ 'supported-device-table-manufacturer-keys',
33
+ className
34
+ )}
35
+ >
36
+ {manufacturers?.map((manufacturer) => (
37
+ <ManufacturerKey
38
+ key={manufacturer.manufacturer_id}
39
+ manufacturer={manufacturer}
40
+ />
41
+ ))}
42
+ </div>
43
+ )
44
+ }
45
+
46
+ function ManufacturerKey({
47
+ manufacturer,
48
+ }: {
49
+ manufacturer: Manufacturer
50
+ }): JSX.Element {
51
+ const key = manufacturer.display_name
52
+ return (
53
+ <div className='seam-manufacturer-key'>
54
+ <div className='seam-manufacturer-key-value'>{key}</div>
55
+ <MenuItem
56
+ className='seam-copy-button'
57
+ onClick={() => {
58
+ void copyToClipboard(key)
59
+ }}
60
+ >
61
+ <CopyIcon />
62
+ </MenuItem>
63
+ </div>
64
+ )
65
+ }
@@ -3,7 +3,7 @@ import type {
3
3
  RouteRequestParams,
4
4
  RouteResponse,
5
5
  } from '@seamapi/types/devicedb'
6
- import { useQuery } from '@tanstack/react-query'
6
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
7
7
  import type { SeamError } from 'seamapi'
8
8
 
9
9
  import { useSeamClient } from 'lib/seam/use-seam-client.js'
@@ -16,6 +16,7 @@ export function useDeviceModels(
16
16
  params?: UseDeviceModelsParams
17
17
  ): UseSeamQueryResult<'deviceModels', UseDeviceModelsData> {
18
18
  const { client: seam } = useSeamClient()
19
+ const queryClient = useQueryClient()
19
20
 
20
21
  const { data, ...rest } = useQuery<
21
22
  DeviceModelsListResponse['device_models'],
@@ -33,6 +34,19 @@ export function useDeviceModels(
33
34
  )
34
35
  return deviceModels
35
36
  },
37
+ onSuccess: (deviceModels) => {
38
+ for (const deviceModel of deviceModels) {
39
+ queryClient.setQueryData(
40
+ [
41
+ 'internal',
42
+ 'device_models',
43
+ 'get',
44
+ { device_model_id: deviceModel.device_model_id },
45
+ ],
46
+ deviceModel
47
+ )
48
+ }
49
+ },
36
50
  })
37
51
 
38
52
  return { ...rest, deviceModels: data }
@@ -3,7 +3,7 @@ import type {
3
3
  RouteRequestParams,
4
4
  RouteResponse,
5
5
  } from '@seamapi/types/devicedb'
6
- import { useQuery } from '@tanstack/react-query'
6
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
7
7
  import type { SeamError } from 'seamapi'
8
8
 
9
9
  import { useSeamClient } from 'lib/seam/use-seam-client.js'
@@ -16,6 +16,7 @@ export function useManufacturers(
16
16
  params?: UseManufacturersParams
17
17
  ): UseSeamQueryResult<'manufacturers', UseManufacturersData> {
18
18
  const { client: seam } = useSeamClient()
19
+ const queryClient = useQueryClient()
19
20
 
20
21
  const { data, ...rest } = useQuery<
21
22
  ManufacturersListResponse['manufacturers'],
@@ -33,6 +34,19 @@ export function useManufacturers(
33
34
  )
34
35
  return manufacturers
35
36
  },
37
+ onSuccess: (manufacturers) => {
38
+ for (const manufacturer of manufacturers) {
39
+ queryClient.setQueryData(
40
+ [
41
+ 'internal',
42
+ 'manufacturers',
43
+ 'get',
44
+ { manufacturer_id: manufacturer.manufacturer_id },
45
+ ],
46
+ manufacturer
47
+ )
48
+ }
49
+ },
36
50
  })
37
51
 
38
52
  return { ...rest, manufacturers: data }
@@ -8,3 +8,4 @@ export * as DeviceDetails from './DeviceDetails/DeviceDetails.element.js'
8
8
  export * as DeviceTable from './DeviceTable/DeviceTable.element.js'
9
9
  export * as EditAccessCodeForm from './EditAccessCodeForm/EditAccessCodeForm.element.js'
10
10
  export * as SupportedDeviceTable from './SupportedDeviceTable/SupportedDeviceTable.element.js'
11
+ export * as SupportedDeviceTableManufacturerKeys from './SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.element.js'
@@ -9,3 +9,4 @@ export * from './DeviceDetails/DeviceDetails.js'
9
9
  export * from './DeviceTable/DeviceTable.js'
10
10
  export * from './EditAccessCodeForm/EditAccessCodeForm.js'
11
11
  export * from './SupportedDeviceTable/SupportedDeviceTable.js'
12
+ export * from './SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js'
@@ -27,7 +27,6 @@ export function useDevices(
27
27
  return await client.devices.list(params)
28
28
  },
29
29
  onSuccess: (devices) => {
30
- // Prime cache for each device.
31
30
  for (const device of devices) {
32
31
  queryClient.setQueryData(
33
32
  ['devices', 'get', { device_id: device.device_id }],
@@ -33,7 +33,6 @@ export function useClimateSettingSchedules(
33
33
  return await client.thermostats.climateSettingSchedules.list(params)
34
34
  },
35
35
  onSuccess: (schedules) => {
36
- // Prime cache for each schedule.
37
36
  for (const schedule of schedules) {
38
37
  queryClient.setQueryData(
39
38
  [
@@ -0,0 +1,81 @@
1
+ import {
2
+ useMutation,
3
+ type UseMutationResult,
4
+ useQueryClient,
5
+ } from '@tanstack/react-query'
6
+ import type {
7
+ SeamError,
8
+ ThermostatDevice,
9
+ ThermostatSetFanModeRequest,
10
+ ThermostatsListResponse,
11
+ } from 'seamapi'
12
+
13
+ import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
14
+
15
+ // UPSTREAM: Missing ThermostatSetFanModeResponse in seamapi.
16
+ type UseUpdateFanModeData = Record<string, unknown>
17
+ type UseUpdateFanModeMutationParams = ThermostatSetFanModeRequest
18
+
19
+ export function useUpdateFanMode(): UseMutationResult<
20
+ UseUpdateFanModeData,
21
+ SeamError,
22
+ UseUpdateFanModeMutationParams
23
+ > {
24
+ const { client } = useSeamClient()
25
+ const queryClient = useQueryClient()
26
+
27
+ return useMutation<
28
+ UseUpdateFanModeData,
29
+ SeamError,
30
+ UseUpdateFanModeMutationParams
31
+ >({
32
+ mutationFn: async (mutationParams: UseUpdateFanModeMutationParams) => {
33
+ if (client === null) throw new NullSeamClientError()
34
+
35
+ return await client.thermostats.setFanMode(mutationParams)
36
+ },
37
+ onSuccess: (_data, variables) => {
38
+ queryClient.setQueryData<ThermostatDevice | null>(
39
+ ['thermostats', 'get', { device_id: variables.device_id }],
40
+ (thermostat) => {
41
+ if (thermostat == null) {
42
+ return
43
+ }
44
+
45
+ return getUpdatedThermostat(thermostat, variables)
46
+ }
47
+ )
48
+
49
+ queryClient.setQueryData<ThermostatsListResponse['thermostats']>(
50
+ ['thermostats', 'list', { device_id: variables.device_id }],
51
+ (thermostats): ThermostatDevice[] => {
52
+ if (thermostats == null) {
53
+ return []
54
+ }
55
+
56
+ return thermostats.map((thermostat) => {
57
+ if (thermostat.device_id === variables.device_id) {
58
+ return getUpdatedThermostat(thermostat, variables)
59
+ }
60
+
61
+ return thermostat
62
+ })
63
+ }
64
+ )
65
+ },
66
+ })
67
+ }
68
+
69
+ function getUpdatedThermostat(
70
+ thermostat: ThermostatDevice,
71
+ variables: UseUpdateFanModeMutationParams
72
+ ): ThermostatDevice {
73
+ return {
74
+ ...thermostat,
75
+ properties: {
76
+ ...thermostat.properties,
77
+ fan_mode_setting:
78
+ variables.fan_mode_setting ?? thermostat.properties.fan_mode_setting,
79
+ },
80
+ }
81
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ useMutation,
3
+ type UseMutationResult,
4
+ useQueryClient,
5
+ } from '@tanstack/react-query'
6
+ import type {
7
+ SeamError,
8
+ ThermostatDevice,
9
+ ThermostatsListResponse,
10
+ ThermostatUpdateRequest,
11
+ } from 'seamapi'
12
+
13
+ import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
14
+
15
+ // UPSTREAM: Missing ThermostatUpdateResponse in seamapi.
16
+ type UseUpdateThermostatData = Record<string, unknown>
17
+ type UseUpdateThermostatMutationParams = ThermostatUpdateRequest
18
+
19
+ export function useUpdateThermostat(): UseMutationResult<
20
+ UseUpdateThermostatData,
21
+ SeamError,
22
+ UseUpdateThermostatMutationParams
23
+ > {
24
+ const { client } = useSeamClient()
25
+ const queryClient = useQueryClient()
26
+
27
+ return useMutation<
28
+ UseUpdateThermostatData,
29
+ SeamError,
30
+ UseUpdateThermostatMutationParams
31
+ >({
32
+ mutationFn: async (mutationParams: UseUpdateThermostatMutationParams) => {
33
+ if (client === null) throw new NullSeamClientError()
34
+
35
+ return await client.thermostats.update(mutationParams)
36
+ },
37
+ onSuccess: (_data, variables) => {
38
+ queryClient.setQueryData<ThermostatDevice | null>(
39
+ ['thermostats', 'get', { device_id: variables.device_id }],
40
+ (thermostat) => {
41
+ if (thermostat == null) {
42
+ return
43
+ }
44
+
45
+ return getUpdatedThermostat(thermostat, variables)
46
+ }
47
+ )
48
+
49
+ queryClient.setQueryData<ThermostatsListResponse['thermostats']>(
50
+ ['thermostats', 'list', { device_id: variables.device_id }],
51
+ (thermostats): ThermostatDevice[] => {
52
+ if (thermostats == null) {
53
+ return []
54
+ }
55
+
56
+ return thermostats.map((thermostat) => {
57
+ if (thermostat.device_id === variables.device_id) {
58
+ return getUpdatedThermostat(thermostat, variables)
59
+ }
60
+
61
+ return thermostat
62
+ })
63
+ }
64
+ )
65
+ },
66
+ })
67
+ }
68
+
69
+ function getUpdatedThermostat(
70
+ thermostat: ThermostatDevice,
71
+ variables: UseUpdateThermostatMutationParams
72
+ ): ThermostatDevice {
73
+ return {
74
+ ...thermostat,
75
+
76
+ properties: {
77
+ ...thermostat.properties,
78
+
79
+ default_climate_setting: {
80
+ ...thermostat.properties.default_climate_setting,
81
+ manual_override_allowed:
82
+ thermostat.properties?.default_climate_setting
83
+ ?.manual_override_allowed != null
84
+ ? thermostat.properties.default_climate_setting
85
+ .manual_override_allowed
86
+ : true,
87
+
88
+ ...variables.default_climate_setting,
89
+ },
90
+ },
91
+ }
92
+ }
@@ -1,9 +1,17 @@
1
1
  import classNames from 'classnames'
2
- import { useState } from 'react'
2
+ import { useEffect, useState } from 'react'
3
3
  import { useForm } from 'react-hook-form'
4
- import type { ClimateSetting } from 'seamapi'
4
+ import {
5
+ type ClimateSetting,
6
+ type HvacModeSetting,
7
+ isThermostatDevice,
8
+ } from 'seamapi'
9
+
10
+ import { useDevice } from 'lib/index.js'
5
11
 
6
12
  import { getSystemTimeZone } from 'lib/dates.js'
13
+ import { ClimateSettingScheduleFormClimateSetting } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js'
14
+ import { ClimateSettingScheduleFormDefaultClimateSetting } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js'
7
15
  import { ClimateSettingScheduleFormDeviceSelect } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js'
8
16
  import { ClimateSettingScheduleFormNameAndSchedule } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormNameAndSchedule.js'
9
17
  import { ClimateSettingScheduleFormTimeZonePicker } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormTimeZonePicker.js'
@@ -30,6 +38,12 @@ export interface ClimateSettingScheduleFormFields {
30
38
  startDate: string
31
39
  endDate: string
32
40
  timeZone: string
41
+ climateSetting: {
42
+ hvacModeSetting: HvacModeSetting
43
+ // may have to ignore one or the other fields here on submit
44
+ heatingSetPoint: number
45
+ coolingSetPoint: number
46
+ }
33
47
  }
34
48
 
35
49
  export function ClimateSettingScheduleForm({
@@ -48,19 +62,29 @@ export function ClimateSettingScheduleForm({
48
62
  function Content({
49
63
  onBack,
50
64
  }: Omit<ClimateSettingScheduleFormProps, 'className'>): JSX.Element {
51
- const { control, watch } = useForm({
52
- defaultValues: {
53
- deviceId: '',
54
- name: '',
55
- startDate: '',
56
- endDate: '',
57
- timeZone: getSystemTimeZone(),
58
- },
59
- })
65
+ const { control, watch, resetField } =
66
+ useForm<ClimateSettingScheduleFormFields>({
67
+ defaultValues: {
68
+ deviceId: '',
69
+ name: '',
70
+ startDate: '',
71
+ endDate: '',
72
+ timeZone: getSystemTimeZone(),
73
+ climateSetting: {
74
+ hvacModeSetting: 'heat_cool',
75
+ heatingSetPoint: 70,
76
+ coolingSetPoint: 75,
77
+ },
78
+ },
79
+ })
60
80
 
61
81
  const deviceId = watch('deviceId')
62
82
  const timeZone = watch('timeZone')
63
83
 
84
+ const { device } = useDevice({
85
+ device_id: deviceId,
86
+ })
87
+
64
88
  const [page, setPage] = useState<
65
89
  | 'device_select'
66
90
  | 'default_setting'
@@ -69,13 +93,38 @@ function Content({
69
93
  | 'climate_setting'
70
94
  >('device_select')
71
95
 
96
+ const returnToDeviceSelect = (): void => {
97
+ resetField('deviceId')
98
+ setPage('device_select')
99
+ }
100
+
101
+ useEffect(() => {
102
+ if (page === 'device_select' && device != null) {
103
+ if (!isThermostatDevice(device)) return
104
+ const defaultSetting = device.properties.default_climate_setting
105
+ if (defaultSetting != null) setPage('name_and_schedule')
106
+ else setPage('default_setting')
107
+ }
108
+ }, [device, page, setPage])
109
+
72
110
  if (page === 'device_select') {
73
111
  return (
74
112
  <ClimateSettingScheduleFormDeviceSelect
75
113
  title={t.addNewClimateSettingSchedule}
76
114
  control={control}
77
115
  onBack={onBack}
78
- onSelect={() => {
116
+ />
117
+ )
118
+ }
119
+
120
+ if (page === 'default_setting') {
121
+ return (
122
+ <ClimateSettingScheduleFormDefaultClimateSetting
123
+ title={t.addNewClimateSettingSchedule}
124
+ deviceId={deviceId}
125
+ onBack={returnToDeviceSelect}
126
+ onCancel={onBack}
127
+ onSave={() => {
79
128
  setPage('name_and_schedule')
80
129
  }}
81
130
  />
@@ -88,9 +137,7 @@ function Content({
88
137
  title={t.addNewClimateSettingSchedule}
89
138
  control={control}
90
139
  deviceId={deviceId}
91
- onBack={() => {
92
- setPage('device_select')
93
- }}
140
+ onBack={returnToDeviceSelect}
94
141
  onCancel={onBack}
95
142
  onNext={() => {
96
143
  setPage('climate_setting')
@@ -114,6 +161,21 @@ function Content({
114
161
  )
115
162
  }
116
163
 
164
+ if (page === 'climate_setting') {
165
+ return (
166
+ <ClimateSettingScheduleFormClimateSetting
167
+ title={t.addNewClimateSettingSchedule}
168
+ control={control}
169
+ deviceId={deviceId}
170
+ onBack={() => {
171
+ setPage('name_and_schedule')
172
+ }}
173
+ onCancel={onBack}
174
+ onSave={() => {}}
175
+ />
176
+ )
177
+ }
178
+
117
179
  return <></>
118
180
  }
119
181
 
@@ -0,0 +1,103 @@
1
+ import { type Control, Controller } from 'react-hook-form'
2
+
3
+ import { useDevice } from 'lib/seam/devices/use-device.js'
4
+ import { Button } from 'lib/ui/Button.js'
5
+ import type { ClimateSettingScheduleFormFields } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js'
6
+ import { FormField } from 'lib/ui/FormField.js'
7
+ import { InputLabel } from 'lib/ui/InputLabel.js'
8
+ import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
9
+ import { ClimateSettingControlGroup } from 'lib/ui/thermostat/ClimateSettingControlGroup.js'
10
+
11
+ interface ClimateSettingScheduleFormClimateSettingProps {
12
+ title: string
13
+ control: Control<ClimateSettingScheduleFormFields>
14
+ deviceId: string
15
+ onBack: () => void
16
+ onCancel: (() => void) | undefined
17
+ onSave: () => void
18
+ }
19
+
20
+ export function ClimateSettingScheduleFormClimateSetting({
21
+ title,
22
+ control,
23
+ deviceId,
24
+ onBack,
25
+ onCancel,
26
+ onSave,
27
+ }: ClimateSettingScheduleFormClimateSettingProps): JSX.Element {
28
+ const { device } = useDevice({
29
+ device_id: deviceId,
30
+ })
31
+
32
+ return (
33
+ <>
34
+ <ContentHeader
35
+ title={title}
36
+ onBack={onBack}
37
+ subheading={device?.properties.name}
38
+ />
39
+ <div className='seam-main'>
40
+ <div className='seam-climate-setting-schedule-form-climate-setting'>
41
+ <div className='seam-content'>
42
+ <div>
43
+ <InputLabel>{t.climateSetting}</InputLabel>
44
+ <span className='seam-label'>{t.climateSettingSubHeading}</span>
45
+ </div>
46
+ <FormContent control={control} />
47
+ </div>
48
+ </div>
49
+ <div className='seam-actions'>
50
+ <Button onClick={onCancel}>{t.cancel}</Button>
51
+ <Button variant='solid' onClick={onSave}>
52
+ {t.save}
53
+ </Button>
54
+ </div>
55
+ </div>
56
+ </>
57
+ )
58
+ }
59
+
60
+ interface FormContentProps {
61
+ control: Control<ClimateSettingScheduleFormFields>
62
+ }
63
+
64
+ function FormContent({ control }: FormContentProps): JSX.Element {
65
+ return (
66
+ <FormField>
67
+ <Controller
68
+ name='climateSetting'
69
+ control={control}
70
+ render={({ field: { value, onChange } }) => (
71
+ <ClimateSettingControlGroup
72
+ mode={value.hvacModeSetting}
73
+ onModeChange={(mode) => {
74
+ onChange({ ...value, hvacModeSetting: mode })
75
+ }}
76
+ coolValue={value.coolingSetPoint}
77
+ heatValue={value.heatingSetPoint}
78
+ onCoolValueChange={(coolingSetPoint) => {
79
+ onChange({
80
+ ...value,
81
+ coolingSetPoint,
82
+ })
83
+ }}
84
+ onHeatValueChange={(heatingSetPoint) => {
85
+ onChange({
86
+ ...value,
87
+ heatingSetPoint,
88
+ })
89
+ }}
90
+ />
91
+ )}
92
+ />
93
+ </FormField>
94
+ )
95
+ }
96
+
97
+ const t = {
98
+ climateSetting: 'Climate setting',
99
+ climateSettingSubHeading: 'Choose mode and adjust the climate',
100
+ cancel: 'Cancel',
101
+ save: 'Save',
102
+ next: 'Next',
103
+ }