@seamapi/react 0.16.0 → 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 +403 -71
- 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 +12 -9
- 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 +5 -0
- package/lib/ui/AccessCodeTable/AccessCodeTableContent.js +19 -0
- package/lib/ui/AccessCodeTable/AccessCodeTableContent.js.map +1 -0
- 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 +32 -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 +8 -7
- 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 +12 -9
- 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 +57 -0
- package/src/lib/ui/AccessCodeTable/CodeDetails.tsx +1 -1
- package/src/lib/ui/Button.tsx +26 -0
- package/src/lib/ui/DeviceDetails/DeviceDetails.tsx +56 -29
- 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 +28 -9
- 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 -2
- package/src/styles/_main.scss +10 -16
- package/src/styles/_reset.scss +114 -0
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamapi/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Seam React component library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"!src/stories"
|
|
45
45
|
],
|
|
46
46
|
"scripts": {
|
|
47
|
-
"start": "concurrently --raw --kill-others npm:examples npm:storybook",
|
|
47
|
+
"start": "concurrently --raw --kill-others npm:examples:storybook npm:storybook",
|
|
48
48
|
"dev": "npm run start",
|
|
49
49
|
"build": "npm run build:js",
|
|
50
50
|
"prebuild": "del 'index.*' lib",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"postbuild:js": "tsc-alias --project tsconfig.build.json",
|
|
55
55
|
"build:css": "sass --load-path=node_modules src/index.scss:index.css",
|
|
56
56
|
"postbuild:css": "sass --style=compressed --load-path=node_modules src/index.scss:index.min.css",
|
|
57
|
-
"docs:start": "concurrently --raw --kill-others npm:examples npm:storybook:docs",
|
|
57
|
+
"docs:start": "concurrently --raw --kill-others npm:examples:storybook npm:storybook:docs",
|
|
58
58
|
"docs:build": "storybook build --docs --output-dir docs",
|
|
59
59
|
"predocs:build": "npm run examples:build",
|
|
60
60
|
"typecheck": "tsc",
|
|
@@ -71,9 +71,10 @@
|
|
|
71
71
|
"storybook:docs": "storybook dev --docs --port 6007",
|
|
72
72
|
"storybook:build": "storybook build",
|
|
73
73
|
"prestorybook:build": "vite build examples",
|
|
74
|
-
"examples": "vite examples
|
|
74
|
+
"examples": "vite examples",
|
|
75
75
|
"examples:build": "vite build examples",
|
|
76
76
|
"examples:preview": "vite preview examples",
|
|
77
|
+
"examples:storybook": "vite examples --host --clearScreen false --mode storybook",
|
|
77
78
|
"preexamples:preview": "npm run examples:build",
|
|
78
79
|
"generate": "svgr --out-dir src/lib/icons -- src/icons",
|
|
79
80
|
"pregenerate": "del src/lib/icons",
|
|
@@ -94,7 +95,7 @@
|
|
|
94
95
|
"@tanstack/react-query": "^4.28.0",
|
|
95
96
|
"classnames": "^2.3.2",
|
|
96
97
|
"luxon": "^3.3.0",
|
|
97
|
-
"seamapi": "^5.
|
|
98
|
+
"seamapi": "^5.21.0",
|
|
98
99
|
"uuid": "^9.0.0"
|
|
99
100
|
},
|
|
100
101
|
"devDependencies": {
|
|
@@ -148,12 +149,12 @@
|
|
|
148
149
|
"storybook-addon-designs": "^7.0.0-beta.2",
|
|
149
150
|
"stylelint": "^15.6.0",
|
|
150
151
|
"stylelint-config-standard-scss": "^9.0.0",
|
|
151
|
-
"the-new-css-reset": "^1.8.4",
|
|
152
152
|
"ts-jest": "^29.1.0",
|
|
153
153
|
"tsc-alias": "^1.8.5",
|
|
154
154
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
|
155
155
|
"typescript": "^5.0.2",
|
|
156
156
|
"vite": "^4.2.1",
|
|
157
|
-
"vite-tsconfig-paths": "^4.0.8"
|
|
157
|
+
"vite-tsconfig-paths": "^4.0.8",
|
|
158
|
+
"zustand-hoist": "^1.0.0"
|
|
158
159
|
}
|
|
159
160
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<mask id="mask0_221_23114" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="25" height="25">
|
|
3
|
+
<rect x="0.460693" y="0.838623" width="24" height="24" fill="#D9D9D9"/>
|
|
4
|
+
</mask>
|
|
5
|
+
<g mask="url(#mask0_221_23114)">
|
|
6
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.924 3.89346C3.45898 3.89346 1.46069 5.89176 1.46069 8.35678V11.702C1.46069 12.2543 1.90841 12.702 2.46069 12.702C3.01298 12.702 3.46069 12.2543 3.46069 11.702V8.35678C3.46069 6.99633 4.56356 5.89346 5.924 5.89346C7.28445 5.89346 8.38731 6.99633 8.38731 8.35677V12.8385H7.72008C7.02454 12.8385 6.46069 13.4024 6.46069 14.0979V19.5791C6.46069 20.2747 7.02454 20.8385 7.72008 20.8385H17.2013C17.8968 20.8385 18.4607 20.2747 18.4607 19.5791V14.0979C18.4607 13.4024 17.8968 12.8385 17.2013 12.8385H10.3873V8.35677C10.3873 5.89176 8.38902 3.89346 5.924 3.89346Z" fill="#232B3A"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<mask id="mask0_221_23076" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="25" height="25">
|
|
3
|
+
<rect x="0.460693" y="0.838623" width="24" height="24" fill="#D9D9D9"/>
|
|
4
|
+
</mask>
|
|
5
|
+
<g mask="url(#mask0_221_23076)">
|
|
6
|
+
<rect x="6.46069" y="12.8386" width="12" height="8.15217" rx="1.25939" fill="#232B3A"/>
|
|
7
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.94067 10.3586C7.94067 7.8623 9.96435 5.83862 12.4607 5.83862C14.957 5.83862 16.9807 7.86229 16.9807 10.3586V16.0506C16.9807 16.6029 16.533 17.0506 15.9807 17.0506C15.4284 17.0506 14.9807 16.6029 14.9807 16.0506V10.3586C14.9807 8.96686 13.8524 7.83862 12.4607 7.83862C11.0689 7.83862 9.94067 8.96687 9.94067 10.3586V13.8902C9.94067 14.4425 9.49296 14.8902 8.94067 14.8902C8.38839 14.8902 7.94067 14.4425 7.94067 13.8902V10.3586Z" fill="#232B3A"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
interface DeviceTableView {
|
|
4
|
+
name: 'device_table'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface DeviceDetailView {
|
|
8
|
+
name: 'device_detail'
|
|
9
|
+
deviceId: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface AccessCodeTableView {
|
|
13
|
+
name: 'access_code_table'
|
|
14
|
+
deviceId: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface AccessCodeDetailView {
|
|
18
|
+
name: 'access_code_detail'
|
|
19
|
+
accessCodeId: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type View =
|
|
23
|
+
| DeviceTableView
|
|
24
|
+
| DeviceDetailView
|
|
25
|
+
| AccessCodeTableView
|
|
26
|
+
| AccessCodeDetailView
|
|
27
|
+
|
|
28
|
+
export interface NavigationStackProps {
|
|
29
|
+
children: JSX.Element
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface NavigationContextProps {
|
|
33
|
+
views: View[]
|
|
34
|
+
show: (view: View) => void
|
|
35
|
+
goBack?: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const NavigationContext = React.createContext<
|
|
39
|
+
NavigationContextProps | undefined
|
|
40
|
+
>(undefined)
|
|
41
|
+
|
|
42
|
+
export default function NavigationProvider({ children }: NavigationStackProps) {
|
|
43
|
+
const [views, setViews] = useState<View[]>([])
|
|
44
|
+
|
|
45
|
+
const pushView = (view: View) => {
|
|
46
|
+
setViews((current) => [view, ...current])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const popView = () => {
|
|
50
|
+
setViews((current) => {
|
|
51
|
+
if (current.length === 0) {
|
|
52
|
+
return current
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const [_showing, ...others] = current
|
|
56
|
+
return others
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hasViews = views.length > 0
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<NavigationContext.Provider
|
|
64
|
+
value={{
|
|
65
|
+
views,
|
|
66
|
+
show: pushView,
|
|
67
|
+
goBack: hasViews ? popView : undefined,
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</NavigationContext.Provider>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function useNavigation(): NavigationContextProps {
|
|
76
|
+
const context = React.useContext(NavigationContext)
|
|
77
|
+
if (context === undefined) {
|
|
78
|
+
return {
|
|
79
|
+
show: () => {},
|
|
80
|
+
goBack: undefined,
|
|
81
|
+
views: [],
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return context
|
|
86
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AccessCodeTable, DeviceDetails, DeviceTable } from 'lib/index.js'
|
|
2
|
+
|
|
3
|
+
import { useNavigation, type View } from 'lib/NavigationProvider.js'
|
|
4
|
+
import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js'
|
|
5
|
+
|
|
6
|
+
interface NavigationViewProps {
|
|
7
|
+
children: JSX.Element
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function NavigationView({ children: rootView }: NavigationViewProps) {
|
|
11
|
+
const { views } = useNavigation()
|
|
12
|
+
|
|
13
|
+
const firstView = views[0]
|
|
14
|
+
if (firstView != null) {
|
|
15
|
+
return <Subview view={firstView} />
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return rootView
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Subview({ view }: { view: View }) {
|
|
22
|
+
switch (view.name) {
|
|
23
|
+
case 'device_detail':
|
|
24
|
+
return <DeviceDetails deviceId={view.deviceId} />
|
|
25
|
+
case 'access_code_detail':
|
|
26
|
+
return <AccessCodeDetails accessCodeId={view.accessCodeId} />
|
|
27
|
+
case 'device_table':
|
|
28
|
+
return <DeviceTable />
|
|
29
|
+
case 'access_code_table':
|
|
30
|
+
return <AccessCodeTable deviceId={view.deviceId} />
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SVGProps } from 'react'
|
|
2
|
+
export const LockLockedIcon = (props: SVGProps<SVGSVGElement>): JSX.Element => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
5
|
+
width={25}
|
|
6
|
+
height={25}
|
|
7
|
+
fill='none'
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<mask
|
|
11
|
+
id='lock-locked_svg__a'
|
|
12
|
+
width={25}
|
|
13
|
+
height={25}
|
|
14
|
+
x={0}
|
|
15
|
+
y={0}
|
|
16
|
+
maskUnits='userSpaceOnUse'
|
|
17
|
+
style={{
|
|
18
|
+
maskType: 'alpha',
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<path fill='#D9D9D9' d='M.461.839h24v24h-24z' />
|
|
22
|
+
</mask>
|
|
23
|
+
<g mask='url(#lock-locked_svg__a)'>
|
|
24
|
+
<path
|
|
25
|
+
fill='#232B3A'
|
|
26
|
+
fillRule='evenodd'
|
|
27
|
+
d='M5.924 3.893a4.463 4.463 0 0 0-4.463 4.464v3.345a1 1 0 1 0 2 0V8.357a2.463 2.463 0 0 1 4.926 0v4.482H7.72a1.26 1.26 0 0 0-1.26 1.259v5.481c0 .696.565 1.26 1.26 1.26h9.481a1.26 1.26 0 0 0 1.26-1.26v-5.481a1.26 1.26 0 0 0-1.26-1.26h-6.814V8.357a4.463 4.463 0 0 0-4.463-4.464Z'
|
|
28
|
+
clipRule='evenodd'
|
|
29
|
+
/>
|
|
30
|
+
</g>
|
|
31
|
+
</svg>
|
|
32
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SVGProps } from 'react'
|
|
2
|
+
export const LockUnlockedIcon = (
|
|
3
|
+
props: SVGProps<SVGSVGElement>
|
|
4
|
+
): JSX.Element => (
|
|
5
|
+
<svg
|
|
6
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
7
|
+
width={25}
|
|
8
|
+
height={25}
|
|
9
|
+
fill='none'
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<mask
|
|
13
|
+
id='lock-unlocked_svg__a'
|
|
14
|
+
width={25}
|
|
15
|
+
height={25}
|
|
16
|
+
x={0}
|
|
17
|
+
y={0}
|
|
18
|
+
maskUnits='userSpaceOnUse'
|
|
19
|
+
style={{
|
|
20
|
+
maskType: 'alpha',
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<path fill='#D9D9D9' d='M.461.839h24v24h-24z' />
|
|
24
|
+
</mask>
|
|
25
|
+
<g fill='#232B3A' mask='url(#lock-unlocked_svg__a)'>
|
|
26
|
+
<rect width={12} height={8.152} x={6.461} y={12.839} rx={1.259} />
|
|
27
|
+
<path
|
|
28
|
+
fillRule='evenodd'
|
|
29
|
+
d='M7.94 10.359a4.52 4.52 0 0 1 9.04 0v5.692a1 1 0 1 1-2 0v-5.692a2.52 2.52 0 0 0-5.04 0v3.531a1 1 0 1 1-2 0V10.36Z'
|
|
30
|
+
clipRule='evenodd'
|
|
31
|
+
/>
|
|
32
|
+
</g>
|
|
33
|
+
</svg>
|
|
34
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { DateTime } from 'luxon'
|
|
3
|
+
import type { AccessCode, AccessCodeGetRequest } from 'seamapi'
|
|
4
|
+
import { v4 as uuid } from 'uuid'
|
|
5
|
+
|
|
6
|
+
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
7
|
+
|
|
8
|
+
export type UseAccessCodeParams = AccessCodeGetRequest | string
|
|
9
|
+
export type UseAccessCodeData = AccessCode
|
|
10
|
+
|
|
11
|
+
export function useAccessCode(params: UseAccessCodeParams) {
|
|
12
|
+
const { client } = useSeamClient()
|
|
13
|
+
|
|
14
|
+
const normalizedParams =
|
|
15
|
+
typeof params === 'string' ? { access_code_id: params } : params
|
|
16
|
+
|
|
17
|
+
return useQuery({
|
|
18
|
+
enabled: client != null,
|
|
19
|
+
queryKey: ['access_code', 'get', normalizedParams],
|
|
20
|
+
queryFn: async () => {
|
|
21
|
+
if (client == null) return null
|
|
22
|
+
|
|
23
|
+
return await client?.accessCodes.get(normalizedParams)
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useFakeAccessCode(_params: UseAccessCodeParams): {
|
|
29
|
+
isLoading: boolean
|
|
30
|
+
data: AccessCode
|
|
31
|
+
} {
|
|
32
|
+
const accessCode: AccessCode = {
|
|
33
|
+
access_code_id: uuid(),
|
|
34
|
+
type: 'time_bound',
|
|
35
|
+
starts_at: DateTime.now().minus({ days: 2 }).toISO() ?? '',
|
|
36
|
+
ends_at: DateTime.now().plus({ days: 1 }).toISO() ?? '',
|
|
37
|
+
code: '1234',
|
|
38
|
+
created_at: DateTime.now().toISO() ?? '',
|
|
39
|
+
device_id: 'some_device_id',
|
|
40
|
+
status: 'set',
|
|
41
|
+
name: 'Guest - Kranz',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { isLoading: false, data: accessCode }
|
|
45
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import type { AccessCode, AccessCodesListResponse, SeamError } from 'seamapi'
|
|
3
|
+
|
|
4
|
+
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
5
|
+
import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
|
|
6
|
+
|
|
7
|
+
export type UseAccessCodesParams = AccessCodesListRequest | string
|
|
8
|
+
export type UseAccessCodesData = AccessCode[]
|
|
9
|
+
|
|
10
|
+
export function useAccessCodes(
|
|
11
|
+
params: UseAccessCodesParams
|
|
12
|
+
): UseSeamQueryResult<'accessCodes', UseAccessCodesData> {
|
|
13
|
+
const normalizedParams =
|
|
14
|
+
typeof params === 'string' ? { device_id: params } : params
|
|
15
|
+
|
|
16
|
+
const { client } = useSeamClient()
|
|
17
|
+
const { data, ...rest } = useQuery<
|
|
18
|
+
AccessCodesListResponse['access_codes'],
|
|
19
|
+
SeamError
|
|
20
|
+
>({
|
|
21
|
+
enabled: client != null,
|
|
22
|
+
queryKey: ['access_codes', 'list', normalizedParams],
|
|
23
|
+
queryFn: async () => {
|
|
24
|
+
if (client == null) return []
|
|
25
|
+
const accessCodes = await client?.accessCodes.list(normalizedParams)
|
|
26
|
+
return accessCodes.sort(
|
|
27
|
+
(a, b) =>
|
|
28
|
+
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
29
|
+
)
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return { ...rest, accessCodes: data }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// UPSTREAM: https://github.com/seamapi/javascript/pull/200
|
|
37
|
+
interface AccessCodesListRequest {
|
|
38
|
+
device_id: string
|
|
39
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import type {
|
|
3
|
+
CommonDeviceProperties,
|
|
4
|
+
Device,
|
|
5
|
+
DeviceGetRequest,
|
|
6
|
+
DeviceGetResponse,
|
|
7
|
+
LockDevice,
|
|
8
|
+
SeamError,
|
|
9
|
+
} from 'seamapi'
|
|
10
|
+
|
|
11
|
+
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
12
|
+
import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
|
|
13
|
+
|
|
14
|
+
export type UseDeviceParams = DeviceGetRequest | string
|
|
15
|
+
export type UseDeviceData = Device<CommonDeviceProperties> | null
|
|
16
|
+
|
|
17
|
+
export function useDevice(
|
|
18
|
+
params: DeviceGetRequest
|
|
19
|
+
): UseSeamQueryResult<'device', UseDeviceData> {
|
|
20
|
+
const normalizedParams =
|
|
21
|
+
typeof params === 'string' ? { device_id: params } : params
|
|
22
|
+
|
|
23
|
+
const { client } = useSeamClient()
|
|
24
|
+
const { data, ...rest } = useQuery<
|
|
25
|
+
DeviceGetResponse['device'] | null,
|
|
26
|
+
SeamError
|
|
27
|
+
>({
|
|
28
|
+
enabled: client != null,
|
|
29
|
+
queryKey: ['devices', 'get', normalizedParams],
|
|
30
|
+
queryFn: async () => {
|
|
31
|
+
if (client == null) return null
|
|
32
|
+
return await client?.devices.get(normalizedParams)
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return { ...rest, device: data }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useFakeDevice(_params: DeviceGetRequest) {
|
|
40
|
+
const device: LockDevice = {
|
|
41
|
+
device_id: 'f9a9ab36-9e14-4390-a88c-b4c78304c6aa',
|
|
42
|
+
device_type: 'august_lock',
|
|
43
|
+
capabilities_supported: ['access_code', 'lock'],
|
|
44
|
+
properties: {
|
|
45
|
+
locked: true,
|
|
46
|
+
online: true,
|
|
47
|
+
door_open: false,
|
|
48
|
+
manufacturer: 'august',
|
|
49
|
+
battery_level: 0.9999532347993827,
|
|
50
|
+
serial_number: '00000004-992d-45a0-bea1-9128fdcd8d12',
|
|
51
|
+
august_metadata: {
|
|
52
|
+
lock_id: 'lock-1',
|
|
53
|
+
house_id: 'house-1',
|
|
54
|
+
lock_name: 'FRONT DOOR',
|
|
55
|
+
has_keypad: true,
|
|
56
|
+
house_name: 'My House',
|
|
57
|
+
},
|
|
58
|
+
supported_code_lengths: [6],
|
|
59
|
+
name: 'FRONT DOOR',
|
|
60
|
+
battery: {
|
|
61
|
+
level: 0.9999532347993827,
|
|
62
|
+
status: 'full',
|
|
63
|
+
},
|
|
64
|
+
image_url:
|
|
65
|
+
'https://connect.getseam.com/assets/images/devices/august_wifi-smart-lock-3rd-gen_silver_front.png',
|
|
66
|
+
},
|
|
67
|
+
location: {
|
|
68
|
+
timezone: 'America/Los_Angeles',
|
|
69
|
+
location_name: 'My House',
|
|
70
|
+
},
|
|
71
|
+
connected_account_id: '6fc0f854-cb5a-461a-af93-e543ce1f730f',
|
|
72
|
+
workspace_id: 'a9598de8-e627-4d01-b119-61ab7b9e07bd',
|
|
73
|
+
created_at: '2023-05-08T22:37:24.206Z',
|
|
74
|
+
errors: [],
|
|
75
|
+
warnings: [],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { isLoading: false, device }
|
|
79
|
+
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { useQuery
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
2
|
import type {
|
|
3
|
+
CommonDeviceProperties,
|
|
4
|
+
Device,
|
|
3
5
|
DevicesListRequest,
|
|
4
6
|
DevicesListResponse,
|
|
5
7
|
SeamError,
|
|
6
8
|
} from 'seamapi'
|
|
7
9
|
|
|
8
10
|
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
11
|
+
import type { UseSeamQueryResult } from 'lib/seam/use-seam-query-result.js'
|
|
9
12
|
|
|
10
13
|
export type UseDevicesParams = DevicesListRequest
|
|
11
|
-
export type UseDevicesData =
|
|
14
|
+
export type UseDevicesData = Array<Device<CommonDeviceProperties>>
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export function useDevices(params: DevicesListRequest): UseDevicesResult {
|
|
16
|
+
export function useDevices(
|
|
17
|
+
params?: UseDevicesParams
|
|
18
|
+
): UseSeamQueryResult<'devices', UseDevicesData> {
|
|
18
19
|
const { client } = useSeamClient()
|
|
19
20
|
const { data, ...rest } = useQuery<DevicesListResponse['devices'], SeamError>(
|
|
20
21
|
{
|
|
@@ -22,7 +23,11 @@ export function useDevices(params: DevicesListRequest): UseDevicesResult {
|
|
|
22
23
|
queryKey: ['devices', 'list', params],
|
|
23
24
|
queryFn: async () => {
|
|
24
25
|
if (client == null) return []
|
|
25
|
-
|
|
26
|
+
const devices = await client?.devices.list(params)
|
|
27
|
+
return devices.sort(
|
|
28
|
+
(a, b) =>
|
|
29
|
+
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
30
|
+
)
|
|
26
31
|
},
|
|
27
32
|
}
|
|
28
33
|
)
|
package/src/lib/seam/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { useEffect } from 'react'
|
|
2
3
|
import { Seam } from 'seamapi'
|
|
3
4
|
import { v4 as uuidv4 } from 'uuid'
|
|
4
5
|
|
|
@@ -68,24 +69,26 @@ export function useSeamClient(): {
|
|
|
68
69
|
return { client: data ?? null, isLoading, isError, error }
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
/**
|
|
72
|
-
* You'll almost always want to supply a user identifier key, but if you don't
|
|
73
|
-
* we'll automatically pick a fingerprint for this user. The user interface
|
|
74
|
-
* will show warnings when you use a fingerprint because this client session
|
|
75
|
-
* is bound to this machine, and is effectively ephemeral.
|
|
76
|
-
*/
|
|
77
72
|
function useUserIdentifierKeyOrFingerprint(
|
|
78
73
|
userIdentifierKey: string | undefined
|
|
79
74
|
): string {
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (userIdentifierKey != null) return
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.warn(`Using an automatically generated fingerprint for the SeamProvider userIdentifierKey!
|
|
79
|
+
The user interface will show warnings when using a fingerprint.
|
|
80
|
+
This is not recommended because the client session is now bound to this machine and is effectively ephemeral.`)
|
|
81
|
+
}, [userIdentifierKey])
|
|
82
|
+
|
|
80
83
|
if (userIdentifierKey != null) {
|
|
81
84
|
return userIdentifierKey
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
const
|
|
87
|
+
const fingerprint =
|
|
85
88
|
globalThis?.localStorage?.getItem('seam_user_fingerprint') ??
|
|
86
89
|
`fingerprint_${uuidv4()}`
|
|
87
90
|
|
|
88
|
-
globalThis?.localStorage?.setItem('seam_user_fingerprint',
|
|
91
|
+
globalThis?.localStorage?.setItem('seam_user_fingerprint', fingerprint)
|
|
89
92
|
|
|
90
|
-
return
|
|
93
|
+
return fingerprint
|
|
91
94
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { DateTime } from 'luxon'
|
|
2
|
+
import type { AccessCode } from 'seamapi'
|
|
3
|
+
|
|
4
|
+
import { useFakeAccessCode } from 'lib/index.js'
|
|
5
|
+
|
|
6
|
+
import AccessCodeDevice from 'lib/ui/AccessCodeDetails/AccessCodeDevice.js'
|
|
7
|
+
import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
|
|
8
|
+
|
|
9
|
+
export interface AccessCodeDetailsProps {
|
|
10
|
+
accessCodeId: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function AccessCodeDetails({
|
|
14
|
+
accessCodeId,
|
|
15
|
+
}: AccessCodeDetailsProps) {
|
|
16
|
+
const { isLoading, data: accessCode } = useFakeAccessCode({
|
|
17
|
+
access_code_id: accessCodeId,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (isLoading || !accessCode) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const name = accessCode.name ?? t.fallbackName
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className='seam-access-code-details'>
|
|
28
|
+
<ContentHeader title='Access code' />
|
|
29
|
+
<div className='seam-summary'>
|
|
30
|
+
<div className='seam-top'>
|
|
31
|
+
<span className='seam-label'>{t.accessCode}</span>
|
|
32
|
+
<h5 className='seam-access-code-name'>{name}</h5>
|
|
33
|
+
<div className='seam-code'>{accessCode.code}</div>
|
|
34
|
+
<div className='seam-duration'>
|
|
35
|
+
<Duration accessCode={accessCode} />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<AccessCodeDevice deviceId={accessCode.device_id} />
|
|
39
|
+
</div>
|
|
40
|
+
<div className='seam-details'>
|
|
41
|
+
<div className='seam-row'>
|
|
42
|
+
<div className='seam-heading'>{t.id}:</div>
|
|
43
|
+
<div className='seam-content'>{accessCode.access_code_id}</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div className='seam-row'>
|
|
46
|
+
<div className='seam-heading'>{t.created}:</div>
|
|
47
|
+
<div className='seam-content'>
|
|
48
|
+
{formatDate(accessCode.created_at)}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className='seam-row seam-schedule'>
|
|
53
|
+
<div className='seam-heading'>{t.timing}:</div>
|
|
54
|
+
<div className='seam-content'>
|
|
55
|
+
<ScheduleInfo accessCode={accessCode} />
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ScheduleInfo({ accessCode }: { accessCode: AccessCode }) {
|
|
64
|
+
if (accessCode.type === 'ongoing') {
|
|
65
|
+
return <span>{t.ongoing}</span>
|
|
66
|
+
}
|
|
67
|
+
return (
|
|
68
|
+
<div className='seam-times'>
|
|
69
|
+
<div>
|
|
70
|
+
<div className='seam-label'>{t.start}</div>
|
|
71
|
+
<div className='seam-date'>{formatDate(accessCode.starts_at)}</div>
|
|
72
|
+
<div className='seam-time'>{formatTime(accessCode.starts_at)}</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div>
|
|
75
|
+
<div className='seam-label'>{t.end}</div>
|
|
76
|
+
<div className='seam-date'>{formatDate(accessCode.ends_at)}</div>
|
|
77
|
+
<div className='seam-time'>{formatTime(accessCode.ends_at)}</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function Duration(props: { accessCode: AccessCode }): JSX.Element {
|
|
84
|
+
const { accessCode } = props
|
|
85
|
+
if (accessCode.type === 'ongoing') {
|
|
86
|
+
return (
|
|
87
|
+
<span>
|
|
88
|
+
<span className='seam-label'>Active</span> (ongoing)
|
|
89
|
+
</span>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const hasStarted = new Date(accessCode.starts_at).valueOf() < Date.now()
|
|
94
|
+
if (hasStarted) {
|
|
95
|
+
return (
|
|
96
|
+
<span>
|
|
97
|
+
<span className='seam-label'>Active</span> until{' '}
|
|
98
|
+
{formatDurationDate(accessCode.ends_at)} at{' '}
|
|
99
|
+
{formatTime(accessCode.ends_at)}
|
|
100
|
+
</span>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<span>
|
|
106
|
+
Starts {formatDurationDate(accessCode.starts_at)} as{' '}
|
|
107
|
+
{formatTime(accessCode.starts_at)}
|
|
108
|
+
</span>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function formatDurationDate(date: string): string {
|
|
113
|
+
return DateTime.fromISO(date).toLocaleString({
|
|
114
|
+
month: 'short',
|
|
115
|
+
day: 'numeric',
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function formatTime(date: string): string {
|
|
120
|
+
return DateTime.fromISO(date).toLocaleString({
|
|
121
|
+
hour: 'numeric',
|
|
122
|
+
minute: '2-digit',
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatDate(date: string): string {
|
|
127
|
+
return DateTime.fromISO(date).toLocaleString({
|
|
128
|
+
weekday: 'short',
|
|
129
|
+
month: 'long',
|
|
130
|
+
day: 'numeric',
|
|
131
|
+
year: 'numeric',
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const t = {
|
|
136
|
+
accessCode: 'Access code',
|
|
137
|
+
fallbackName: 'Code',
|
|
138
|
+
id: 'ID',
|
|
139
|
+
created: 'Created',
|
|
140
|
+
timing: 'Timing',
|
|
141
|
+
ongoing: 'Ongoing',
|
|
142
|
+
start: 'Start',
|
|
143
|
+
end: 'End',
|
|
144
|
+
}
|