@seamapi/react 0.16.1 → 0.17.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 (196) hide show
  1. package/README.md +5 -2
  2. package/index.css +294 -69
  3. package/index.css.map +1 -1
  4. package/index.min.css +1 -1
  5. package/index.min.css.map +1 -1
  6. package/lib/NavigationProvider.d.ts +27 -0
  7. package/lib/NavigationProvider.js +36 -0
  8. package/lib/NavigationProvider.js.map +1 -0
  9. package/lib/NavigationView.d.ts +5 -0
  10. package/lib/NavigationView.js +25 -0
  11. package/lib/NavigationView.js.map +1 -0
  12. package/lib/icons/LockLocked.d.ts +2 -0
  13. package/lib/icons/LockLocked.js +5 -0
  14. package/lib/icons/LockLocked.js.map +1 -0
  15. package/lib/icons/LockUnlocked.d.ts +2 -0
  16. package/lib/icons/LockUnlocked.js +5 -0
  17. package/lib/icons/LockUnlocked.js.map +1 -0
  18. package/lib/seam/access-codes/use-access-code.d.ts +8 -0
  19. package/lib/seam/access-codes/use-access-code.js +32 -0
  20. package/lib/seam/access-codes/use-access-code.js.map +1 -0
  21. package/lib/seam/access-codes/use-access-codes.d.ts +9 -0
  22. package/lib/seam/access-codes/use-access-codes.js +18 -0
  23. package/lib/seam/access-codes/use-access-codes.js.map +1 -0
  24. package/lib/seam/devices/types.d.ts +2 -0
  25. package/lib/seam/devices/types.js +2 -0
  26. package/lib/seam/devices/types.js.map +1 -0
  27. package/lib/seam/devices/use-device.d.ts +9 -0
  28. package/lib/seam/devices/use-device.js +56 -0
  29. package/lib/seam/devices/use-device.js.map +1 -0
  30. package/lib/seam/devices/use-devices.d.ts +4 -9
  31. package/lib/seam/devices/use-devices.js +2 -1
  32. package/lib/seam/devices/use-devices.js.map +1 -1
  33. package/lib/seam/index.d.ts +4 -1
  34. package/lib/seam/index.js +4 -1
  35. package/lib/seam/index.js.map +1 -1
  36. package/lib/seam/use-seam-client.js +3 -3
  37. package/lib/seam/use-seam-client.js.map +1 -1
  38. package/lib/seam/use-seam-query-result.js.map +1 -0
  39. package/lib/ui/AccessCodeDetails/AccessCodeDetails.d.ts +4 -0
  40. package/lib/ui/AccessCodeDetails/AccessCodeDetails.js +63 -0
  41. package/lib/ui/AccessCodeDetails/AccessCodeDetails.js.map +1 -0
  42. package/lib/ui/AccessCodeDetails/AccessCodeDevice.d.ts +3 -0
  43. package/lib/ui/AccessCodeDetails/AccessCodeDevice.js +25 -0
  44. package/lib/ui/AccessCodeDetails/AccessCodeDevice.js.map +1 -0
  45. package/lib/ui/AccessCodeTable/AccessCodeTable.d.ts +0 -1
  46. package/lib/ui/AccessCodeTable/AccessCodeTable.js +14 -4
  47. package/lib/ui/AccessCodeTable/AccessCodeTable.js.map +1 -1
  48. package/lib/ui/AccessCodeTable/AccessCodeTableContent.d.ts +0 -1
  49. package/lib/ui/AccessCodeTable/AccessCodeTableContent.js +7 -3
  50. package/lib/ui/AccessCodeTable/AccessCodeTableContent.js.map +1 -1
  51. package/lib/ui/AccessCodeTable/CodeDetails.js +1 -1
  52. package/lib/ui/AccessCodeTable/CodeDetails.js.map +1 -1
  53. package/lib/ui/Button.d.ts +8 -0
  54. package/lib/ui/Button.js +8 -0
  55. package/lib/ui/Button.js.map +1 -0
  56. package/lib/ui/DeviceDetails/DeviceDetails.d.ts +2 -3
  57. package/lib/ui/DeviceDetails/DeviceDetails.js +31 -13
  58. package/lib/ui/DeviceDetails/DeviceDetails.js.map +1 -1
  59. package/lib/ui/DeviceDetails/DeviceModel.d.ts +7 -0
  60. package/lib/ui/DeviceDetails/DeviceModel.js +18 -0
  61. package/lib/ui/DeviceDetails/DeviceModel.js.map +1 -0
  62. package/lib/ui/DeviceTable/DeviceTable.d.ts +7 -0
  63. package/lib/ui/DeviceTable/DeviceTable.js +43 -0
  64. package/lib/ui/DeviceTable/DeviceTable.js.map +1 -0
  65. package/lib/ui/IconButton.js +1 -1
  66. package/lib/ui/IconButton.js.map +1 -1
  67. package/lib/ui/Table/TableRow.d.ts +3 -1
  68. package/lib/ui/Table/TableRow.js +1 -1
  69. package/lib/ui/Table/TableRow.js.map +1 -1
  70. package/lib/ui/TextButton.d.ts +7 -0
  71. package/lib/ui/TextButton.js +5 -0
  72. package/lib/ui/TextButton.js.map +1 -0
  73. package/lib/ui/TextField/SearchTextField.d.ts +1 -1
  74. package/lib/ui/TextField/SearchTextField.js +1 -1
  75. package/lib/ui/TextField/SearchTextField.js.map +1 -1
  76. package/lib/ui/device/BatteryStatus.d.ts +5 -0
  77. package/lib/ui/{DeviceDetails → device}/BatteryStatus.js +5 -6
  78. package/lib/ui/device/BatteryStatus.js.map +1 -0
  79. package/lib/ui/{DeviceDetails → device}/DeviceImage.d.ts +2 -1
  80. package/lib/ui/{DeviceDetails → device}/DeviceImage.js +2 -2
  81. package/lib/ui/device/DeviceImage.js.map +1 -0
  82. package/lib/ui/device/LockStatus.d.ts +5 -0
  83. package/lib/ui/device/LockStatus.js +19 -0
  84. package/lib/ui/device/LockStatus.js.map +1 -0
  85. package/lib/ui/device/OnlineStatus.d.ts +5 -0
  86. package/lib/ui/{DeviceDetails → device}/OnlineStatus.js +4 -5
  87. package/lib/ui/device/OnlineStatus.js.map +1 -0
  88. package/lib/ui/index.d.ts +3 -1
  89. package/lib/ui/index.js +3 -1
  90. package/lib/ui/index.js.map +1 -1
  91. package/lib/ui/layout/ContentHeader.d.ts +1 -2
  92. package/lib/ui/layout/ContentHeader.js +7 -1
  93. package/lib/ui/layout/ContentHeader.js.map +1 -1
  94. package/package.json +4 -3
  95. package/src/icons/lock-locked.svg +8 -0
  96. package/src/icons/lock-unlocked.svg +9 -0
  97. package/src/lib/NavigationProvider.tsx +86 -0
  98. package/src/lib/NavigationView.tsx +32 -0
  99. package/src/lib/icons/LockLocked.tsx +32 -0
  100. package/src/lib/icons/LockUnlocked.tsx +34 -0
  101. package/src/lib/seam/access-codes/use-access-code.ts +45 -0
  102. package/src/lib/seam/access-codes/use-access-codes.ts +39 -0
  103. package/src/lib/seam/devices/types.ts +5 -0
  104. package/src/lib/seam/devices/use-device.ts +79 -0
  105. package/src/lib/seam/devices/use-devices.ts +13 -8
  106. package/src/lib/seam/index.ts +4 -1
  107. package/src/lib/seam/use-seam-client.ts +3 -3
  108. package/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx +144 -0
  109. package/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +43 -0
  110. package/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +20 -16
  111. package/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx +7 -11
  112. package/src/lib/ui/AccessCodeTable/CodeDetails.tsx +1 -1
  113. package/src/lib/ui/Button.tsx +26 -0
  114. package/src/lib/ui/DeviceDetails/DeviceDetails.tsx +54 -28
  115. package/src/lib/ui/DeviceDetails/DeviceModel.tsx +34 -0
  116. package/src/lib/ui/DeviceTable/DeviceTable.tsx +94 -0
  117. package/src/lib/ui/IconButton.tsx +1 -1
  118. package/src/lib/ui/Table/TableRow.tsx +8 -2
  119. package/src/lib/ui/TextButton.tsx +15 -0
  120. package/src/lib/ui/TextField/SearchTextField.tsx +1 -1
  121. package/src/lib/ui/{DeviceDetails → device}/BatteryStatus.tsx +12 -14
  122. package/src/lib/ui/device/DeviceImage.tsx +15 -0
  123. package/src/lib/ui/device/LockStatus.tsx +46 -0
  124. package/src/lib/ui/{DeviceDetails → device}/OnlineStatus.tsx +15 -11
  125. package/src/lib/ui/index.ts +3 -1
  126. package/src/lib/ui/layout/ContentHeader.tsx +11 -6
  127. package/src/styles/_access-code-details.scss +121 -0
  128. package/src/styles/_access-code-table.scss +34 -38
  129. package/src/styles/_buttons.scss +125 -8
  130. package/src/styles/_colors.scss +30 -0
  131. package/src/styles/_device-details.scss +21 -10
  132. package/src/styles/_device-table.scss +63 -0
  133. package/src/styles/_icons.scss +9 -0
  134. package/src/styles/_inputs.scss +3 -1
  135. package/src/styles/_layout.scss +5 -1
  136. package/src/styles/_main.scss +8 -14
  137. package/src/styles/_tables.scss +6 -0
  138. package/src/styles/_typography.scss +18 -2
  139. package/lib/hooks/use-access-codes.d.ts +0 -5
  140. package/lib/hooks/use-access-codes.js +0 -16
  141. package/lib/hooks/use-access-codes.js.map +0 -1
  142. package/lib/hooks/use-device.d.ts +0 -5
  143. package/lib/hooks/use-device.js +0 -17
  144. package/lib/hooks/use-device.js.map +0 -1
  145. package/lib/hooks/use-devices.d.ts +0 -1
  146. package/lib/hooks/use-devices.js +0 -2
  147. package/lib/hooks/use-devices.js.map +0 -1
  148. package/lib/hooks/use-seam-client.d.ts +0 -1
  149. package/lib/hooks/use-seam-client.js +0 -2
  150. package/lib/hooks/use-seam-client.js.map +0 -1
  151. package/lib/seam/devices/index.d.ts +0 -1
  152. package/lib/seam/devices/index.js +0 -2
  153. package/lib/seam/devices/index.js.map +0 -1
  154. package/lib/types/use-seam-query-result.js.map +0 -1
  155. package/lib/ui/AccessCodeTable/index.d.ts +0 -2
  156. package/lib/ui/AccessCodeTable/index.js +0 -3
  157. package/lib/ui/AccessCodeTable/index.js.map +0 -1
  158. package/lib/ui/DeviceDetails/BatteryStatus.d.ts +0 -4
  159. package/lib/ui/DeviceDetails/BatteryStatus.js.map +0 -1
  160. package/lib/ui/DeviceDetails/DeviceImage.js.map +0 -1
  161. package/lib/ui/DeviceDetails/ModelStatus.d.ts +0 -4
  162. package/lib/ui/DeviceDetails/ModelStatus.js +0 -13
  163. package/lib/ui/DeviceDetails/ModelStatus.js.map +0 -1
  164. package/lib/ui/DeviceDetails/OnlineStatus.d.ts +0 -4
  165. package/lib/ui/DeviceDetails/OnlineStatus.js.map +0 -1
  166. package/lib/ui/DeviceDetails/index.d.ts +0 -5
  167. package/lib/ui/DeviceDetails/index.js +0 -6
  168. package/lib/ui/DeviceDetails/index.js.map +0 -1
  169. package/lib/ui/DeviceManager/DeviceManager.d.ts +0 -6
  170. package/lib/ui/DeviceManager/DeviceManager.js +0 -24
  171. package/lib/ui/DeviceManager/DeviceManager.js.map +0 -1
  172. package/lib/ui/DeviceManager/index.d.ts +0 -1
  173. package/lib/ui/DeviceManager/index.js +0 -2
  174. package/lib/ui/DeviceManager/index.js.map +0 -1
  175. package/lib/ui/Table/index.d.ts +0 -5
  176. package/lib/ui/Table/index.js +0 -6
  177. package/lib/ui/Table/index.js.map +0 -1
  178. package/lib/ui/TextField/index.d.ts +0 -2
  179. package/lib/ui/TextField/index.js +0 -3
  180. package/lib/ui/TextField/index.js.map +0 -1
  181. package/src/lib/hooks/use-access-codes.ts +0 -21
  182. package/src/lib/hooks/use-device.ts +0 -26
  183. package/src/lib/hooks/use-devices.ts +0 -1
  184. package/src/lib/hooks/use-seam-client.ts +0 -1
  185. package/src/lib/seam/devices/index.ts +0 -1
  186. package/src/lib/ui/AccessCodeTable/index.ts +0 -2
  187. package/src/lib/ui/DeviceDetails/DeviceImage.tsx +0 -12
  188. package/src/lib/ui/DeviceDetails/ModelStatus.tsx +0 -25
  189. package/src/lib/ui/DeviceDetails/index.ts +0 -5
  190. package/src/lib/ui/DeviceManager/DeviceManager.tsx +0 -52
  191. package/src/lib/ui/DeviceManager/index.ts +0 -1
  192. package/src/lib/ui/Table/index.ts +0 -5
  193. package/src/lib/ui/TextField/index.ts +0 -2
  194. /package/lib/{types → seam}/use-seam-query-result.d.ts +0 -0
  195. /package/lib/{types → seam}/use-seam-query-result.js +0 -0
  196. /package/src/lib/{types → seam}/use-seam-query-result.ts +0 -0
@@ -1,37 +1,49 @@
1
- import { type LockDevice } from 'seamapi'
2
-
3
1
  import { ChevronRightIcon } from 'lib/icons/ChevronRight.js'
4
- import { AccessCodeTable } from 'lib/ui/AccessCodeTable/AccessCodeTable.js'
5
- import { BatteryStatus } from 'lib/ui/DeviceDetails/BatteryStatus.js'
6
- import { DeviceImage } from 'lib/ui/DeviceDetails/DeviceImage.js'
7
- import { ModelStatus } from 'lib/ui/DeviceDetails/ModelStatus.js'
8
- import { OnlineStatus } from 'lib/ui/DeviceDetails/OnlineStatus.js'
2
+ import { useNavigation } from 'lib/NavigationProvider.js'
3
+ import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
4
+ import { isLockDevice } from 'lib/seam/devices/types.js'
5
+ import { useFakeDevice } from 'lib/seam/devices/use-device.js'
6
+ import { Button } from 'lib/ui/Button.js'
7
+ import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js'
8
+ import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
9
+ import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js'
10
+ import { DeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js'
9
11
  import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
10
- import useToggle from 'lib/use-toggle.js'
11
12
 
12
13
  export interface DeviceDetailsProps {
13
- device: LockDevice
14
+ deviceId: string
14
15
  }
15
16
 
16
- export function DeviceDetails(props: DeviceDetailsProps): JSX.Element {
17
- const { device } = props
17
+ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null {
18
+ const { deviceId } = props
18
19
 
19
- const lockStatus = device.properties.locked ? t.locked : t.unlocked
20
+ const { isLoading: isLoadingDevice, device } = useFakeDevice({
21
+ device_id: deviceId,
22
+ })
20
23
 
21
- const accessCodeLength =
22
- device.properties?.schlage_metadata?.access_code_length
24
+ const { isLoading: isLoadingAccessCodes, accessCodes } = useAccessCodes({
25
+ device_id: deviceId,
26
+ })
27
+
28
+ const isLoading = isLoadingDevice || isLoadingAccessCodes
23
29
 
24
- const [showingAccessCodes, toggleAccessCodes] = useToggle()
30
+ const { show } = useNavigation()
25
31
 
26
- if (showingAccessCodes) {
27
- return (
28
- <AccessCodeTable
29
- deviceId={device.device_id}
30
- onClickBack={toggleAccessCodes}
31
- />
32
- )
32
+ if (isLoading || !device) {
33
+ return null
34
+ }
35
+
36
+ if (!isLockDevice(device)) {
37
+ return null
33
38
  }
34
39
 
40
+ const lockStatus = device.properties.locked ? t.locked : t.unlocked
41
+
42
+ const accessCodeCount = accessCodes?.length
43
+
44
+ const accessCodeLength =
45
+ device.properties?.schlage_metadata?.access_code_length
46
+
35
47
  return (
36
48
  <div className='seam-device-details'>
37
49
  <ContentHeader title='Device' />
@@ -45,9 +57,11 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element {
45
57
  <span className='seam-label'>{t.device}</span>
46
58
  <h4 className='seam-device-name'>{device.properties.name}</h4>
47
59
  <div className='seam-properties'>
60
+ <span className='seam-label'>{t.status}:</span>{' '}
48
61
  <OnlineStatus device={device} />
62
+ <span className='seam-label'>{t.power}:</span>{' '}
49
63
  <BatteryStatus device={device} />
50
- <ModelStatus device={device} />
64
+ <DeviceModel device={device} />
51
65
  </div>
52
66
  </div>
53
67
  </div>
@@ -55,17 +69,26 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element {
55
69
  <div className='seam-box'>
56
70
  <div
57
71
  className='seam-content seam-access-codes'
58
- onClick={toggleAccessCodes}
72
+ onClick={() => {
73
+ show({ name: 'access_code_table', deviceId: device.device_id })
74
+ }}
59
75
  >
60
- <span className='seam-value'>49 {t.accessCodes}</span>
76
+ <span className='seam-value'>
77
+ {accessCodeCount} {t.accessCodes}
78
+ </span>
61
79
  <ChevronRightIcon />
62
80
  </div>
63
81
  </div>
64
82
 
65
83
  <div className='seam-box'>
66
- <div className='seam-content'>
67
- <span className='seam-label'>{t.lockStatus}</span>
68
- <span className='seam-value'>{lockStatus}</span>
84
+ <div className='seam-content seam-lock-status'>
85
+ <div>
86
+ <span className='seam-label'>{t.lockStatus}</span>
87
+ <span className='seam-value'>{lockStatus}</span>
88
+ </div>
89
+ <div className='seam-right'>
90
+ <Button size='small'>{t.unlock}</Button>
91
+ </div>
69
92
  </div>
70
93
  <AccessCodeLength accessCodeLength={accessCodeLength} />
71
94
  </div>
@@ -94,10 +117,13 @@ function AccessCodeLength(props: {
94
117
 
95
118
  const t = {
96
119
  device: 'Device',
120
+ unlock: 'Unlock',
97
121
  locked: 'Locked',
98
122
  unlocked: 'Unlocked',
99
123
  accessCodes: 'access codes',
100
124
  codeLength: 'Code length',
101
125
  digits: 'digits',
102
126
  lockStatus: 'Lock status',
127
+ status: 'Status',
128
+ power: 'Power',
103
129
  }
@@ -0,0 +1,34 @@
1
+ import type { LockDevice } from 'seamapi'
2
+
3
+ interface DeviceModelProps {
4
+ device: LockDevice
5
+ }
6
+
7
+ export function DeviceModel({ device }: DeviceModelProps): JSX.Element | null {
8
+ const model = getDeviceModel(device)
9
+ if (!model) {
10
+ return null
11
+ }
12
+
13
+ return (
14
+ <>
15
+ <span className='seam-label'>{t.model}:</span>{' '}
16
+ <div className='seam-device-model'>{model}</div>
17
+ </>
18
+ )
19
+ }
20
+
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
+ const t = {
32
+ model: 'Model',
33
+ fallbackModel: 'Unknown Lock',
34
+ }
@@ -0,0 +1,94 @@
1
+ import { useNavigation } from 'lib/NavigationProvider.js'
2
+ import { isLockDevice } from 'lib/seam/devices/types.js'
3
+ import {
4
+ useDevices,
5
+ type UseDevicesData,
6
+ type UseDevicesParams,
7
+ } from 'lib/seam/devices/use-devices.js'
8
+ import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js'
9
+ import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
10
+ import { LockStatus } from 'lib/ui/device/LockStatus.js'
11
+ import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js'
12
+ import { getDeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js'
13
+ import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
14
+ import { TableBody } from 'lib/ui/Table/TableBody.js'
15
+ import { TableCell } from 'lib/ui/Table/TableCell.js'
16
+ import { TableHeader } from 'lib/ui/Table/TableHeader.js'
17
+ import { TableRow } from 'lib/ui/Table/TableRow.js'
18
+ import { TableTitle } from 'lib/ui/Table/TableTitle.js'
19
+ import { Caption } from 'lib/ui/typography/Caption.js'
20
+ import { Title } from 'lib/ui/typography/Title.js'
21
+
22
+ export type DeviceTableProps = Props & UseDevicesParams
23
+
24
+ interface Props {
25
+ onBack?: () => void
26
+ }
27
+
28
+ export function DeviceTable({ onBack, ...props }: DeviceTableProps) {
29
+ const { devices, isLoading, isError, error } = useDevices(props)
30
+ const { show } = useNavigation()
31
+
32
+ if (isLoading) return <p>...</p>
33
+ if (isError) return <p>{error?.message}</p>
34
+ if (devices == null) return null
35
+
36
+ const deviceCount = devices.length
37
+
38
+ return (
39
+ <div className='seam-device-table'>
40
+ <ContentHeader />
41
+ <TableHeader>
42
+ <TableTitle>
43
+ {t.devices} <Caption>({deviceCount})</Caption>
44
+ </TableTitle>
45
+ </TableHeader>
46
+ <TableBody>
47
+ {devices.map((device) => (
48
+ <DeviceRow
49
+ device={device}
50
+ key={device.device_id}
51
+ onClick={() => {
52
+ show({ name: 'device_detail', deviceId: device.device_id })
53
+ }}
54
+ />
55
+ ))}
56
+ </TableBody>
57
+ </div>
58
+ )
59
+ }
60
+
61
+ function DeviceRow(props: {
62
+ device: UseDevicesData[number]
63
+ onClick: () => void
64
+ }) {
65
+ const { device, onClick } = props
66
+
67
+ if (!isLockDevice(device)) return null
68
+
69
+ const deviceModel = getDeviceModel(device) ?? t.unknownLock
70
+
71
+ return (
72
+ <TableRow key={device.device_id} onClick={onClick}>
73
+ <TableCell className='seam-image-cell'>
74
+ <DeviceImage device={device} />
75
+ </TableCell>
76
+ <TableCell className='seam-body-cell'>
77
+ <Title>{device.properties.name}</Title>
78
+ <div className='seam-bottom'>
79
+ <span className='seam-device-model'>{deviceModel}</span>
80
+ <div className='seam-device-statuses'>
81
+ <OnlineStatus device={device} />
82
+ <BatteryStatus device={device} />
83
+ <LockStatus device={device} />
84
+ </div>
85
+ </div>
86
+ </TableCell>
87
+ </TableRow>
88
+ )
89
+ }
90
+
91
+ const t = {
92
+ devices: 'Devices',
93
+ unknownLock: 'Unknown Lock',
94
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ButtonProps } from 'lib/ui/types.js'
2
2
 
3
3
  export function IconButton(props: ButtonProps): JSX.Element {
4
- return <button {...props} className='seam-button seam-icon-button' />
4
+ return <button {...props} className='seam-icon-btn' />
5
5
  }
@@ -1,5 +1,11 @@
1
1
  import type { DivProps } from 'lib/ui/types.js'
2
2
 
3
- export function TableRow(props: DivProps): JSX.Element {
4
- return <div className='seam-table-row'>{props.children}</div>
3
+ export function TableRow(
4
+ props: DivProps & { onClick?: () => void }
5
+ ): JSX.Element {
6
+ return (
7
+ <div className='seam-table-row' {...props}>
8
+ {props.children}
9
+ </div>
10
+ )
5
11
  }
@@ -0,0 +1,15 @@
1
+ import type { ButtonProps } from 'lib/ui/types.js'
2
+
3
+ interface TextButtonProps extends ButtonProps {
4
+ color?: 'primary' | 'inherit'
5
+ disabled?: boolean
6
+ }
7
+
8
+ export function TextButton({
9
+ color = 'primary',
10
+ ...buttonProps
11
+ }: TextButtonProps): JSX.Element {
12
+ return (
13
+ <button className={`seam-text-btn seam-color-${color}`} {...buttonProps} />
14
+ )
15
+ }
@@ -1,6 +1,6 @@
1
1
  import classNames from 'classnames'
2
2
 
3
- import { TextField, type TextFieldProps } from 'lib/ui/TextField/index.js'
3
+ import { TextField, type TextFieldProps } from 'lib/ui/TextField/TextField.js'
4
4
 
5
5
  export function SearchTextField({
6
6
  className,
@@ -5,32 +5,31 @@ import { BatteryLevelFullIcon } from 'lib/icons/BatteryLevelFull.js'
5
5
  import { BatteryLevelHighIcon } from 'lib/icons/BatteryLevelHigh.js'
6
6
  import { BatteryLevelLowIcon } from 'lib/icons/BatteryLevelLow.js'
7
7
 
8
- export function BatteryStatus(props: {
8
+ export interface BatteryStatusProps {
9
9
  device: LockDevice
10
- }): JSX.Element | null {
10
+ }
11
+
12
+ export function BatteryStatus(props: BatteryStatusProps) {
11
13
  const {
12
14
  device: {
13
15
  properties: { battery_level: batteryLevel },
14
16
  },
15
17
  } = props
16
18
 
17
- if (batteryLevel == null) {
18
- return null
19
- }
20
-
21
19
  return (
22
- <>
23
- <span className='seam-label'>{t.power}:</span>{' '}
24
- <div className='seam-battery-status'>
25
- <Status batteryLevel={batteryLevel} />
26
- </div>
27
- </>
20
+ <div className='seam-battery-status'>
21
+ <Content batteryLevel={batteryLevel} />
22
+ </div>
28
23
  )
29
24
  }
30
25
 
31
- function Status(props: { batteryLevel: number }): JSX.Element {
26
+ function Content(props: { batteryLevel?: number }) {
32
27
  const { batteryLevel } = props
33
28
 
29
+ if (batteryLevel == null) {
30
+ return null
31
+ }
32
+
34
33
  if (batteryLevel > batteryThreshold.full) {
35
34
  return (
36
35
  <>
@@ -73,7 +72,6 @@ const batteryThreshold = {
73
72
  }
74
73
 
75
74
  const t = {
76
- power: 'Power',
77
75
  full: 'Good',
78
76
  high: 'Good',
79
77
  low: 'Low',
@@ -0,0 +1,15 @@
1
+ import type { HTMLAttributes } from 'react'
2
+ import type { LockDevice } from 'seamapi'
3
+
4
+ export function DeviceImage(
5
+ props: { device: LockDevice } & HTMLAttributes<HTMLImageElement>
6
+ ): JSX.Element {
7
+ const { device, ...imageProps } = props
8
+
9
+ const url = device.properties.image_url ?? fallbackImageUrl
10
+
11
+ return <img src={url} alt={device.properties.name} {...imageProps} />
12
+ }
13
+
14
+ const fallbackImageUrl =
15
+ 'https://connect.getseam.com/assets/images/devices/unknown-lock.png'
@@ -0,0 +1,46 @@
1
+ import type { LockDevice } from 'seamapi'
2
+
3
+ import { LockLockedIcon } from 'lib/icons/LockLocked.js'
4
+ import { LockUnlockedIcon } from 'lib/icons/LockUnlocked.js'
5
+
6
+ export interface LockStatusProps {
7
+ device: LockDevice
8
+ }
9
+
10
+ export function LockStatus(props: LockStatusProps): JSX.Element {
11
+ const {
12
+ device: {
13
+ properties: { locked },
14
+ },
15
+ } = props
16
+
17
+ return (
18
+ <div className='seam-lock-status'>
19
+ <Content isLocked={locked} />
20
+ </div>
21
+ )
22
+ }
23
+
24
+ function Content(props: { isLocked: boolean }) {
25
+ const { isLocked } = props
26
+ if (isLocked) {
27
+ return (
28
+ <>
29
+ <LockLockedIcon className='seam-lock-icon' />
30
+ <span className='seam-status-text'>{t.locked}</span>
31
+ </>
32
+ )
33
+ }
34
+
35
+ return (
36
+ <>
37
+ <LockUnlockedIcon className='seam-lock-icon' />
38
+ <span className='seam-status-text'>{t.unlocked}</span>
39
+ </>
40
+ )
41
+ }
42
+
43
+ const t = {
44
+ locked: 'Locked',
45
+ unlocked: 'Unlocked',
46
+ }
@@ -3,21 +3,26 @@ import type { LockDevice } from 'seamapi'
3
3
  import { OnlineStatusDeviceOfflineIcon } from 'lib/icons/OnlineStatusDeviceOffline.js'
4
4
  import { OnlineStatusOnlineIcon } from 'lib/icons/OnlineStatusOnline.js'
5
5
 
6
- export function OnlineStatus(props: { device: LockDevice }): JSX.Element {
7
- const { device } = props
6
+ export interface OnlineStatusProps {
7
+ device: LockDevice
8
+ }
9
+
10
+ export function OnlineStatus(props: OnlineStatusProps): JSX.Element {
11
+ const {
12
+ device: {
13
+ properties: { online },
14
+ },
15
+ } = props
16
+
8
17
  return (
9
- <>
10
- <span className='seam-label'>{t.status}:</span>{' '}
11
- <div className='seam-online-status'>
12
- <Status isOnline={device.properties.online} />
13
- </div>
14
- </>
18
+ <div className='seam-online-status'>
19
+ <Content isOnline={online} />
20
+ </div>
15
21
  )
16
22
  }
17
23
 
18
- function Status(props: { isOnline: boolean }): JSX.Element {
24
+ function Content(props: { isOnline: boolean }) {
19
25
  const { isOnline } = props
20
-
21
26
  if (isOnline) {
22
27
  return (
23
28
  <>
@@ -38,7 +43,6 @@ function Status(props: { isOnline: boolean }): JSX.Element {
38
43
  }
39
44
 
40
45
  const t = {
41
- status: 'Status',
42
46
  online: 'Online',
43
47
  deviceOffline: 'Device Offline',
44
48
  }
@@ -1 +1,3 @@
1
- export * from './DeviceManager/DeviceManager.js'
1
+ export * from './AccessCodeTable/AccessCodeTable.js'
2
+ export * from './DeviceDetails/DeviceDetails.js'
3
+ export * from './DeviceTable/DeviceTable.js'
@@ -1,13 +1,18 @@
1
1
  import { ArrowBackIcon } from 'lib/icons/ArrowBack.js'
2
+ import { useNavigation } from 'lib/NavigationProvider.js'
3
+
4
+ export function ContentHeader(props: { title?: string }): JSX.Element | null {
5
+ const { title } = props
6
+
7
+ const { goBack } = useNavigation()
8
+ if (!title && !goBack) {
9
+ return null
10
+ }
2
11
 
3
- export function ContentHeader(props: {
4
- title?: string
5
- onClickBack?: () => void
6
- }): JSX.Element {
7
12
  return (
8
13
  <div className='seam-content-header'>
9
- <BackIcon onClick={props.onClickBack} />
10
- <span className='seam-title'>{props.title}</span>
14
+ {goBack && <BackIcon onClick={goBack} />}
15
+ <span className='seam-title'>{title}</span>
11
16
  </div>
12
17
  )
13
18
  }
@@ -0,0 +1,121 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-access-code-details {
5
+ > .seam-summary {
6
+ background: colors.$bg-a;
7
+ border-radius: 16px;
8
+ margin: 0 24px 16px;
9
+
10
+ > .seam-top {
11
+ padding: 24px 16px;
12
+
13
+ > .seam-label {
14
+ font-weight: 600;
15
+ font-size: 14px;
16
+ line-height: 134%;
17
+ color: colors.$text-gray-1;
18
+ margin-bottom: 4px;
19
+ }
20
+
21
+ > .seam-access-code-name {
22
+ font-weight: 600;
23
+ font-size: 21px;
24
+ line-height: 132%;
25
+ margin-bottom: 8px;
26
+ }
27
+
28
+ > .seam-code {
29
+ background: #fff;
30
+ border: 1px solid colors.$text-gray-2;
31
+ border-radius: 8px;
32
+ padding: 10px 16px;
33
+ font-weight: 600;
34
+ font-size: 24px;
35
+ line-height: 134%;
36
+ margin-bottom: 8px;
37
+ }
38
+
39
+ .seam-duration {
40
+ color: colors.$text-gray-1;
41
+ font-size: 14px;
42
+ line-height: 134%;
43
+
44
+ .seam-label {
45
+ font-weight: 600;
46
+ color: colors.$text-default;
47
+ }
48
+ }
49
+ }
50
+
51
+ .seam-access-code-device {
52
+ border-top: 1px solid colors.$bg-c;
53
+ padding: 8px 16px 8px 8px;
54
+ display: flex;
55
+ align-items: center;
56
+
57
+ .seam-device-image {
58
+ width: 72px;
59
+ height: 72px;
60
+ margin-right: 16px;
61
+
62
+ img {
63
+ width: 100%;
64
+ max-height: 100%;
65
+ }
66
+ }
67
+
68
+ .seam-body {
69
+ flex: 1;
70
+ font-weight: 400;
71
+ font-size: 14px;
72
+ line-height: 134%;
73
+ margin-right: 8px;
74
+ }
75
+ }
76
+ }
77
+
78
+ .seam-details {
79
+ font-size: 16px;
80
+ line-height: 134%;
81
+ border-bottom: 1px solid rgb(0 0 0 / 12%);
82
+ padding: 0 24px;
83
+
84
+ .seam-row {
85
+ padding: 16px;
86
+ display: flex;
87
+ border-top: 1px solid rgb(0 0 0 / 12%);
88
+
89
+ .seam-heading {
90
+ width: 112px;
91
+ }
92
+
93
+ .seam-content {
94
+ display: flex;
95
+ flex: 1;
96
+
97
+ .seam-times {
98
+ display: flex;
99
+ flex: 1;
100
+ font-size: 16px;
101
+ line-height: 134%;
102
+
103
+ .seam-label {
104
+ color: colors.$text-gray-2;
105
+ }
106
+
107
+ > div {
108
+ div {
109
+ margin-bottom: 4px;
110
+ }
111
+
112
+ &:first-child {
113
+ flex: 0 1 160px;
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }