@seamapi/react 1.47.0 → 1.48.0

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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/elements.js +422 -424
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +65 -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/icons/Info.d.ts +2 -0
  9. package/lib/icons/Info.js +7 -0
  10. package/lib/icons/Info.js.map +1 -0
  11. package/lib/icons/InfoDark.d.ts +2 -0
  12. package/lib/icons/InfoDark.js +7 -0
  13. package/lib/icons/InfoDark.js.map +1 -0
  14. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
  15. package/lib/seam/components/AccessCodeDetails/AccessCodeDevice.js +1 -1
  16. package/lib/seam/components/AccessCodeDetails/AccessCodeDevice.js.map +1 -1
  17. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.d.ts +6 -1
  18. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.js +21 -4
  19. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.js.map +1 -1
  20. package/lib/seam/components/DeviceTable/DeviceTable.js +2 -2
  21. package/lib/seam/components/DeviceTable/DeviceTable.js.map +1 -1
  22. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedule.d.ts +5 -0
  23. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedule.js +24 -0
  24. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedule.js.map +1 -0
  25. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js +1 -0
  26. package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js.map +1 -1
  27. package/lib/seam/thermostats/climate-setting-schedules/use-create-climate-setting-schedule.d.ts +5 -0
  28. package/lib/seam/thermostats/climate-setting-schedules/use-create-climate-setting-schedule.js +21 -0
  29. package/lib/seam/thermostats/climate-setting-schedules/use-create-climate-setting-schedule.js.map +1 -0
  30. package/lib/seam/thermostats/climate-setting-schedules/use-delete-climate-setting-schedule.d.ts +5 -0
  31. package/lib/seam/thermostats/climate-setting-schedules/use-delete-climate-setting-schedule.js +27 -0
  32. package/lib/seam/thermostats/climate-setting-schedules/use-delete-climate-setting-schedule.js.map +1 -0
  33. package/lib/ui/Tooltip/Tooltip.d.ts +2 -0
  34. package/lib/ui/Tooltip/Tooltip.js +33 -0
  35. package/lib/ui/Tooltip/Tooltip.js.map +1 -0
  36. package/lib/ui/thermostat/ClimateSettingStatus.js +10 -3
  37. package/lib/ui/thermostat/ClimateSettingStatus.js.map +1 -1
  38. package/lib/version.d.ts +1 -1
  39. package/lib/version.js +1 -1
  40. package/package.json +2 -2
  41. package/src/lib/icons/Info.tsx +36 -0
  42. package/src/lib/icons/InfoDark.tsx +37 -0
  43. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +0 -2
  44. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDevice.tsx +1 -4
  45. package/src/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.element.ts +4 -0
  46. package/src/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.tsx +59 -6
  47. package/src/lib/seam/components/DeviceTable/DeviceTable.tsx +2 -2
  48. package/src/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedule.ts +47 -0
  49. package/src/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.ts +1 -0
  50. package/src/lib/seam/thermostats/climate-setting-schedules/use-create-climate-setting-schedule.ts +48 -0
  51. package/src/lib/seam/thermostats/climate-setting-schedules/use-delete-climate-setting-schedule.ts +53 -0
  52. package/src/lib/ui/Tooltip/Tooltip.tsx +64 -0
  53. package/src/lib/ui/thermostat/ClimateSettingStatus.tsx +12 -10
  54. package/src/lib/version.ts +1 -1
  55. package/src/styles/_main.scss +2 -0
  56. package/src/styles/_tooltip.scss +83 -0
@@ -0,0 +1,5 @@
1
+ import { type UseMutationResult } from '@tanstack/react-query';
2
+ import type { ClimateSettingSchedule, ClimateSettingScheduleCreateRequest, SeamError } from 'seamapi';
3
+ export type UseCreateClimateSettingScheduleData = ClimateSettingSchedule;
4
+ export type UseCreateClimateSettingScheduleMutationParams = ClimateSettingScheduleCreateRequest;
5
+ export declare function useCreateClimateSettingSchedule(): UseMutationResult<UseCreateClimateSettingScheduleData, SeamError, UseCreateClimateSettingScheduleMutationParams>;
@@ -0,0 +1,21 @@
1
+ import { useMutation, useQueryClient, } from '@tanstack/react-query';
2
+ import { NullSeamClientError, useSeamClient } from '../../../../lib/seam/use-seam-client.js';
3
+ export function useCreateClimateSettingSchedule() {
4
+ const { client } = useSeamClient();
5
+ const queryClient = useQueryClient();
6
+ return useMutation({
7
+ mutationFn: async (mutationParams) => {
8
+ if (client === null)
9
+ throw new NullSeamClientError();
10
+ return await client.thermostats.climateSettingSchedules.create(mutationParams);
11
+ },
12
+ onSuccess: () => {
13
+ void queryClient.invalidateQueries([
14
+ 'thermostats',
15
+ 'climate_setting_schedules',
16
+ 'list',
17
+ ]);
18
+ },
19
+ });
20
+ }
21
+ //# sourceMappingURL=use-create-climate-setting-schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-create-climate-setting-schedule.js","sourceRoot":"","sources":["../../../../src/lib/seam/thermostats/climate-setting-schedules/use-create-climate-setting-schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAEX,cAAc,GACf,MAAM,uBAAuB,CAAA;AAQ9B,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAMhF,MAAM,UAAU,+BAA+B;IAK7C,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;IAClC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAA;IAEpC,OAAO,WAAW,CAIhB;QACA,UAAU,EAAE,KAAK,EACf,cAA6D,EAC7D,EAAE;YACF,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,IAAI,mBAAmB,EAAE,CAAA;YACpD,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,MAAM,CAC5D,cAAc,CACf,CAAA;QACH,CAAC;QACD,SAAS,EAAE,GAAG,EAAE;YACd,KAAK,WAAW,CAAC,iBAAiB,CAAC;gBACjC,aAAa;gBACb,2BAA2B;gBAC3B,MAAM;aACP,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type UseMutationResult } from '@tanstack/react-query';
2
+ import type { ClimateSettingScheduleDeleteRequest, ClimateSettingScheduleDeleteResponse, SeamError } from 'seamapi';
3
+ export type UseDeleteClimateSettingScheduleMutationParams = ClimateSettingScheduleDeleteRequest;
4
+ export type UseDeleteClimateSettingScheduleData = ClimateSettingScheduleDeleteResponse;
5
+ export declare function useDeleteClimateSettingSchedule(): UseMutationResult<UseDeleteClimateSettingScheduleData, SeamError, UseDeleteClimateSettingScheduleMutationParams>;
@@ -0,0 +1,27 @@
1
+ import { useMutation, useQueryClient, } from '@tanstack/react-query';
2
+ import { NullSeamClientError, useSeamClient } from '../../../../lib/seam/use-seam-client.js';
3
+ export function useDeleteClimateSettingSchedule() {
4
+ const { client } = useSeamClient();
5
+ const queryClient = useQueryClient();
6
+ return useMutation({
7
+ mutationFn: async (mutationParams) => {
8
+ if (client === null)
9
+ throw new NullSeamClientError();
10
+ await client.thermostats.climateSettingSchedules.delete(mutationParams);
11
+ },
12
+ onSuccess: (_data, variables) => {
13
+ void queryClient.invalidateQueries([
14
+ 'thermostats',
15
+ 'climate_setting_schedules',
16
+ 'get',
17
+ { climate_setting_schedule_id: variables.climate_setting_schedule_id },
18
+ ]);
19
+ void queryClient.invalidateQueries([
20
+ 'thermostats',
21
+ 'climate_setting_schedules',
22
+ 'list',
23
+ ]);
24
+ },
25
+ });
26
+ }
27
+ //# sourceMappingURL=use-delete-climate-setting-schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-delete-climate-setting-schedule.js","sourceRoot":"","sources":["../../../../src/lib/seam/thermostats/climate-setting-schedules/use-delete-climate-setting-schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAEX,cAAc,GACf,MAAM,uBAAuB,CAAA;AAO9B,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAQhF,MAAM,UAAU,+BAA+B;IAK7C,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;IAClC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAA;IAEpC,OAAO,WAAW,CAIhB;QACA,UAAU,EAAE,KAAK,EACf,cAA6D,EAC7D,EAAE;YACF,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,IAAI,mBAAmB,EAAE,CAAA;YACpD,MAAM,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;QACzE,CAAC;QACD,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;YAC9B,KAAK,WAAW,CAAC,iBAAiB,CAAC;gBACjC,aAAa;gBACb,2BAA2B;gBAC3B,KAAK;gBACL,EAAE,2BAA2B,EAAE,SAAS,CAAC,2BAA2B,EAAE;aACvE,CAAC,CAAA;YACF,KAAK,WAAW,CAAC,iBAAiB,CAAC;gBACjC,aAAa;gBACb,2BAA2B;gBAC3B,MAAM;aACP,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type PropsWithChildren } from 'react';
2
+ export declare function Tooltip({ children }: PropsWithChildren): JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, } from 'react';
3
+ import { InfoIcon } from '../../../lib/icons/Info.js';
4
+ import { InfoDarkIcon } from '../../../lib/icons/InfoDark.js';
5
+ export function Tooltip({ children }) {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ useEffect(() => {
8
+ const handleClose = () => {
9
+ setIsOpen(false);
10
+ };
11
+ const handleEscape = (event) => {
12
+ if (event.key === 'Escape') {
13
+ handleClose();
14
+ }
15
+ };
16
+ if (isOpen) {
17
+ globalThis.document?.addEventListener('click', handleClose);
18
+ globalThis.addEventListener?.('keydown', handleEscape);
19
+ }
20
+ return () => {
21
+ globalThis.document?.removeEventListener('click', handleClose);
22
+ globalThis.removeEventListener?.('keydown', handleEscape);
23
+ };
24
+ }, [isOpen, setIsOpen]);
25
+ const handleToggle = (event) => {
26
+ event.stopPropagation();
27
+ setIsOpen((prev) => !prev);
28
+ };
29
+ return (_jsxs("div", { className: 'seam-tooltip', children: [_jsx("div", { className: 'seam-tooltip-trigger-wrap', children: _jsxs("button", { onClick: handleToggle, className: 'seam-tooltip-button', children: [_jsx("div", { className: 'seam-tooltip-button-icon seam-tooltip-button-icon-default', children: _jsx(InfoIcon, {}) }), _jsx("div", { className: 'seam-tooltip-button-icon seam-tooltip-button-icon-hover', children: _jsx(InfoDarkIcon, {}) })] }) }), _jsx("div", { className: 'seam-tooltip-popover', "aria-expanded": isOpen, onClick: (event) => {
30
+ event.stopPropagation();
31
+ }, children: _jsx("p", { className: 'seam-tooltip-text', children: children }) })] }));
32
+ }
33
+ //# sourceMappingURL=Tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.js","sourceRoot":"","sources":["../../../src/lib/ui/Tooltip/Tooltip.tsx"],"names":[],"mappings":";AAAA,OAAO,EAGL,SAAS,EACT,QAAQ,GACT,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,MAAM,UAAU,OAAO,CAAC,EAAE,QAAQ,EAAqB;IACrD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE3C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,GAAS,EAAE;YAC7B,SAAS,CAAC,KAAK,CAAC,CAAA;QAClB,CAAC,CAAA;QAED,MAAM,YAAY,GAAG,CAAC,KAAoB,EAAQ,EAAE;YAClD,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE;gBAC1B,WAAW,EAAE,CAAA;aACd;QACH,CAAC,CAAA;QACD,IAAI,MAAM,EAAE;YACV,UAAU,CAAC,QAAQ,EAAE,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;YAC3D,UAAU,CAAC,gBAAgB,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;SACvD;QAED,OAAO,GAAG,EAAE;YACV,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;YAC9D,UAAU,CAAC,mBAAmB,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;QAC3D,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;IAEvB,MAAM,YAAY,GAAyC,CAAC,KAAK,EAAQ,EAAE;QACzE,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC,CAAA;IAED,OAAO,CACL,eAAK,SAAS,EAAC,cAAc,aAC3B,cAAK,SAAS,EAAC,2BAA2B,YACxC,kBAAQ,OAAO,EAAE,YAAY,EAAE,SAAS,EAAC,qBAAqB,aAC5D,cAAK,SAAS,EAAC,2DAA2D,YACxE,KAAC,QAAQ,KAAG,GACR,EACN,cAAK,SAAS,EAAC,yDAAyD,YACtE,KAAC,YAAY,KAAG,GACZ,IACC,GACL,EAEN,cACE,SAAS,EAAC,sBAAsB,mBACjB,MAAM,EACrB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,KAAK,CAAC,eAAe,EAAE,CAAA;gBACzB,CAAC,YAED,YAAG,SAAS,EAAC,mBAAmB,YAAE,QAAQ,GAAK,GAC3C,IACF,CACP,CAAA;AACH,CAAC"}
@@ -4,11 +4,11 @@ import { ThermostatHeatIcon } from '../../../lib/icons/ThermostatHeat.js';
4
4
  import { ThermostatHeatCoolIcon } from '../../../lib/icons/ThermostatHeatCool.js';
5
5
  import { ThermostatOffIcon } from '../../../lib/icons/ThermostatOff.js';
6
6
  export function ClimateSettingStatus({ climateSetting, temperatureUnit = 'fahrenheit', iconPlacement = 'left', }) {
7
- return (_jsxs("div", { className: 'seam-climate-setting-status', children: [iconPlacement === 'left' && (_jsx(ClimateSettingIcon, { mode: climateSetting.hvac_mode_setting })), _jsx(Content, { mode: climateSetting.hvac_mode_setting, coolingSetPoint: temperatureUnit === 'fahrenheit'
7
+ return (_jsxs("div", { className: 'seam-climate-setting-status', children: [iconPlacement === 'left' && (_jsx(ClimateSettingIcon, { mode: climateSetting.hvac_mode_setting })), _jsx(Content, { mode: climateSetting.hvac_mode_setting, coolingSetPoint: formatTemperatureValue(temperatureUnit === 'fahrenheit'
8
8
  ? climateSetting.cooling_set_point_fahrenheit
9
- : climateSetting.cooling_set_point_celsius, heatingSetPoint: temperatureUnit === 'fahrenheit'
9
+ : climateSetting.cooling_set_point_celsius), heatingSetPoint: formatTemperatureValue(temperatureUnit === 'fahrenheit'
10
10
  ? climateSetting.heating_set_point_fahrenheit
11
- : climateSetting.heating_set_point_celsius, temperatureUnit: temperatureUnit }), iconPlacement === 'right' && (_jsx(ClimateSettingIcon, { mode: climateSetting.hvac_mode_setting }))] }));
11
+ : climateSetting.heating_set_point_celsius), temperatureUnit: temperatureUnit }), iconPlacement === 'right' && (_jsx(ClimateSettingIcon, { mode: climateSetting.hvac_mode_setting }))] }));
12
12
  }
13
13
  function ClimateSettingIcon(props) {
14
14
  const { mode } = props;
@@ -29,6 +29,13 @@ function Content(props) {
29
29
  return _jsx("span", { children: t.off });
30
30
  return null;
31
31
  }
32
+ const formatTemperatureValue = (value) => {
33
+ if (value == null)
34
+ return '0';
35
+ if (Number.isInteger(value))
36
+ return value.toFixed();
37
+ return value.toFixed(1);
38
+ };
32
39
  const t = {
33
40
  degreeFahrenheit: '°F',
34
41
  degreeCelsius: '°C',
@@ -1 +1 @@
1
- {"version":3,"file":"ClimateSettingStatus.js","sourceRoot":"","sources":["../../../src/lib/ui/thermostat/ClimateSettingStatus.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAQ9D,MAAM,UAAU,oBAAoB,CAAC,EACnC,cAAc,EACd,eAAe,GAAG,YAAY,EAC9B,aAAa,GAAG,MAAM,GACI;IAC1B,OAAO,CACL,eAAK,SAAS,EAAC,6BAA6B,aACzC,aAAa,KAAK,MAAM,IAAI,CAC3B,KAAC,kBAAkB,IAAC,IAAI,EAAE,cAAc,CAAC,iBAAiB,GAAI,CAC/D,EACD,KAAC,OAAO,IACN,IAAI,EAAE,cAAc,CAAC,iBAAiB,EACtC,eAAe,EACb,eAAe,KAAK,YAAY;oBAC9B,CAAC,CAAC,cAAc,CAAC,4BAA4B;oBAC7C,CAAC,CAAC,cAAc,CAAC,yBAAyB,EAE9C,eAAe,EACb,eAAe,KAAK,YAAY;oBAC9B,CAAC,CAAC,cAAc,CAAC,4BAA4B;oBAC7C,CAAC,CAAC,cAAc,CAAC,yBAAyB,EAE9C,eAAe,EAAE,eAAe,GAChC,EACD,aAAa,KAAK,OAAO,IAAI,CAC5B,KAAC,kBAAkB,IAAC,IAAI,EAAE,cAAc,CAAC,iBAAiB,GAAI,CAC/D,IACG,CACP,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,KAE3B;IACC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;IAEtB,OAAO,CACL,eAAK,SAAS,EAAC,kCAAkC,aAC9C,IAAI,KAAK,MAAM,IAAI,KAAC,kBAAkB,KAAG,EACzC,IAAI,KAAK,MAAM,IAAI,KAAC,kBAAkB,KAAG,EACzC,IAAI,KAAK,UAAU,IAAI,KAAC,sBAAsB,KAAG,EACjD,IAAI,KAAK,KAAK,IAAI,KAAC,iBAAiB,KAAG,IACpC,CACP,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,KAShB;IACC,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,GAAG,KAAK,CAAA;IACzE,MAAM,kBAAkB,GAAG,eAAe,KAAK,SAAS,CAAA;IACxD,MAAM,kBAAkB,GAAG,eAAe,KAAK,SAAS,CAAA;IACxD,MAAM,IAAI,GACR,eAAe,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAA;IAEzE,IAAI,IAAI,KAAK,MAAM,IAAI,kBAAkB;QACvC,OAAO,yBAAO,GAAG,eAAe,GAAG,IAAI,EAAE,GAAQ,CAAA;IAEnD,IAAI,IAAI,KAAK,MAAM,IAAI,kBAAkB;QACvC,OAAO,yBAAO,GAAG,eAAe,GAAG,IAAI,EAAE,GAAQ,CAAA;IAEnD,IAAI,IAAI,KAAK,UAAU,IAAI,kBAAkB,IAAI,kBAAkB;QACjE,OAAO,CACL,yBAAO,GAAG,eAAe,GAAG,IAAI,MAAM,eAAe,GAAG,IAAI,EAAE,GAAQ,CACvE,CAAA;IAEH,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,yBAAO,CAAC,CAAC,GAAG,GAAQ,CAAA;IAE/C,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,CAAC,GAAG;IACR,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,GAAG,EAAE,KAAK;CACX,CAAA"}
1
+ {"version":3,"file":"ClimateSettingStatus.js","sourceRoot":"","sources":["../../../src/lib/ui/thermostat/ClimateSettingStatus.tsx"],"names":[],"mappings":"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"}
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const seamapiReactVersion = "1.47.0";
1
+ declare const seamapiReactVersion = "1.48.0";
2
2
  export default seamapiReactVersion;
package/lib/version.js CHANGED
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '1.47.0';
1
+ const seamapiReactVersion = '1.48.0';
2
2
  export default seamapiReactVersion;
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamapi/react",
3
- "version": "1.47.0",
3
+ "version": "1.48.0",
4
4
  "description": "Seam Components.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -122,7 +122,7 @@
122
122
  "@tanstack/react-query": "^4.28.0",
123
123
  "classnames": "^2.3.2",
124
124
  "luxon": "^3.3.0",
125
- "seamapi": "^8.6.0",
125
+ "seamapi": "^8.6.3",
126
126
  "uuid": "^9.0.0"
127
127
  },
128
128
  "devDependencies": {
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Automaically generated by SVGR from assets/icons/*.svg.
3
+ * Do not edit this file or add other components to this directory.
4
+ */
5
+ import type { SVGProps } from 'react'
6
+ export function InfoIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
7
+ return (
8
+ <svg
9
+ xmlns='http://www.w3.org/2000/svg'
10
+ width={18}
11
+ height={19}
12
+ fill='none'
13
+ {...props}
14
+ >
15
+ <mask
16
+ id='info_svg__a'
17
+ width={18}
18
+ height={19}
19
+ x={0}
20
+ y={0}
21
+ maskUnits='userSpaceOnUse'
22
+ style={{
23
+ maskType: 'alpha',
24
+ }}
25
+ >
26
+ <path fill='#D9D9D9' d='M0 .5h18v18H0z' />
27
+ </mask>
28
+ <g mask='url(#info_svg__a)'>
29
+ <path
30
+ fill='#D5D8DC'
31
+ d='M8.25 13.25h1.5v-4.5h-1.5v4.5Zm.75-6c.213 0 .39-.072.535-.216A.725.725 0 0 0 9.75 6.5a.728.728 0 0 0-.215-.535A.728.728 0 0 0 9 5.75a.725.725 0 0 0-.534.215.726.726 0 0 0-.216.535c0 .213.072.39.216.534A.724.724 0 0 0 9 7.25ZM9 17a7.3 7.3 0 0 1-2.925-.591 7.569 7.569 0 0 1-2.381-1.603 7.569 7.569 0 0 1-1.603-2.381A7.3 7.3 0 0 1 1.5 9.5a7.3 7.3 0 0 1 .591-2.925 7.569 7.569 0 0 1 1.603-2.381A7.58 7.58 0 0 1 6.075 2.59 7.307 7.307 0 0 1 9 2c1.037 0 2.012.197 2.925.59a7.58 7.58 0 0 1 2.381 1.604 7.569 7.569 0 0 1 1.603 2.381A7.3 7.3 0 0 1 16.5 9.5a7.3 7.3 0 0 1-.591 2.925 7.569 7.569 0 0 1-1.603 2.381 7.569 7.569 0 0 1-2.381 1.603A7.3 7.3 0 0 1 9 17Zm0-1.5c1.675 0 3.094-.581 4.256-1.744C14.42 12.594 15 11.175 15 9.5c0-1.675-.581-3.094-1.744-4.256C12.094 4.08 10.675 3.5 9 3.5c-1.675 0-3.094.581-4.256 1.744C3.58 6.406 3 7.825 3 9.5c0 1.675.581 3.094 1.744 4.256C5.906 14.92 7.325 15.5 9 15.5Z'
32
+ />
33
+ </g>
34
+ </svg>
35
+ )
36
+ }
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Automaically generated by SVGR from assets/icons/*.svg.
3
+ * Do not edit this file or add other components to this directory.
4
+ */
5
+ import type { SVGProps } from 'react'
6
+ export function InfoDarkIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
7
+ return (
8
+ <svg
9
+ xmlns='http://www.w3.org/2000/svg'
10
+ width={19}
11
+ height={19}
12
+ fill='none'
13
+ {...props}
14
+ >
15
+ <circle cx={9.755} cy={9.416} r={6.655} fill='#E9EDEF' />
16
+ <mask
17
+ id='info-dark_svg__a'
18
+ width={19}
19
+ height={19}
20
+ x={0}
21
+ y={0}
22
+ maskUnits='userSpaceOnUse'
23
+ style={{
24
+ maskType: 'alpha',
25
+ }}
26
+ >
27
+ <path fill='#D9D9D9' d='M.755.416h18v18h-18z' />
28
+ </mask>
29
+ <g mask='url(#info-dark_svg__a)'>
30
+ <path
31
+ fill='#9DA1A9'
32
+ d='M9.005 13.166h1.5v-4.5h-1.5v4.5Zm.75-6c.213 0 .391-.072.535-.216a.725.725 0 0 0 .215-.534.728.728 0 0 0-.215-.534.728.728 0 0 0-.535-.216.725.725 0 0 0-.534.216.726.726 0 0 0-.216.534c0 .213.072.39.216.534a.724.724 0 0 0 .534.216Zm0 9.75a7.3 7.3 0 0 1-2.925-.59 7.569 7.569 0 0 1-2.38-1.603 7.568 7.568 0 0 1-1.604-2.382 7.3 7.3 0 0 1-.59-2.925 7.3 7.3 0 0 1 .59-2.925 7.569 7.569 0 0 1 1.603-2.38A7.58 7.58 0 0 1 6.83 2.506a7.307 7.307 0 0 1 2.925-.59c1.038 0 2.013.196 2.925.59a7.579 7.579 0 0 1 2.382 1.603 7.569 7.569 0 0 1 1.602 2.381 7.3 7.3 0 0 1 .591 2.925 7.3 7.3 0 0 1-.59 2.925 7.569 7.569 0 0 1-1.603 2.382 7.569 7.569 0 0 1-2.382 1.602 7.3 7.3 0 0 1-2.925.591Zm0-1.5c1.675 0 3.094-.58 4.257-1.743 1.162-1.163 1.743-2.582 1.743-4.257 0-1.675-.58-3.093-1.743-4.256-1.163-1.162-2.582-1.744-4.257-1.744-1.675 0-3.093.582-4.256 1.744-1.162 1.163-1.744 2.581-1.744 4.256 0 1.675.582 3.094 1.744 4.257 1.163 1.162 2.581 1.743 4.256 1.743Z'
33
+ />
34
+ </g>
35
+ </svg>
36
+ )
37
+ }
@@ -87,9 +87,7 @@ export function AccessCodeDetails({
87
87
  <Duration accessCode={accessCode} />
88
88
  </div>
89
89
  </div>
90
-
91
90
  <Alerts alerts={alerts} className='seam-alerts-padded' />
92
-
93
91
  <AccessCodeDevice
94
92
  deviceId={accessCode.device_id}
95
93
  disableLockUnlock={disableLockUnlock}
@@ -56,10 +56,7 @@ function Content(props: {
56
56
  <DeviceImage device={device} />
57
57
  </div>
58
58
  <div className='seam-body'>
59
- <div className='seam-model'>
60
- {device.properties.model.manufacturer_display_name}{' '}
61
- {device.properties.model.display_name}
62
- </div>
59
+ <div>{device.properties.name}</div>
63
60
  <TextButton
64
61
  onClick={() => {
65
62
  onSelectDevice(device.device_id)
@@ -6,7 +6,11 @@ export const name = 'seam-climate-setting-schedule-table'
6
6
 
7
7
  export const props: ElementProps<ClimateSettingScheduleTableProps> = {
8
8
  deviceId: 'string',
9
+ disableSearch: 'boolean',
10
+ climateSettingScheduleFilter: 'object',
11
+ climateSettingScheduleComparator: 'object',
9
12
  onBack: 'object',
13
+ heading: 'string',
10
14
  className: 'string',
11
15
  }
12
16
 
@@ -1,5 +1,8 @@
1
1
  import classNames from 'classnames'
2
+ import { useMemo, useState } from 'react'
3
+ import type { ClimateSettingSchedule } from 'seamapi'
2
4
 
5
+ import { compareByCreatedAtDesc } from 'lib/dates.js'
3
6
  import {
4
7
  useClimateSettingSchedules,
5
8
  type UseClimateSettingSchedulesData,
@@ -9,19 +12,44 @@ import { EmptyPlaceholder } from 'lib/ui/Table/EmptyPlaceholder.js'
9
12
  import { TableBody } from 'lib/ui/Table/TableBody.js'
10
13
  import { TableHeader } from 'lib/ui/Table/TableHeader.js'
11
14
  import { TableTitle } from 'lib/ui/Table/TableTitle.js'
15
+ import { SearchTextField } from 'lib/ui/TextField/SearchTextField.js'
12
16
  import { Caption } from 'lib/ui/typography/Caption.js'
13
17
 
14
18
  import { ClimateSettingScheduleRow } from './ClimateSettingScheduleRow.js'
15
19
 
16
20
  export interface ClimateSettingScheduleTableProps {
17
21
  deviceId: string
22
+ disableSearch?: boolean
23
+ climateSettingScheduleFilter?: (
24
+ climateSettingSchedule: ClimateSettingSchedule,
25
+ searchInputValue: string
26
+ ) => boolean
27
+ climateSettingScheduleComparator?: (
28
+ climateSettingScheduleA: ClimateSettingSchedule,
29
+ climateSettingScheduleB: ClimateSettingSchedule
30
+ ) => number
18
31
  onBack?: () => void
32
+ heading?: string | null
19
33
  className?: string
20
34
  }
21
35
 
36
+ const defaultClimateSettingScheduleFilter = (
37
+ climateSettingSchedule: ClimateSettingSchedule,
38
+ searchInputValue: string
39
+ ): boolean => {
40
+ const value = searchInputValue.trim().toLowerCase()
41
+ if (value === '') return true
42
+ const name = climateSettingSchedule.name ?? ''
43
+ return name.trim().toLowerCase().includes(value)
44
+ }
45
+
22
46
  export function ClimateSettingScheduleTable({
23
47
  deviceId,
48
+ disableSearch = false,
49
+ climateSettingScheduleFilter = defaultClimateSettingScheduleFilter,
50
+ climateSettingScheduleComparator = compareByCreatedAtDesc,
24
51
  onBack,
52
+ heading = t.climateSettingSchedules,
25
53
  className,
26
54
  }: ClimateSettingScheduleTableProps): JSX.Element {
27
55
  const { climateSettingSchedules, isLoading, isError, error } =
@@ -29,8 +57,22 @@ export function ClimateSettingScheduleTable({
29
57
  device_id: deviceId,
30
58
  })
31
59
 
32
- // TODO filtering
33
- const filteredClimateSettingSchedules = climateSettingSchedules ?? []
60
+ const [searchInputValue, setSearchInputValue] = useState('')
61
+
62
+ const filteredClimateSettingSchedules = useMemo(
63
+ () =>
64
+ climateSettingSchedules
65
+ ?.filter((schedule) =>
66
+ climateSettingScheduleFilter(schedule, searchInputValue)
67
+ )
68
+ ?.sort(climateSettingScheduleComparator) ?? [],
69
+ [
70
+ climateSettingSchedules,
71
+ searchInputValue,
72
+ climateSettingScheduleFilter,
73
+ climateSettingScheduleComparator,
74
+ ]
75
+ )
34
76
 
35
77
  if (isLoading) {
36
78
  return <p className={className}>...</p>
@@ -44,10 +86,21 @@ export function ClimateSettingScheduleTable({
44
86
  <div className={classNames('seam-table', className)}>
45
87
  <ContentHeader onBack={onBack} />
46
88
  <TableHeader>
47
- <TableTitle>
48
- {t.climateSettingSchedules}{' '}
49
- <Caption>({filteredClimateSettingSchedules.length})</Caption>
50
- </TableTitle>
89
+ {heading != null ? (
90
+ <TableTitle>
91
+ {heading ?? t.climateSettingSchedules}{' '}
92
+ <Caption>({filteredClimateSettingSchedules.length})</Caption>
93
+ </TableTitle>
94
+ ) : (
95
+ <div className='seam-fragment' />
96
+ )}
97
+ {!disableSearch && (
98
+ <SearchTextField
99
+ value={searchInputValue}
100
+ onChange={setSearchInputValue}
101
+ disabled={(climateSettingSchedules?.length ?? 0) === 0}
102
+ />
103
+ )}
51
104
  </TableHeader>
52
105
  <TableBody>
53
106
  <Content
@@ -1,6 +1,6 @@
1
1
  import classNames from 'classnames'
2
2
  import { useCallback, useMemo, useState } from 'react'
3
- import { isLockDevice } from 'seamapi'
3
+ import { isLockDevice, isThermostatDevice } from 'seamapi'
4
4
 
5
5
  import { compareByCreatedAtDesc } from 'lib/dates.js'
6
6
  import { DeviceDetails } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
@@ -76,7 +76,7 @@ export function DeviceTable({
76
76
  const filteredDevices = useMemo(
77
77
  () =>
78
78
  devices
79
- ?.filter(isLockDevice)
79
+ ?.filter((device) => isLockDevice(device) || isThermostatDevice(device))
80
80
  ?.filter((device) => deviceFilter(device, searchInputValue))
81
81
  ?.sort(deviceComparator) ?? [],
82
82
  [devices, searchInputValue, deviceFilter, deviceComparator]
@@ -0,0 +1,47 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import type {
3
+ ClimateSettingSchedule,
4
+ ClimateSettingScheduleGetRequest,
5
+ ClimateSettingScheduleGetResponse,
6
+ SeamError,
7
+ } from 'seamapi'
8
+
9
+ import { useSeamClient } from 'lib/seam/use-seam-client.js'
10
+ import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
11
+
12
+ export type UseClimateSettingScheduleParams =
13
+ | ClimateSettingScheduleGetRequest
14
+ | string
15
+
16
+ export type UseClimateSettingScheduleData = ClimateSettingSchedule | null
17
+
18
+ export function useClimateSettingSchedule(
19
+ params: UseClimateSettingScheduleParams
20
+ ): UseSeamQueryResult<'climateSettingSchedule', UseClimateSettingScheduleData> {
21
+ const normalizedParams =
22
+ typeof params === 'string'
23
+ ? { climate_setting_schedule_id: params }
24
+ : params
25
+ const { client } = useSeamClient()
26
+
27
+ const { data, ...rest } = useQuery<
28
+ ClimateSettingScheduleGetResponse['climate_setting_schedule'] | null,
29
+ SeamError
30
+ >({
31
+ enabled: client != null,
32
+ queryKey: [
33
+ 'thermostats',
34
+ 'climate_setting_schedules',
35
+ 'get',
36
+ normalizedParams,
37
+ ],
38
+ queryFn: async () => {
39
+ if (client == null) return null
40
+ return await client.thermostats.climateSettingSchedules.get(
41
+ normalizedParams
42
+ )
43
+ },
44
+ })
45
+
46
+ return { ...rest, climateSettingSchedule: data }
47
+ }
@@ -37,6 +37,7 @@ export function useClimateSettingSchedules(
37
37
  for (const schedule of schedules) {
38
38
  queryClient.setQueryData(
39
39
  [
40
+ 'thermostats',
40
41
  'climate_setting_schedules',
41
42
  'get',
42
43
  {
@@ -0,0 +1,48 @@
1
+ import {
2
+ useMutation,
3
+ type UseMutationResult,
4
+ useQueryClient,
5
+ } from '@tanstack/react-query'
6
+ import type {
7
+ ClimateSettingSchedule,
8
+ ClimateSettingScheduleCreateRequest,
9
+ ClimateSettingScheduleCreateResponse,
10
+ SeamError,
11
+ } from 'seamapi'
12
+
13
+ import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
14
+
15
+ export type UseCreateClimateSettingScheduleData = ClimateSettingSchedule
16
+ export type UseCreateClimateSettingScheduleMutationParams =
17
+ ClimateSettingScheduleCreateRequest
18
+
19
+ export function useCreateClimateSettingSchedule(): UseMutationResult<
20
+ UseCreateClimateSettingScheduleData,
21
+ SeamError,
22
+ UseCreateClimateSettingScheduleMutationParams
23
+ > {
24
+ const { client } = useSeamClient()
25
+ const queryClient = useQueryClient()
26
+
27
+ return useMutation<
28
+ ClimateSettingScheduleCreateResponse['climate_setting_schedule'],
29
+ SeamError,
30
+ ClimateSettingScheduleCreateRequest
31
+ >({
32
+ mutationFn: async (
33
+ mutationParams: UseCreateClimateSettingScheduleMutationParams
34
+ ) => {
35
+ if (client === null) throw new NullSeamClientError()
36
+ return await client.thermostats.climateSettingSchedules.create(
37
+ mutationParams
38
+ )
39
+ },
40
+ onSuccess: () => {
41
+ void queryClient.invalidateQueries([
42
+ 'thermostats',
43
+ 'climate_setting_schedules',
44
+ 'list',
45
+ ])
46
+ },
47
+ })
48
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ useMutation,
3
+ type UseMutationResult,
4
+ useQueryClient,
5
+ } from '@tanstack/react-query'
6
+ import type {
7
+ ClimateSettingScheduleDeleteRequest,
8
+ ClimateSettingScheduleDeleteResponse,
9
+ SeamError,
10
+ } from 'seamapi'
11
+
12
+ import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
13
+
14
+ export type UseDeleteClimateSettingScheduleMutationParams =
15
+ ClimateSettingScheduleDeleteRequest
16
+
17
+ export type UseDeleteClimateSettingScheduleData =
18
+ ClimateSettingScheduleDeleteResponse
19
+
20
+ export function useDeleteClimateSettingSchedule(): UseMutationResult<
21
+ UseDeleteClimateSettingScheduleData,
22
+ SeamError,
23
+ UseDeleteClimateSettingScheduleMutationParams
24
+ > {
25
+ const { client } = useSeamClient()
26
+ const queryClient = useQueryClient()
27
+
28
+ return useMutation<
29
+ UseDeleteClimateSettingScheduleData,
30
+ SeamError,
31
+ ClimateSettingScheduleDeleteRequest
32
+ >({
33
+ mutationFn: async (
34
+ mutationParams: UseDeleteClimateSettingScheduleMutationParams
35
+ ) => {
36
+ if (client === null) throw new NullSeamClientError()
37
+ await client.thermostats.climateSettingSchedules.delete(mutationParams)
38
+ },
39
+ onSuccess: (_data, variables) => {
40
+ void queryClient.invalidateQueries([
41
+ 'thermostats',
42
+ 'climate_setting_schedules',
43
+ 'get',
44
+ { climate_setting_schedule_id: variables.climate_setting_schedule_id },
45
+ ])
46
+ void queryClient.invalidateQueries([
47
+ 'thermostats',
48
+ 'climate_setting_schedules',
49
+ 'list',
50
+ ])
51
+ },
52
+ })
53
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ type MouseEventHandler,
3
+ type PropsWithChildren,
4
+ useEffect,
5
+ useState,
6
+ } from 'react'
7
+
8
+ import { InfoIcon } from 'lib/icons/Info.js'
9
+ import { InfoDarkIcon } from 'lib/icons/InfoDark.js'
10
+
11
+ export function Tooltip({ children }: PropsWithChildren): JSX.Element {
12
+ const [isOpen, setIsOpen] = useState(false)
13
+
14
+ useEffect(() => {
15
+ const handleClose = (): void => {
16
+ setIsOpen(false)
17
+ }
18
+
19
+ const handleEscape = (event: KeyboardEvent): void => {
20
+ if (event.key === 'Escape') {
21
+ handleClose()
22
+ }
23
+ }
24
+ if (isOpen) {
25
+ globalThis.document?.addEventListener('click', handleClose)
26
+ globalThis.addEventListener?.('keydown', handleEscape)
27
+ }
28
+
29
+ return () => {
30
+ globalThis.document?.removeEventListener('click', handleClose)
31
+ globalThis.removeEventListener?.('keydown', handleEscape)
32
+ }
33
+ }, [isOpen, setIsOpen])
34
+
35
+ const handleToggle: MouseEventHandler<HTMLButtonElement> = (event): void => {
36
+ event.stopPropagation()
37
+ setIsOpen((prev) => !prev)
38
+ }
39
+
40
+ return (
41
+ <div className='seam-tooltip'>
42
+ <div className='seam-tooltip-trigger-wrap'>
43
+ <button onClick={handleToggle} className='seam-tooltip-button'>
44
+ <div className='seam-tooltip-button-icon seam-tooltip-button-icon-default'>
45
+ <InfoIcon />
46
+ </div>
47
+ <div className='seam-tooltip-button-icon seam-tooltip-button-icon-hover'>
48
+ <InfoDarkIcon />
49
+ </div>
50
+ </button>
51
+ </div>
52
+
53
+ <div
54
+ className='seam-tooltip-popover'
55
+ aria-expanded={isOpen}
56
+ onClick={(event) => {
57
+ event.stopPropagation()
58
+ }}
59
+ >
60
+ <p className='seam-tooltip-text'>{children}</p>
61
+ </div>
62
+ </div>
63
+ )
64
+ }