@seamapi/react 1.16.2 → 1.18.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 (60) hide show
  1. package/README.md +2 -4
  2. package/dist/elements.js +3225 -3206
  3. package/dist/elements.js.map +1 -1
  4. package/lib/seam/components/AccessCodeDetails/AccessCodeDevice.js +11 -7
  5. package/lib/seam/components/AccessCodeDetails/AccessCodeDevice.js.map +1 -1
  6. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +2 -2
  7. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
  8. package/lib/seam/components/ConnectAccountButton/ConnectAccountButton.js +2 -5
  9. package/lib/seam/components/ConnectAccountButton/ConnectAccountButton.js.map +1 -1
  10. package/lib/seam/components/DeviceDetails/DeviceDetails.js +6 -69
  11. package/lib/seam/components/DeviceDetails/DeviceDetails.js.map +1 -1
  12. package/lib/seam/components/DeviceDetails/DeviceModel.d.ts +2 -3
  13. package/lib/seam/components/DeviceDetails/DeviceModel.js +1 -11
  14. package/lib/seam/components/DeviceDetails/DeviceModel.js.map +1 -1
  15. package/lib/seam/components/DeviceDetails/LockDeviceDetails.d.ts +7 -0
  16. package/lib/seam/components/DeviceDetails/LockDeviceDetails.js +68 -0
  17. package/lib/seam/components/DeviceDetails/LockDeviceDetails.js.map +1 -0
  18. package/lib/seam/components/DeviceTable/DeviceRow.js +1 -12
  19. package/lib/seam/components/DeviceTable/DeviceRow.js.map +1 -1
  20. package/lib/seam/components/DeviceTable/DeviceTable.js +4 -3
  21. package/lib/seam/components/DeviceTable/DeviceTable.js.map +1 -1
  22. package/lib/seam/devices/use-device.d.ts +2 -2
  23. package/lib/seam/devices/use-device.js.map +1 -1
  24. package/lib/seam/devices/use-devices.d.ts +2 -2
  25. package/lib/seam/devices/use-devices.js.map +1 -1
  26. package/lib/seam/devices/use-toggle-lock.d.ts +1 -1
  27. package/lib/seam/devices/use-toggle-lock.js +1 -1
  28. package/lib/seam/devices/use-toggle-lock.js.map +1 -1
  29. package/lib/ui/device/BatteryStatus.d.ts +2 -2
  30. package/lib/ui/device/BatteryStatus.js +9 -15
  31. package/lib/ui/device/BatteryStatus.js.map +1 -1
  32. package/lib/ui/device/DeviceImage.d.ts +2 -2
  33. package/lib/ui/device/DeviceImage.js.map +1 -1
  34. package/lib/ui/device/LockStatus.d.ts +3 -3
  35. package/lib/ui/device/LockStatus.js +4 -0
  36. package/lib/ui/device/LockStatus.js.map +1 -1
  37. package/lib/ui/device/OnlineStatus.d.ts +2 -2
  38. package/lib/version.d.ts +1 -1
  39. package/lib/version.js +1 -1
  40. package/package.json +4 -4
  41. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDevice.tsx +13 -8
  42. package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +3 -3
  43. package/src/lib/seam/components/ConnectAccountButton/ConnectAccountButton.tsx +2 -5
  44. package/src/lib/seam/components/DeviceDetails/DeviceDetails.tsx +7 -153
  45. package/src/lib/seam/components/DeviceDetails/DeviceModel.tsx +5 -19
  46. package/src/lib/seam/components/DeviceDetails/LockDeviceDetails.tsx +158 -0
  47. package/src/lib/seam/components/DeviceTable/DeviceRow.tsx +3 -15
  48. package/src/lib/seam/components/DeviceTable/DeviceTable.tsx +8 -4
  49. package/src/lib/seam/devices/use-device.ts +2 -3
  50. package/src/lib/seam/devices/use-devices.ts +2 -3
  51. package/src/lib/seam/devices/use-toggle-lock.ts +7 -8
  52. package/src/lib/ui/device/BatteryStatus.tsx +19 -29
  53. package/src/lib/ui/device/DeviceImage.tsx +4 -2
  54. package/src/lib/ui/device/LockStatus.tsx +7 -3
  55. package/src/lib/ui/device/OnlineStatus.tsx +2 -2
  56. package/src/lib/version.ts +1 -1
  57. package/lib/seam/devices/types.d.ts +0 -2
  58. package/lib/seam/devices/types.js +0 -2
  59. package/lib/seam/devices/types.js.map +0 -1
  60. package/src/lib/seam/devices/types.ts +0 -5
@@ -1,7 +1,11 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isLockDevice } from 'seamapi';
2
3
  import { LockLockedIcon } from '../../../lib/icons/LockLocked.js';
3
4
  import { LockUnlockedIcon } from '../../../lib/icons/LockUnlocked.js';
4
5
  export function LockStatus(props) {
6
+ if (!isLockDevice(props.device)) {
7
+ return null;
8
+ }
5
9
  const { device: { properties: { locked }, }, } = props;
6
10
  return (_jsx("div", { className: 'seam-lock-status', children: _jsx(Content, { isLocked: locked }) }));
7
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LockStatus.js","sourceRoot":"","sources":["../../../src/lib/ui/device/LockStatus.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAM5D,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,EACJ,MAAM,EAAE,EACN,UAAU,EAAE,EAAE,MAAM,EAAE,GACvB,GACF,GAAG,KAAK,CAAA;IAET,OAAO,CACL,cAAK,SAAS,EAAC,kBAAkB,YAC/B,KAAC,OAAO,IAAC,QAAQ,EAAE,MAAM,GAAI,GACzB,CACP,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,KAA4B;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAC1B,IAAI,QAAQ,EAAE;QACZ,OAAO,CACL,8BACE,KAAC,cAAc,IAAC,SAAS,EAAC,gBAAgB,GAAG,EAC7C,eAAM,SAAS,EAAC,kBAAkB,YAAE,CAAC,CAAC,MAAM,GAAQ,IACnD,CACJ,CAAA;KACF;IAED,OAAO,CACL,8BACE,KAAC,gBAAgB,IAAC,SAAS,EAAC,gBAAgB,GAAG,EAC/C,eAAM,SAAS,EAAC,kBAAkB,YAAE,CAAC,CAAC,QAAQ,GAAQ,IACrD,CACJ,CAAA;AACH,CAAC;AAED,MAAM,CAAC,GAAG;IACR,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACrB,CAAA"}
1
+ {"version":3,"file":"LockStatus.js","sourceRoot":"","sources":["../../../src/lib/ui/device/LockStatus.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,SAAS,CAAA;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAM5D,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;QAC/B,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,EACJ,MAAM,EAAE,EACN,UAAU,EAAE,EAAE,MAAM,EAAE,GACvB,GACF,GAAG,KAAK,CAAA;IAET,OAAO,CACL,cAAK,SAAS,EAAC,kBAAkB,YAC/B,KAAC,OAAO,IAAC,QAAQ,EAAE,MAAM,GAAI,GACzB,CACP,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,KAA4B;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAC1B,IAAI,QAAQ,EAAE;QACZ,OAAO,CACL,8BACE,KAAC,cAAc,IAAC,SAAS,EAAC,gBAAgB,GAAG,EAC7C,eAAM,SAAS,EAAC,kBAAkB,YAAE,CAAC,CAAC,MAAM,GAAQ,IACnD,CACJ,CAAA;KACF;IAED,OAAO,CACL,8BACE,KAAC,gBAAgB,IAAC,SAAS,EAAC,gBAAgB,GAAG,EAC/C,eAAM,SAAS,EAAC,kBAAkB,YAAE,CAAC,CAAC,QAAQ,GAAQ,IACrD,CACJ,CAAA;AACH,CAAC;AAED,MAAM,CAAC,GAAG;IACR,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACrB,CAAA"}
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
- import type { LockDevice } from 'seamapi';
2
+ import type { CommonDevice } from 'seamapi';
3
3
  interface OnlineStatusProps {
4
- device: LockDevice;
4
+ device: CommonDevice;
5
5
  }
6
6
  export declare function OnlineStatus(props: OnlineStatusProps): JSX.Element;
7
7
  export {};
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const seamapiReactVersion = "1.16.2";
1
+ declare const seamapiReactVersion = "1.18.0";
2
2
  export default seamapiReactVersion;
package/lib/version.js CHANGED
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '1.16.2';
1
+ const seamapiReactVersion = '1.18.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.16.2",
3
+ "version": "1.18.0",
4
4
  "description": "Seam Components.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -110,7 +110,7 @@
110
110
  "@tanstack/react-query": "^4.28.0",
111
111
  "classnames": "^2.3.2",
112
112
  "luxon": "^3.3.0",
113
- "seamapi": "^6.2.0",
113
+ "seamapi": "^6.7.0",
114
114
  "uuid": "^9.0.0"
115
115
  },
116
116
  "devDependencies": {
@@ -121,7 +121,7 @@
121
121
  "@mui/icons-material": "^5.11.16",
122
122
  "@mui/material": "^5.12.2",
123
123
  "@r2wc/react-to-web-component": "^2.0.2",
124
- "@seamapi/fake-seam-connect": "^0.7.2",
124
+ "@seamapi/fake-seam-connect": "^0.8.0",
125
125
  "@storybook/addon-essentials": "^7.0.2",
126
126
  "@storybook/addon-interactions": "^7.0.2",
127
127
  "@storybook/addon-links": "^7.0.2",
@@ -147,7 +147,7 @@
147
147
  "eslint-config-prettier": "^8.4.0",
148
148
  "eslint-config-standard-jsx": "^11.0.0",
149
149
  "eslint-config-standard-react": "^13.0.0",
150
- "eslint-config-standard-with-typescript": "^34.0.0",
150
+ "eslint-config-standard-with-typescript": "^35.0.0",
151
151
  "eslint-plugin-simple-import-sort": "^10.0.0",
152
152
  "eslint-plugin-storybook": "^0.6.11",
153
153
  "eslint-plugin-unused-imports": "^2.0.0",
@@ -1,7 +1,5 @@
1
- import type { LockDevice } from 'seamapi'
1
+ import { isLockDevice, type LockDevice } from 'seamapi'
2
2
 
3
- import { getDeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
4
- import { isLockDevice } from 'lib/seam/devices/types.js'
5
3
  import { useDevice } from 'lib/seam/devices/use-device.js'
6
4
  import { useToggleLock } from 'lib/seam/devices/use-toggle-lock.js'
7
5
  import { Button } from 'lib/ui/Button.js'
@@ -17,20 +15,27 @@ export function AccessCodeDevice({
17
15
  }): JSX.Element | null {
18
16
  const { isLoading, device } = useDevice({ device_id: deviceId })
19
17
 
20
- if (isLoading || device == null || !isLockDevice(device)) {
18
+ if (isLoading) {
21
19
  return null
22
20
  }
23
21
 
24
- return <LockDeviceDetails device={device} onSelectDevice={onSelectDevice} />
22
+ if (device == null) {
23
+ return null
24
+ }
25
+
26
+ if (!isLockDevice(device)) {
27
+ return null
28
+ }
29
+
30
+ return <Content device={device} onSelectDevice={onSelectDevice} />
25
31
  }
26
32
 
27
- function LockDeviceDetails(props: {
33
+ function Content(props: {
28
34
  device: LockDevice
29
35
  onSelectDevice: (deviceId: string) => void
30
36
  }): JSX.Element {
31
37
  const { device, onSelectDevice } = props
32
38
  const toggleLock = useToggleLock(device)
33
- const model = getDeviceModel(device)
34
39
 
35
40
  const toggleLockLabel = device.properties.locked ? t.unlock : t.lock
36
41
 
@@ -40,7 +45,7 @@ function LockDeviceDetails(props: {
40
45
  <DeviceImage device={device} />
41
46
  </div>
42
47
  <div className='seam-body'>
43
- <div className='seam-model'>{model}</div>
48
+ <div className='seam-model'>{device.properties.model.display_name}</div>
44
49
  <TextButton
45
50
  onClick={() => {
46
51
  onSelectDevice(device.device_id)
@@ -46,7 +46,7 @@ type AccessCode = UseAccessCodesData[number]
46
46
  const defaultAccessCodeFilter = (
47
47
  accessCode: AccessCode,
48
48
  searchInputValue: string
49
- ) => {
49
+ ): boolean => {
50
50
  const value = searchInputValue.trim()
51
51
  if (value === '') return true
52
52
  return new RegExp(value, 'i').test(accessCode.name ?? '')
@@ -118,7 +118,7 @@ export function AccessCodeTable({
118
118
  />
119
119
  </TableHeader>
120
120
  <TableBody>
121
- <Body
121
+ <Content
122
122
  accessCodes={filteredAccessCodes}
123
123
  onAccessCodeClick={handleAccessCodeClick}
124
124
  />
@@ -127,7 +127,7 @@ export function AccessCodeTable({
127
127
  )
128
128
  }
129
129
 
130
- function Body(props: {
130
+ function Content(props: {
131
131
  accessCodes: Array<UseAccessCodesData[number]>
132
132
  onAccessCodeClick: (accessCodeId: string) => void
133
133
  }): JSX.Element {
@@ -14,14 +14,11 @@ export function ConnectAccountButton({
14
14
  willNavigateToWebview: true,
15
15
  })
16
16
 
17
- const url = window.location.href
18
17
  const handleClick = useCallback(() => {
19
18
  mutate({
20
- accepted_providers: [],
21
- custom_redirect_url: url,
22
- custom_redirect_failure_url: url,
19
+ custom_redirect_url: globalThis.location?.href,
23
20
  })
24
- }, [mutate, url])
21
+ }, [mutate])
25
22
 
26
23
  return (
27
24
  <Button
@@ -1,20 +1,7 @@
1
- import classNames from 'classnames'
2
- import type { LockDevice } from 'seamapi'
1
+ import { isLockDevice } from 'seamapi'
3
2
 
4
- import { ChevronRightIcon } from 'lib/icons/ChevronRight.js'
5
- import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
6
- import { AccessCodeTable } from 'lib/seam/components/AccessCodeTable/AccessCodeTable.js'
7
- import { DeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
8
- import { isLockDevice } from 'lib/seam/devices/types.js'
3
+ import { LockDeviceDetails } from 'lib/seam/components/DeviceDetails/LockDeviceDetails.js'
9
4
  import { useDevice } from 'lib/seam/devices/use-device.js'
10
- import { useToggleLock } from 'lib/seam/devices/use-toggle-lock.js'
11
- import { Alerts } from 'lib/ui/Alert/Alerts.js'
12
- import { Button } from 'lib/ui/Button.js'
13
- import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js'
14
- import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
15
- import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js'
16
- import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
17
- import { useToggle } from 'lib/ui/use-toggle.js'
18
5
 
19
6
  export interface DeviceDetailsProps {
20
7
  deviceId: string
@@ -35,148 +22,15 @@ export function DeviceDetails({
35
22
  return null
36
23
  }
37
24
 
38
- if (!isLockDevice(device)) {
39
- return null
40
- }
41
-
42
- return (
43
- <LockDeviceDetails className={className} device={device} onBack={onBack} />
44
- )
45
- }
46
-
47
- function LockDeviceDetails(props: {
48
- device: LockDevice
49
- onBack?: () => void
50
- className?: string
51
- }): JSX.Element | null {
52
- const { device, onBack, className } = props
53
-
54
- const [accessCodesOpen, toggleAccessCodesOpen] = useToggle()
55
- const toggleLock = useToggleLock(device)
56
- const { accessCodes } = useAccessCodes({
57
- device_id: device.device_id,
58
- })
59
-
60
- const lockStatus = device.properties.locked ? t.locked : t.unlocked
61
- const toggleLockLabel = device.properties.locked ? t.unlock : t.lock
62
-
63
- const accessCodeCount = accessCodes?.length
64
-
65
- const accessCodeLength =
66
- device.properties?.schlage_metadata?.access_code_length
67
-
68
- if (accessCodes == null) {
69
- return null
70
- }
71
-
72
- if (accessCodesOpen) {
25
+ if (isLockDevice(device)) {
73
26
  return (
74
- <AccessCodeTable
27
+ <LockDeviceDetails
75
28
  className={className}
76
- deviceId={device.device_id}
77
- onBack={toggleAccessCodesOpen}
29
+ device={device}
30
+ onBack={onBack}
78
31
  />
79
32
  )
80
33
  }
81
34
 
82
- const alerts = [
83
- ...device.errors.map((error) => ({
84
- variant: 'error' as const,
85
- message: error.message,
86
- })),
87
- ...device.warnings.map((warning) => ({
88
- variant: 'warning' as const,
89
- message: warning.message,
90
- })),
91
- ]
92
-
93
- return (
94
- <div className={classNames('seam-device-details', className)}>
95
- <ContentHeader title='Device' onBack={onBack} />
96
- <div className='seam-body'>
97
- <div className='seam-summary'>
98
- <div className='seam-content'>
99
- <div className='seam-image'>
100
- <DeviceImage device={device} />
101
- </div>
102
- <div className='seam-info'>
103
- <span className='seam-label'>{t.device}</span>
104
- <h4 className='seam-device-name'>{device.properties.name}</h4>
105
- <div className='seam-properties'>
106
- <span className='seam-label'>{t.status}:</span>{' '}
107
- <OnlineStatus device={device} />
108
- <span className='seam-label'>{t.power}:</span>{' '}
109
- <BatteryStatus device={device} />
110
- <DeviceModel device={device} />
111
- </div>
112
- </div>
113
- </div>
114
- <Alerts alerts={alerts} className='seam-alerts-space-top' />
115
- </div>
116
- <div className='seam-box'>
117
- <div
118
- className='seam-content seam-access-codes'
119
- onClick={toggleAccessCodesOpen}
120
- >
121
- <span className='seam-value'>
122
- {accessCodeCount} {t.accessCodes}
123
- </span>
124
- <ChevronRightIcon />
125
- </div>
126
- </div>
127
-
128
- <div className='seam-box'>
129
- <div className='seam-content seam-lock-status'>
130
- <div>
131
- <span className='seam-label'>{t.lockStatus}</span>
132
- <span className='seam-value'>{lockStatus}</span>
133
- </div>
134
- <div className='seam-right'>
135
- <Button
136
- size='small'
137
- onClick={() => {
138
- toggleLock.mutate()
139
- }}
140
- >
141
- {toggleLockLabel}
142
- </Button>
143
- </div>
144
- </div>
145
- <AccessCodeLength accessCodeLength={accessCodeLength} />
146
- </div>
147
- </div>
148
- </div>
149
- )
150
- }
151
-
152
- function AccessCodeLength(props: {
153
- accessCodeLength: number | null | undefined
154
- }): JSX.Element | null {
155
- const { accessCodeLength } = props
156
- if (accessCodeLength == null) {
157
- return null
158
- }
159
-
160
- return (
161
- <div className='seam-content seam-access-code-length'>
162
- <span className='seam-label'>{t.codeLength}</span>
163
- <span className='seam-value'>
164
- {accessCodeLength} {t.digits}
165
- </span>
166
- </div>
167
- )
168
- }
169
-
170
- const t = {
171
- device: 'Device',
172
- unlock: 'Unlock',
173
- lock: 'Lock',
174
- locked: 'Locked',
175
- unlocked: 'Unlocked',
176
- accessCodes: 'access codes',
177
- codeLength: 'Code length',
178
- digits: 'digits',
179
- lockStatus: 'Lock status',
180
- status: 'Status',
181
- power: 'Power',
35
+ return null
182
36
  }
@@ -1,34 +1,20 @@
1
- import type { LockDevice } from 'seamapi'
1
+ import type { CommonDevice } from 'seamapi'
2
2
 
3
3
  interface DeviceModelProps {
4
- device: LockDevice
4
+ device: CommonDevice
5
5
  }
6
6
 
7
7
  export function DeviceModel({ device }: DeviceModelProps): JSX.Element | null {
8
- const model = getDeviceModel(device)
9
- if (model == null) {
10
- return null
11
- }
12
-
13
8
  return (
14
9
  <>
15
10
  <span className='seam-label'>{t.model}:</span>{' '}
16
- <div className='seam-device-model'>{model}</div>
11
+ <div className='seam-device-model'>
12
+ {device.properties.model.display_name}
13
+ </div>
17
14
  </>
18
15
  )
19
16
  }
20
17
 
21
- export const getDeviceModel = ({
22
- properties,
23
- }: LockDevice): string | undefined => {
24
- return (
25
- properties?.august_metadata?.model ??
26
- properties?.schlage_metadata?.model ??
27
- t.fallbackModel
28
- )
29
- }
30
-
31
18
  const t = {
32
19
  model: 'Model',
33
- fallbackModel: 'Unknown Lock',
34
20
  }
@@ -0,0 +1,158 @@
1
+ import classNames from 'classnames'
2
+ import type { LockDevice } from 'seamapi'
3
+
4
+ import { ChevronRightIcon } from 'lib/icons/ChevronRight.js'
5
+ import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
6
+ import { AccessCodeTable } from 'lib/seam/components/AccessCodeTable/AccessCodeTable.js'
7
+ import { DeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
8
+ import { useToggleLock } from 'lib/seam/devices/use-toggle-lock.js'
9
+ import { Alerts } from 'lib/ui/Alert/Alerts.js'
10
+ import { Button } from 'lib/ui/Button.js'
11
+ import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js'
12
+ import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
13
+ import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js'
14
+ import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
15
+ import { useToggle } from 'lib/ui/use-toggle.js'
16
+ export function LockDeviceDetails(props: {
17
+ device: LockDevice
18
+ onBack?: () => void
19
+ className?: string
20
+ }): JSX.Element | null {
21
+ const { device, onBack, className } = props
22
+
23
+ const [accessCodesOpen, toggleAccessCodesOpen] = useToggle()
24
+ const toggleLock = useToggleLock(device)
25
+ const { accessCodes } = useAccessCodes({
26
+ device_id: device.device_id,
27
+ })
28
+
29
+ const lockStatus = device.properties.locked ? t.locked : t.unlocked
30
+ const toggleLockLabel = device.properties.locked ? t.unlock : t.lock
31
+
32
+ const accessCodeCount = accessCodes?.length
33
+
34
+ if (accessCodes == null) {
35
+ return null
36
+ }
37
+
38
+ if (accessCodesOpen) {
39
+ return (
40
+ <AccessCodeTable
41
+ className={className}
42
+ deviceId={device.device_id}
43
+ onBack={toggleAccessCodesOpen}
44
+ />
45
+ )
46
+ }
47
+
48
+ const alerts = [
49
+ ...device.errors.map((error) => ({
50
+ variant: 'error' as const,
51
+ message: error.message,
52
+ })),
53
+ ...device.warnings.map((warning) => ({
54
+ variant: 'warning' as const,
55
+ message: warning.message,
56
+ })),
57
+ ]
58
+
59
+ return (
60
+ <div className={classNames('seam-device-details', className)}>
61
+ <ContentHeader title='Device' onBack={onBack} />
62
+ <div className='seam-body'>
63
+ <div className='seam-summary'>
64
+ <div className='seam-content'>
65
+ <div className='seam-image'>
66
+ <DeviceImage device={device} />
67
+ </div>
68
+ <div className='seam-info'>
69
+ <span className='seam-label'>{t.device}</span>
70
+ <h4 className='seam-device-name'>{device.properties.name}</h4>
71
+ <div className='seam-properties'>
72
+ <span className='seam-label'>{t.status}:</span>{' '}
73
+ <OnlineStatus device={device} />
74
+ <span className='seam-label'>{t.power}:</span>{' '}
75
+ <BatteryStatus device={device} />
76
+ <DeviceModel device={device} />
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <Alerts alerts={alerts} className='seam-alerts-space-top' />
81
+ </div>
82
+ <div className='seam-box'>
83
+ <div
84
+ className='seam-content seam-access-codes'
85
+ onClick={toggleAccessCodesOpen}
86
+ >
87
+ <span className='seam-value'>
88
+ {accessCodeCount} {t.accessCodes}
89
+ </span>
90
+ <ChevronRightIcon />
91
+ </div>
92
+ </div>
93
+
94
+ <div className='seam-box'>
95
+ <div className='seam-content seam-lock-status'>
96
+ <div>
97
+ <span className='seam-label'>{t.lockStatus}</span>
98
+ <span className='seam-value'>{lockStatus}</span>
99
+ </div>
100
+ <div className='seam-right'>
101
+ <Button
102
+ size='small'
103
+ onClick={() => {
104
+ toggleLock.mutate()
105
+ }}
106
+ >
107
+ {toggleLockLabel}
108
+ </Button>
109
+ </div>
110
+ </div>
111
+ <AccessCodeLength
112
+ supportedCodeLengths={
113
+ device.properties?.supported_code_lengths ?? []
114
+ }
115
+ />
116
+ </div>
117
+ </div>
118
+ </div>
119
+ )
120
+ }
121
+
122
+ function AccessCodeLength(props: {
123
+ supportedCodeLengths: number[]
124
+ }): JSX.Element | null {
125
+ const { supportedCodeLengths } = props
126
+
127
+ if (supportedCodeLengths.length === 0) {
128
+ return null
129
+ }
130
+
131
+ const min = Math.min(...supportedCodeLengths)
132
+ const max = Math.max(...supportedCodeLengths)
133
+
134
+ const range = min === max ? max : `${min}–${max}`
135
+
136
+ return (
137
+ <div className='seam-content seam-access-code-length'>
138
+ <span className='seam-label'>{t.codeLength}</span>
139
+ <span className='seam-value'>
140
+ {range} {t.digits}
141
+ </span>
142
+ </div>
143
+ )
144
+ }
145
+
146
+ const t = {
147
+ device: 'Device',
148
+ unlock: 'Unlock',
149
+ lock: 'Lock',
150
+ locked: 'Locked',
151
+ unlocked: 'Unlocked',
152
+ accessCodes: 'access codes',
153
+ codeLength: 'Code length',
154
+ digits: 'digits',
155
+ lockStatus: 'Lock status',
156
+ status: 'Status',
157
+ power: 'Power',
158
+ }
@@ -1,5 +1,3 @@
1
- import { getDeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
2
- import { isLockDevice } from 'lib/seam/devices/types.js'
3
1
  import type {
4
2
  UseDevicesData,
5
3
  UseDevicesParams,
@@ -27,12 +25,6 @@ export function DeviceRow({
27
25
  device,
28
26
  onClick,
29
27
  }: DeviceRowProps): JSX.Element | null {
30
- if (!isLockDevice(device)) {
31
- return null
32
- }
33
-
34
- const deviceModel = getDeviceModel(device) ?? t.unknownLock
35
-
36
28
  return (
37
29
  <TableRow key={device.device_id} onClick={onClick}>
38
30
  <TableCell className='seam-image-cell'>
@@ -41,7 +33,9 @@ export function DeviceRow({
41
33
  <TableCell className='seam-body-cell'>
42
34
  <Title className='seam-truncated-text'>{device.properties.name}</Title>
43
35
  <div className='seam-bottom'>
44
- <span className='seam-device-model'>{deviceModel}</span>
36
+ <span className='seam-device-model'>
37
+ {device.properties.model.display_name}
38
+ </span>
45
39
  <div className='seam-device-statuses'>
46
40
  <OnlineStatus device={device} />
47
41
  <BatteryStatus device={device} />
@@ -52,9 +46,3 @@ export function DeviceRow({
52
46
  </TableRow>
53
47
  )
54
48
  }
55
-
56
- const t = {
57
- devices: 'Devices',
58
- unknownLock: 'Unknown Lock',
59
- noDevicesMessage: 'Sorry, no devices were found',
60
- }
@@ -1,5 +1,6 @@
1
1
  import classNames from 'classnames'
2
2
  import { useCallback, useMemo, useState } from 'react'
3
+ import { isLockDevice } from 'seamapi'
3
4
 
4
5
  import { compareByCreatedAtDesc } from 'lib/dates.js'
5
6
  import { DeviceDetails } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
@@ -32,7 +33,10 @@ export interface DeviceTableProps {
32
33
  className?: string
33
34
  }
34
35
 
35
- const defaultDeviceFilter = (device: Device, searchInputValue: string) => {
36
+ const defaultDeviceFilter = (
37
+ device: Device,
38
+ searchInputValue: string
39
+ ): boolean => {
36
40
  const value = searchInputValue.trim()
37
41
  if (value === '') return true
38
42
  return new RegExp(value, 'i').test(device.properties.name ?? '')
@@ -57,6 +61,7 @@ export function DeviceTable({
57
61
  const filteredDevices = useMemo(
58
62
  () =>
59
63
  devices
64
+ ?.filter(isLockDevice)
60
65
  ?.filter((device) => deviceFilter(device, searchInputValue))
61
66
  ?.sort(deviceComparator) ?? [],
62
67
  [devices, searchInputValue, deviceFilter, deviceComparator]
@@ -105,13 +110,13 @@ export function DeviceTable({
105
110
  />
106
111
  </TableHeader>
107
112
  <TableBody>
108
- <Body devices={filteredDevices} onDeviceClick={handleDeviceClick} />
113
+ <Content devices={filteredDevices} onDeviceClick={handleDeviceClick} />
109
114
  </TableBody>
110
115
  </div>
111
116
  )
112
117
  }
113
118
 
114
- function Body(props: {
119
+ function Content(props: {
115
120
  devices: Array<UseDevicesData[number]>
116
121
  onDeviceClick: (deviceId: string) => void
117
122
  }): JSX.Element {
@@ -156,6 +161,5 @@ function Body(props: {
156
161
 
157
162
  const t = {
158
163
  devices: 'Devices',
159
- unknownLock: 'Unknown Lock',
160
164
  noDevicesMessage: 'Sorry, no devices were found',
161
165
  }
@@ -1,7 +1,6 @@
1
1
  import { useQuery } from '@tanstack/react-query'
2
2
  import type {
3
- CommonDeviceProperties,
4
- Device,
3
+ CommonDevice,
5
4
  DeviceGetRequest,
6
5
  DeviceGetResponse,
7
6
  SeamError,
@@ -11,7 +10,7 @@ import { useSeamClient } from 'lib/seam/use-seam-client.js'
11
10
  import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
12
11
 
13
12
  export type UseDeviceParams = DeviceGetRequest | string
14
- export type UseDeviceData = Device<CommonDeviceProperties> | null
13
+ export type UseDeviceData = CommonDevice | null
15
14
 
16
15
  export function useDevice(
17
16
  params: DeviceGetRequest
@@ -1,7 +1,6 @@
1
1
  import { useQuery, useQueryClient } from '@tanstack/react-query'
2
2
  import type {
3
- CommonDeviceProperties,
4
- Device,
3
+ CommonDevice,
5
4
  DevicesListRequest,
6
5
  DevicesListResponse,
7
6
  SeamError,
@@ -11,7 +10,7 @@ import { useSeamClient } from 'lib/seam/use-seam-client.js'
11
10
  import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
12
11
 
13
12
  export type UseDevicesParams = DevicesListRequest
14
- export type UseDevicesData = Array<Device<CommonDeviceProperties>>
13
+ export type UseDevicesData = CommonDevice[]
15
14
 
16
15
  export function useDevices(
17
16
  params?: UseDevicesParams