@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.
- package/README.md +5 -2
- package/index.css +294 -69
- package/index.css.map +1 -1
- package/index.min.css +1 -1
- package/index.min.css.map +1 -1
- package/lib/NavigationProvider.d.ts +27 -0
- package/lib/NavigationProvider.js +36 -0
- package/lib/NavigationProvider.js.map +1 -0
- package/lib/NavigationView.d.ts +5 -0
- package/lib/NavigationView.js +25 -0
- package/lib/NavigationView.js.map +1 -0
- package/lib/icons/LockLocked.d.ts +2 -0
- package/lib/icons/LockLocked.js +5 -0
- package/lib/icons/LockLocked.js.map +1 -0
- package/lib/icons/LockUnlocked.d.ts +2 -0
- package/lib/icons/LockUnlocked.js +5 -0
- package/lib/icons/LockUnlocked.js.map +1 -0
- package/lib/seam/access-codes/use-access-code.d.ts +8 -0
- package/lib/seam/access-codes/use-access-code.js +32 -0
- package/lib/seam/access-codes/use-access-code.js.map +1 -0
- package/lib/seam/access-codes/use-access-codes.d.ts +9 -0
- package/lib/seam/access-codes/use-access-codes.js +18 -0
- package/lib/seam/access-codes/use-access-codes.js.map +1 -0
- package/lib/seam/devices/types.d.ts +2 -0
- package/lib/seam/devices/types.js +2 -0
- package/lib/seam/devices/types.js.map +1 -0
- package/lib/seam/devices/use-device.d.ts +9 -0
- package/lib/seam/devices/use-device.js +56 -0
- package/lib/seam/devices/use-device.js.map +1 -0
- package/lib/seam/devices/use-devices.d.ts +4 -9
- package/lib/seam/devices/use-devices.js +2 -1
- package/lib/seam/devices/use-devices.js.map +1 -1
- package/lib/seam/index.d.ts +4 -1
- package/lib/seam/index.js +4 -1
- package/lib/seam/index.js.map +1 -1
- package/lib/seam/use-seam-client.js +3 -3
- package/lib/seam/use-seam-client.js.map +1 -1
- package/lib/seam/use-seam-query-result.js.map +1 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDetails.d.ts +4 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDetails.js +63 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDetails.js.map +1 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDevice.d.ts +3 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDevice.js +25 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDevice.js.map +1 -0
- package/lib/ui/AccessCodeTable/AccessCodeTable.d.ts +0 -1
- package/lib/ui/AccessCodeTable/AccessCodeTable.js +14 -4
- package/lib/ui/AccessCodeTable/AccessCodeTable.js.map +1 -1
- package/lib/ui/AccessCodeTable/AccessCodeTableContent.d.ts +0 -1
- package/lib/ui/AccessCodeTable/AccessCodeTableContent.js +7 -3
- package/lib/ui/AccessCodeTable/AccessCodeTableContent.js.map +1 -1
- package/lib/ui/AccessCodeTable/CodeDetails.js +1 -1
- package/lib/ui/AccessCodeTable/CodeDetails.js.map +1 -1
- package/lib/ui/Button.d.ts +8 -0
- package/lib/ui/Button.js +8 -0
- package/lib/ui/Button.js.map +1 -0
- package/lib/ui/DeviceDetails/DeviceDetails.d.ts +2 -3
- package/lib/ui/DeviceDetails/DeviceDetails.js +31 -13
- package/lib/ui/DeviceDetails/DeviceDetails.js.map +1 -1
- package/lib/ui/DeviceDetails/DeviceModel.d.ts +7 -0
- package/lib/ui/DeviceDetails/DeviceModel.js +18 -0
- package/lib/ui/DeviceDetails/DeviceModel.js.map +1 -0
- package/lib/ui/DeviceTable/DeviceTable.d.ts +7 -0
- package/lib/ui/DeviceTable/DeviceTable.js +43 -0
- package/lib/ui/DeviceTable/DeviceTable.js.map +1 -0
- package/lib/ui/IconButton.js +1 -1
- package/lib/ui/IconButton.js.map +1 -1
- package/lib/ui/Table/TableRow.d.ts +3 -1
- package/lib/ui/Table/TableRow.js +1 -1
- package/lib/ui/Table/TableRow.js.map +1 -1
- package/lib/ui/TextButton.d.ts +7 -0
- package/lib/ui/TextButton.js +5 -0
- package/lib/ui/TextButton.js.map +1 -0
- package/lib/ui/TextField/SearchTextField.d.ts +1 -1
- package/lib/ui/TextField/SearchTextField.js +1 -1
- package/lib/ui/TextField/SearchTextField.js.map +1 -1
- package/lib/ui/device/BatteryStatus.d.ts +5 -0
- package/lib/ui/{DeviceDetails → device}/BatteryStatus.js +5 -6
- package/lib/ui/device/BatteryStatus.js.map +1 -0
- package/lib/ui/{DeviceDetails → device}/DeviceImage.d.ts +2 -1
- package/lib/ui/{DeviceDetails → device}/DeviceImage.js +2 -2
- package/lib/ui/device/DeviceImage.js.map +1 -0
- package/lib/ui/device/LockStatus.d.ts +5 -0
- package/lib/ui/device/LockStatus.js +19 -0
- package/lib/ui/device/LockStatus.js.map +1 -0
- package/lib/ui/device/OnlineStatus.d.ts +5 -0
- package/lib/ui/{DeviceDetails → device}/OnlineStatus.js +4 -5
- package/lib/ui/device/OnlineStatus.js.map +1 -0
- package/lib/ui/index.d.ts +3 -1
- package/lib/ui/index.js +3 -1
- package/lib/ui/index.js.map +1 -1
- package/lib/ui/layout/ContentHeader.d.ts +1 -2
- package/lib/ui/layout/ContentHeader.js +7 -1
- package/lib/ui/layout/ContentHeader.js.map +1 -1
- package/package.json +4 -3
- package/src/icons/lock-locked.svg +8 -0
- package/src/icons/lock-unlocked.svg +9 -0
- package/src/lib/NavigationProvider.tsx +86 -0
- package/src/lib/NavigationView.tsx +32 -0
- package/src/lib/icons/LockLocked.tsx +32 -0
- package/src/lib/icons/LockUnlocked.tsx +34 -0
- package/src/lib/seam/access-codes/use-access-code.ts +45 -0
- package/src/lib/seam/access-codes/use-access-codes.ts +39 -0
- package/src/lib/seam/devices/types.ts +5 -0
- package/src/lib/seam/devices/use-device.ts +79 -0
- package/src/lib/seam/devices/use-devices.ts +13 -8
- package/src/lib/seam/index.ts +4 -1
- package/src/lib/seam/use-seam-client.ts +3 -3
- package/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx +144 -0
- package/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +43 -0
- package/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +20 -16
- package/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx +7 -11
- package/src/lib/ui/AccessCodeTable/CodeDetails.tsx +1 -1
- package/src/lib/ui/Button.tsx +26 -0
- package/src/lib/ui/DeviceDetails/DeviceDetails.tsx +54 -28
- package/src/lib/ui/DeviceDetails/DeviceModel.tsx +34 -0
- package/src/lib/ui/DeviceTable/DeviceTable.tsx +94 -0
- package/src/lib/ui/IconButton.tsx +1 -1
- package/src/lib/ui/Table/TableRow.tsx +8 -2
- package/src/lib/ui/TextButton.tsx +15 -0
- package/src/lib/ui/TextField/SearchTextField.tsx +1 -1
- package/src/lib/ui/{DeviceDetails → device}/BatteryStatus.tsx +12 -14
- package/src/lib/ui/device/DeviceImage.tsx +15 -0
- package/src/lib/ui/device/LockStatus.tsx +46 -0
- package/src/lib/ui/{DeviceDetails → device}/OnlineStatus.tsx +15 -11
- package/src/lib/ui/index.ts +3 -1
- package/src/lib/ui/layout/ContentHeader.tsx +11 -6
- package/src/styles/_access-code-details.scss +121 -0
- package/src/styles/_access-code-table.scss +34 -38
- package/src/styles/_buttons.scss +125 -8
- package/src/styles/_colors.scss +30 -0
- package/src/styles/_device-details.scss +21 -10
- package/src/styles/_device-table.scss +63 -0
- package/src/styles/_icons.scss +9 -0
- package/src/styles/_inputs.scss +3 -1
- package/src/styles/_layout.scss +5 -1
- package/src/styles/_main.scss +8 -14
- package/src/styles/_tables.scss +6 -0
- package/src/styles/_typography.scss +18 -2
- package/lib/hooks/use-access-codes.d.ts +0 -5
- package/lib/hooks/use-access-codes.js +0 -16
- package/lib/hooks/use-access-codes.js.map +0 -1
- package/lib/hooks/use-device.d.ts +0 -5
- package/lib/hooks/use-device.js +0 -17
- package/lib/hooks/use-device.js.map +0 -1
- package/lib/hooks/use-devices.d.ts +0 -1
- package/lib/hooks/use-devices.js +0 -2
- package/lib/hooks/use-devices.js.map +0 -1
- package/lib/hooks/use-seam-client.d.ts +0 -1
- package/lib/hooks/use-seam-client.js +0 -2
- package/lib/hooks/use-seam-client.js.map +0 -1
- package/lib/seam/devices/index.d.ts +0 -1
- package/lib/seam/devices/index.js +0 -2
- package/lib/seam/devices/index.js.map +0 -1
- package/lib/types/use-seam-query-result.js.map +0 -1
- package/lib/ui/AccessCodeTable/index.d.ts +0 -2
- package/lib/ui/AccessCodeTable/index.js +0 -3
- package/lib/ui/AccessCodeTable/index.js.map +0 -1
- package/lib/ui/DeviceDetails/BatteryStatus.d.ts +0 -4
- package/lib/ui/DeviceDetails/BatteryStatus.js.map +0 -1
- package/lib/ui/DeviceDetails/DeviceImage.js.map +0 -1
- package/lib/ui/DeviceDetails/ModelStatus.d.ts +0 -4
- package/lib/ui/DeviceDetails/ModelStatus.js +0 -13
- package/lib/ui/DeviceDetails/ModelStatus.js.map +0 -1
- package/lib/ui/DeviceDetails/OnlineStatus.d.ts +0 -4
- package/lib/ui/DeviceDetails/OnlineStatus.js.map +0 -1
- package/lib/ui/DeviceDetails/index.d.ts +0 -5
- package/lib/ui/DeviceDetails/index.js +0 -6
- package/lib/ui/DeviceDetails/index.js.map +0 -1
- package/lib/ui/DeviceManager/DeviceManager.d.ts +0 -6
- package/lib/ui/DeviceManager/DeviceManager.js +0 -24
- package/lib/ui/DeviceManager/DeviceManager.js.map +0 -1
- package/lib/ui/DeviceManager/index.d.ts +0 -1
- package/lib/ui/DeviceManager/index.js +0 -2
- package/lib/ui/DeviceManager/index.js.map +0 -1
- package/lib/ui/Table/index.d.ts +0 -5
- package/lib/ui/Table/index.js +0 -6
- package/lib/ui/Table/index.js.map +0 -1
- package/lib/ui/TextField/index.d.ts +0 -2
- package/lib/ui/TextField/index.js +0 -3
- package/lib/ui/TextField/index.js.map +0 -1
- package/src/lib/hooks/use-access-codes.ts +0 -21
- package/src/lib/hooks/use-device.ts +0 -26
- package/src/lib/hooks/use-devices.ts +0 -1
- package/src/lib/hooks/use-seam-client.ts +0 -1
- package/src/lib/seam/devices/index.ts +0 -1
- package/src/lib/ui/AccessCodeTable/index.ts +0 -2
- package/src/lib/ui/DeviceDetails/DeviceImage.tsx +0 -12
- package/src/lib/ui/DeviceDetails/ModelStatus.tsx +0 -25
- package/src/lib/ui/DeviceDetails/index.ts +0 -5
- package/src/lib/ui/DeviceManager/DeviceManager.tsx +0 -52
- package/src/lib/ui/DeviceManager/index.ts +0 -1
- package/src/lib/ui/Table/index.ts +0 -5
- package/src/lib/ui/TextField/index.ts +0 -2
- /package/lib/{types → seam}/use-seam-query-result.d.ts +0 -0
- /package/lib/{types → seam}/use-seam-query-result.js +0 -0
- /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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
-
|
|
14
|
+
deviceId: string
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function DeviceDetails(props: DeviceDetailsProps): JSX.Element {
|
|
17
|
-
const {
|
|
17
|
+
export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null {
|
|
18
|
+
const { deviceId } = props
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
+
const { isLoading: isLoadingDevice, device } = useFakeDevice({
|
|
21
|
+
device_id: deviceId,
|
|
22
|
+
})
|
|
20
23
|
|
|
21
|
-
const
|
|
22
|
-
|
|
24
|
+
const { isLoading: isLoadingAccessCodes, accessCodes } = useAccessCodes({
|
|
25
|
+
device_id: deviceId,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const isLoading = isLoadingDevice || isLoadingAccessCodes
|
|
23
29
|
|
|
24
|
-
const
|
|
30
|
+
const { show } = useNavigation()
|
|
25
31
|
|
|
26
|
-
if (
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
<
|
|
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={
|
|
72
|
+
onClick={() => {
|
|
73
|
+
show({ name: 'access_code_table', deviceId: device.device_id })
|
|
74
|
+
}}
|
|
59
75
|
>
|
|
60
|
-
<span className='seam-value'>
|
|
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
|
-
<
|
|
68
|
-
|
|
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,11 @@
|
|
|
1
1
|
import type { DivProps } from 'lib/ui/types.js'
|
|
2
2
|
|
|
3
|
-
export function TableRow(
|
|
4
|
-
|
|
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
|
+
}
|
|
@@ -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
|
|
8
|
+
export interface BatteryStatusProps {
|
|
9
9
|
device: LockDevice
|
|
10
|
-
}
|
|
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
|
-
<
|
|
24
|
-
|
|
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
|
|
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
|
|
7
|
-
|
|
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
|
-
<
|
|
11
|
-
|
|
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
|
|
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
|
}
|
package/src/lib/ui/index.ts
CHANGED
|
@@ -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={
|
|
10
|
-
<span className='seam-title'>{
|
|
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
|
+
}
|