@seamapi/react 1.41.1 → 1.42.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 +1 -1
- package/dist/elements.js +4970 -4793
- package/dist/elements.js.map +1 -1
- package/dist/index.css +10 -1
- package/dist/index.css.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.css.map +1 -1
- package/lib/dates.d.ts +8 -0
- package/lib/dates.js +8 -0
- package/lib/dates.js.map +1 -1
- package/lib/seam/access-codes/use-update-access-code.d.ts +6 -0
- package/lib/seam/access-codes/use-update-access-code.js +22 -0
- package/lib/seam/access-codes/use-update-access-code.js.map +1 -0
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.d.ts +2 -1
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js +5 -2
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +34 -18
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.d.ts +7 -0
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js +49 -0
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js.map +1 -0
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.d.ts +7 -0
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js +60 -0
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js.map +1 -0
- package/lib/ui/AccessCodeForm/AccessCodeForm.d.ts +21 -0
- package/lib/ui/AccessCodeForm/AccessCodeForm.js +97 -0
- package/lib/ui/AccessCodeForm/AccessCodeForm.js.map +1 -0
- package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.js +6 -6
- package/lib/ui/AccessCodeForm/AccessCodeFormDatePicker.js.map +1 -0
- package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.js +3 -3
- package/lib/ui/AccessCodeForm/AccessCodeFormTimes.js.map +1 -0
- package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.js +4 -4
- package/lib/ui/AccessCodeForm/AccessCodeFormTimezonePicker.js.map +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -3
- package/src/lib/dates.ts +10 -0
- package/src/lib/seam/access-codes/use-update-access-code.ts +46 -0
- package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.element.ts +1 -0
- package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +14 -1
- package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +62 -13
- package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.element.ts +12 -0
- package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.tsx +94 -0
- package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.element.ts +12 -0
- package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.tsx +117 -0
- package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeForm.tsx +86 -84
- package/src/lib/version.ts +1 -1
- package/src/styles/_access-code-details.scss +5 -0
- package/src/styles/_access-code-table.scss +1 -1
- package/src/styles/_menus.scss +6 -0
- package/lib/seam/components/AccessCodeForm/AccessCodeForm.d.ts +0 -7
- package/lib/seam/components/AccessCodeForm/AccessCodeForm.js +0 -106
- package/lib/seam/components/AccessCodeForm/AccessCodeForm.js.map +0 -1
- package/lib/seam/components/AccessCodeForm/AccessCodeFormDatePicker.js.map +0 -1
- package/lib/seam/components/AccessCodeForm/AccessCodeFormTimes.js.map +0 -1
- package/lib/seam/components/AccessCodeForm/AccessCodeFormTimezonePicker.js.map +0 -1
- package/src/lib/seam/components/AccessCodeForm/AccessCodeForm.element.ts +0 -12
- /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.d.ts +0 -0
- /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.d.ts +0 -0
- /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.d.ts +0 -0
- /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.tsx +0 -0
- /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.tsx +0 -0
- /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.tsx +0 -0
|
@@ -12,12 +12,13 @@ import {
|
|
|
12
12
|
type UseAccessCodesData,
|
|
13
13
|
} from 'lib/seam/access-codes/use-access-codes.js'
|
|
14
14
|
import { AccessCodeDetails } from 'lib/seam/components/AccessCodeDetails/AccessCodeDetails.js'
|
|
15
|
-
import { AccessCodeForm } from 'lib/seam/components/AccessCodeForm/AccessCodeForm.js'
|
|
16
15
|
import {
|
|
17
16
|
type AccessCodeFilter,
|
|
18
17
|
AccessCodeHealthBar,
|
|
19
18
|
} from 'lib/seam/components/AccessCodeTable/AccessCodeHealthBar.js'
|
|
20
19
|
import { CodeDetails } from 'lib/seam/components/AccessCodeTable/CodeDetails.js'
|
|
20
|
+
import { CreateAccessCodeForm } from 'lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js'
|
|
21
|
+
import { EditAccessCodeForm } from 'lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js'
|
|
21
22
|
import { copyToClipboard } from 'lib/ui/clipboard.js'
|
|
22
23
|
import { IconButton } from 'lib/ui/IconButton.js'
|
|
23
24
|
import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
|
|
@@ -35,6 +36,7 @@ import { Title } from 'lib/ui/typography/Title.js'
|
|
|
35
36
|
import { useToggle } from 'lib/ui/use-toggle.js'
|
|
36
37
|
|
|
37
38
|
const disableCreateAccessCode = true
|
|
39
|
+
const disableEditAccessCode = false
|
|
38
40
|
|
|
39
41
|
export interface AccessCodeTableProps {
|
|
40
42
|
deviceId: string
|
|
@@ -91,12 +93,15 @@ export function AccessCodeTable({
|
|
|
91
93
|
device_id: deviceId,
|
|
92
94
|
})
|
|
93
95
|
|
|
94
|
-
const [
|
|
96
|
+
const [selectedViewAccessCodeId, setSelectedViewAccessCodeId] = useState<
|
|
95
97
|
string | null
|
|
96
98
|
>(null)
|
|
97
99
|
|
|
98
100
|
const [searchInputValue, setSearchInputValue] = useState('')
|
|
99
|
-
const [
|
|
101
|
+
const [addAccessCodeFormVisible, toggleAddAccessCodeForm] = useToggle()
|
|
102
|
+
const [selectedEditAccessCodeId, setSelectedEditAccessCodeId] = useState<
|
|
103
|
+
string | null
|
|
104
|
+
>(null)
|
|
100
105
|
|
|
101
106
|
const filteredAccessCodes = useMemo(
|
|
102
107
|
() =>
|
|
@@ -110,31 +115,53 @@ export function AccessCodeTable({
|
|
|
110
115
|
(accessCodeId: string): void => {
|
|
111
116
|
onAccessCodeClick(accessCodeId)
|
|
112
117
|
if (preventDefaultOnAccessCodeClick) return
|
|
113
|
-
|
|
118
|
+
setSelectedViewAccessCodeId(accessCodeId)
|
|
114
119
|
},
|
|
115
120
|
[
|
|
116
121
|
onAccessCodeClick,
|
|
117
122
|
preventDefaultOnAccessCodeClick,
|
|
118
|
-
|
|
123
|
+
setSelectedViewAccessCodeId,
|
|
119
124
|
]
|
|
120
125
|
)
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
const handleAccessCodeEdit = useCallback(
|
|
128
|
+
(accessCodeId: string): void => {
|
|
129
|
+
setSelectedEditAccessCodeId(accessCodeId)
|
|
130
|
+
},
|
|
131
|
+
[setSelectedEditAccessCodeId]
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if (selectedEditAccessCodeId != null) {
|
|
135
|
+
return (
|
|
136
|
+
<EditAccessCodeForm
|
|
137
|
+
accessCodeId={selectedEditAccessCodeId}
|
|
138
|
+
className={className}
|
|
139
|
+
onBack={() => {
|
|
140
|
+
setSelectedEditAccessCodeId(null)
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (selectedViewAccessCodeId != null) {
|
|
123
147
|
return (
|
|
124
148
|
<AccessCodeDetails
|
|
125
149
|
className={className}
|
|
126
|
-
accessCodeId={
|
|
150
|
+
accessCodeId={selectedViewAccessCodeId}
|
|
127
151
|
onBack={() => {
|
|
128
|
-
|
|
152
|
+
setSelectedViewAccessCodeId(null)
|
|
129
153
|
}}
|
|
130
154
|
disableLockUnlock={disableLockUnlock}
|
|
155
|
+
onEdit={() => {
|
|
156
|
+
setSelectedEditAccessCodeId(selectedViewAccessCodeId)
|
|
157
|
+
}}
|
|
131
158
|
/>
|
|
132
159
|
)
|
|
133
160
|
}
|
|
134
161
|
|
|
135
|
-
if (
|
|
162
|
+
if (addAccessCodeFormVisible) {
|
|
136
163
|
return (
|
|
137
|
-
<
|
|
164
|
+
<CreateAccessCodeForm
|
|
138
165
|
className={className}
|
|
139
166
|
onBack={toggleAddAccessCodeForm}
|
|
140
167
|
deviceId={deviceId}
|
|
@@ -176,6 +203,8 @@ export function AccessCodeTable({
|
|
|
176
203
|
<Content
|
|
177
204
|
accessCodes={filteredAccessCodes}
|
|
178
205
|
onAccessCodeClick={handleAccessCodeClick}
|
|
206
|
+
onAccessCodeEdit={handleAccessCodeEdit}
|
|
207
|
+
disableEditAccessCode={disableEditAccessCode}
|
|
179
208
|
/>
|
|
180
209
|
</TableBody>
|
|
181
210
|
</div>
|
|
@@ -185,8 +214,15 @@ export function AccessCodeTable({
|
|
|
185
214
|
function Content(props: {
|
|
186
215
|
accessCodes: Array<UseAccessCodesData[number]>
|
|
187
216
|
onAccessCodeClick: (accessCodeId: string) => void
|
|
217
|
+
onAccessCodeEdit: (accessCodeId: string) => void
|
|
218
|
+
disableEditAccessCode: boolean
|
|
188
219
|
}): JSX.Element {
|
|
189
|
-
const {
|
|
220
|
+
const {
|
|
221
|
+
accessCodes,
|
|
222
|
+
onAccessCodeClick,
|
|
223
|
+
onAccessCodeEdit,
|
|
224
|
+
disableEditAccessCode,
|
|
225
|
+
} = props
|
|
190
226
|
const [filter, setFilter] = useState<AccessCodeFilter | null>(null)
|
|
191
227
|
|
|
192
228
|
const filteredAccessCodes = useMemo(() => {
|
|
@@ -221,6 +257,10 @@ function Content(props: {
|
|
|
221
257
|
onClick={() => {
|
|
222
258
|
onAccessCodeClick(accessCode.access_code_id)
|
|
223
259
|
}}
|
|
260
|
+
disableEditAccessCode={disableEditAccessCode}
|
|
261
|
+
onEdit={() => {
|
|
262
|
+
onAccessCodeEdit(accessCode.access_code_id)
|
|
263
|
+
}}
|
|
224
264
|
/>
|
|
225
265
|
))}
|
|
226
266
|
</>
|
|
@@ -230,8 +270,10 @@ function Content(props: {
|
|
|
230
270
|
function AccessCodeRow(props: {
|
|
231
271
|
accessCode: UseAccessCodesData[number]
|
|
232
272
|
onClick: () => void
|
|
273
|
+
onEdit: () => void
|
|
274
|
+
disableEditAccessCode: boolean
|
|
233
275
|
}): JSX.Element {
|
|
234
|
-
const { onClick, accessCode } = props
|
|
276
|
+
const { onClick, accessCode, onEdit, disableEditAccessCode } = props
|
|
235
277
|
|
|
236
278
|
const errorCount = accessCode.errors.length
|
|
237
279
|
const warningCount = accessCode.warnings.length
|
|
@@ -277,13 +319,19 @@ function AccessCodeRow(props: {
|
|
|
277
319
|
void copyToClipboard(accessCode.code ?? '')
|
|
278
320
|
}}
|
|
279
321
|
>
|
|
280
|
-
<div className='menu-item-copy'>
|
|
322
|
+
<div className='seam-menu-item-copy'>
|
|
281
323
|
<span>
|
|
282
324
|
{t.copyCode} - {accessCode.code}
|
|
283
325
|
</span>
|
|
284
326
|
<CopyIcon />
|
|
285
327
|
</div>
|
|
286
328
|
</MenuItem>
|
|
329
|
+
{!disableEditAccessCode && (
|
|
330
|
+
<>
|
|
331
|
+
<div className='seam-divider' />
|
|
332
|
+
<MenuItem onClick={onEdit}>{t.editCode}</MenuItem>
|
|
333
|
+
</>
|
|
334
|
+
)}
|
|
287
335
|
</MoreActionsMenu>
|
|
288
336
|
</TableCell>
|
|
289
337
|
</TableRow>
|
|
@@ -296,4 +344,5 @@ const t = {
|
|
|
296
344
|
noAccessCodesMessage: 'Sorry, no access codes were found',
|
|
297
345
|
codeIssue: 'code issue',
|
|
298
346
|
codeIssues: 'code issues',
|
|
347
|
+
editCode: 'Edit code',
|
|
299
348
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ElementProps } from 'lib/element.js'
|
|
2
|
+
import type { CreateAccessCodeFormProps } from 'lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js'
|
|
3
|
+
|
|
4
|
+
export const name = 'seam-create-access-code-form'
|
|
5
|
+
|
|
6
|
+
export const props: ElementProps<CreateAccessCodeFormProps> = {
|
|
7
|
+
className: 'string',
|
|
8
|
+
onBack: 'object',
|
|
9
|
+
deviceId: 'string',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { CreateAccessCodeForm as Component } from './CreateAccessCodeForm.js'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createIsoDate } from 'lib/dates.js'
|
|
2
|
+
import { useCreateAccessCode } from 'lib/seam/access-codes/use-create-access-code.js'
|
|
3
|
+
import { useDevice, type UseDeviceData } from 'lib/seam/devices/use-device.js'
|
|
4
|
+
import {
|
|
5
|
+
AccessCodeForm,
|
|
6
|
+
type AccessCodeFormSubmitData,
|
|
7
|
+
} from 'lib/ui/AccessCodeForm/AccessCodeForm.js'
|
|
8
|
+
|
|
9
|
+
export interface CreateAccessCodeFormProps {
|
|
10
|
+
className?: string
|
|
11
|
+
onBack?: () => void
|
|
12
|
+
deviceId: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function CreateAccessCodeForm({
|
|
16
|
+
className,
|
|
17
|
+
onBack,
|
|
18
|
+
deviceId,
|
|
19
|
+
}: CreateAccessCodeFormProps): JSX.Element | null {
|
|
20
|
+
const { device } = useDevice({
|
|
21
|
+
device_id: deviceId,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (device == null) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return <Content device={device} className={className} onBack={onBack} />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Content({
|
|
32
|
+
device,
|
|
33
|
+
className,
|
|
34
|
+
onBack,
|
|
35
|
+
}: Omit<CreateAccessCodeFormProps, 'deviceId'> & {
|
|
36
|
+
device: NonNullable<UseDeviceData>
|
|
37
|
+
}): JSX.Element {
|
|
38
|
+
const { submit, isSubmitting } = useSubmitCreateAccessCode(onBack)
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<AccessCodeForm
|
|
42
|
+
device={device}
|
|
43
|
+
className={className}
|
|
44
|
+
onBack={onBack}
|
|
45
|
+
onSubmit={submit}
|
|
46
|
+
isSubmitting={isSubmitting}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function useSubmitCreateAccessCode(onSuccess?: () => void): {
|
|
52
|
+
submit: (data: AccessCodeFormSubmitData) => void
|
|
53
|
+
isSubmitting: boolean
|
|
54
|
+
} {
|
|
55
|
+
const { mutate, isLoading: isSubmitting } = useCreateAccessCode()
|
|
56
|
+
const submit = (data: AccessCodeFormSubmitData): void => {
|
|
57
|
+
const { name, type, device, startDate, endDate, timezone } = data
|
|
58
|
+
if (name === '') {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (isSubmitting) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (type === 'time_bound') {
|
|
67
|
+
mutate(
|
|
68
|
+
{
|
|
69
|
+
name,
|
|
70
|
+
device_id: device.device_id,
|
|
71
|
+
starts_at: createIsoDate(startDate, timezone),
|
|
72
|
+
ends_at: createIsoDate(endDate, timezone),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
onSuccess,
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
mutate(
|
|
83
|
+
{
|
|
84
|
+
name,
|
|
85
|
+
device_id: device.device_id,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
onSuccess,
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { submit, isSubmitting }
|
|
94
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ElementProps } from 'lib/element.js'
|
|
2
|
+
import type { EditAccessCodeFormProps } from 'lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js'
|
|
3
|
+
|
|
4
|
+
export const name = 'seam-create-access-code-form'
|
|
5
|
+
|
|
6
|
+
export const props: ElementProps<EditAccessCodeFormProps> = {
|
|
7
|
+
className: 'string',
|
|
8
|
+
onBack: 'object',
|
|
9
|
+
accessCodeId: 'string',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { EditAccessCodeForm as Component } from './EditAccessCodeForm.js'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createIsoDate } from 'lib/dates.js'
|
|
2
|
+
import {
|
|
3
|
+
useAccessCode,
|
|
4
|
+
type UseAccessCodeData,
|
|
5
|
+
} from 'lib/seam/access-codes/use-access-code.js'
|
|
6
|
+
import { useUpdateAccessCode } from 'lib/seam/access-codes/use-update-access-code.js'
|
|
7
|
+
import { useDevice } from 'lib/seam/devices/use-device.js'
|
|
8
|
+
import {
|
|
9
|
+
AccessCodeForm,
|
|
10
|
+
type AccessCodeFormSubmitData,
|
|
11
|
+
} from 'lib/ui/AccessCodeForm/AccessCodeForm.js'
|
|
12
|
+
|
|
13
|
+
export interface EditAccessCodeFormProps {
|
|
14
|
+
className?: string
|
|
15
|
+
onBack?: () => void
|
|
16
|
+
accessCodeId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function EditAccessCodeForm({
|
|
20
|
+
accessCodeId,
|
|
21
|
+
onBack,
|
|
22
|
+
className,
|
|
23
|
+
}: EditAccessCodeFormProps): JSX.Element | null {
|
|
24
|
+
const { accessCode } = useAccessCode({
|
|
25
|
+
access_code_id: accessCodeId,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (accessCode == null) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Content accessCode={accessCode} className={className} onBack={onBack} />
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Content({
|
|
38
|
+
className,
|
|
39
|
+
onBack,
|
|
40
|
+
accessCode,
|
|
41
|
+
}: Omit<EditAccessCodeFormProps, 'accessCodeId'> & {
|
|
42
|
+
accessCode: NonNullable<UseAccessCodeData>
|
|
43
|
+
}): JSX.Element | null {
|
|
44
|
+
const { device } = useDevice({
|
|
45
|
+
device_id: accessCode.device_id,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const { submit, isSubmitting } = useSubmitEditAccessCode(accessCode, onBack)
|
|
49
|
+
|
|
50
|
+
if (device == null) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<AccessCodeForm
|
|
56
|
+
onBack={onBack}
|
|
57
|
+
className={className}
|
|
58
|
+
accessCode={accessCode}
|
|
59
|
+
device={device}
|
|
60
|
+
onSubmit={submit}
|
|
61
|
+
isSubmitting={isSubmitting}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function useSubmitEditAccessCode(
|
|
67
|
+
accessCode: NonNullable<UseAccessCodeData>,
|
|
68
|
+
onSuccess?: () => void
|
|
69
|
+
): {
|
|
70
|
+
submit: (data: AccessCodeFormSubmitData) => void
|
|
71
|
+
isSubmitting: boolean
|
|
72
|
+
} {
|
|
73
|
+
const { mutate, isLoading: isSubmitting } = useUpdateAccessCode()
|
|
74
|
+
|
|
75
|
+
const submit = (data: AccessCodeFormSubmitData): void => {
|
|
76
|
+
const { name, type, device, startDate, endDate, timezone } = data
|
|
77
|
+
if (name === '') {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (isSubmitting) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (type === 'time_bound') {
|
|
86
|
+
mutate(
|
|
87
|
+
{
|
|
88
|
+
access_code_id: accessCode.access_code_id,
|
|
89
|
+
name,
|
|
90
|
+
device_id: device.device_id,
|
|
91
|
+
type: 'time_bound',
|
|
92
|
+
starts_at: createIsoDate(startDate, timezone),
|
|
93
|
+
ends_at: createIsoDate(endDate, timezone),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
onSuccess,
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
mutate(
|
|
104
|
+
{
|
|
105
|
+
access_code_id: accessCode.access_code_id,
|
|
106
|
+
name,
|
|
107
|
+
type: 'ongoing',
|
|
108
|
+
device_id: device.device_id,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
onSuccess,
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { submit, isSubmitting }
|
|
117
|
+
}
|
|
@@ -3,16 +3,16 @@ import { useState } from 'react'
|
|
|
3
3
|
import type { AccessCode } from 'seamapi'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
createIsoDate,
|
|
7
6
|
get24HoursLater,
|
|
8
7
|
getBrowserTimezone,
|
|
9
8
|
getNow,
|
|
9
|
+
getTimezoneFromIsoDate,
|
|
10
10
|
} from 'lib/dates.js'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import type { UseAccessCodeData } from 'lib/seam/access-codes/use-access-code.js'
|
|
12
|
+
import type { UseDeviceData } from 'lib/seam/devices/use-device.js'
|
|
13
|
+
import { AccessCodeFormDatePicker } from 'lib/ui/AccessCodeForm/AccessCodeFormDatePicker.js'
|
|
14
|
+
import { AccessCodeFormTimes } from 'lib/ui/AccessCodeForm/AccessCodeFormTimes.js'
|
|
15
|
+
import { AccessCodeFormTimezonePicker } from 'lib/ui/AccessCodeForm/AccessCodeFormTimezonePicker.js'
|
|
16
16
|
import { Button } from 'lib/ui/Button.js'
|
|
17
17
|
import { FormField } from 'lib/ui/FormField.js'
|
|
18
18
|
import { InputLabel } from 'lib/ui/InputLabel.js'
|
|
@@ -21,55 +21,72 @@ import { RadioField } from 'lib/ui/RadioField/RadioField.js'
|
|
|
21
21
|
import { TextField } from 'lib/ui/TextField/TextField.js'
|
|
22
22
|
import { useToggle } from 'lib/ui/use-toggle.js'
|
|
23
23
|
|
|
24
|
+
export interface AccessCodeFormSubmitData {
|
|
25
|
+
name: string
|
|
26
|
+
type: AccessCode['type']
|
|
27
|
+
device: NonNullable<UseDeviceData>
|
|
28
|
+
startDate: string
|
|
29
|
+
endDate: string
|
|
30
|
+
timezone: string
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
export interface AccessCodeFormProps {
|
|
25
|
-
deviceId: string
|
|
26
|
-
onBack?: () => void
|
|
27
34
|
className?: string
|
|
35
|
+
onBack?: () => void
|
|
36
|
+
accessCode?: NonNullable<UseAccessCodeData>
|
|
37
|
+
device: NonNullable<UseDeviceData>
|
|
38
|
+
isSubmitting: boolean
|
|
39
|
+
onSubmit: (data: AccessCodeFormSubmitData) => void
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
export function AccessCodeForm({
|
|
31
|
-
deviceId,
|
|
32
|
-
onBack,
|
|
33
43
|
className,
|
|
44
|
+
...props
|
|
34
45
|
}: AccessCodeFormProps): JSX.Element | null {
|
|
35
|
-
const { device } = useDevice({
|
|
36
|
-
device_id: deviceId,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
if (device == null) {
|
|
40
|
-
return null
|
|
41
|
-
}
|
|
42
|
-
|
|
43
46
|
return (
|
|
44
47
|
<div className={classNames('seam-access-code-form', className)}>
|
|
45
|
-
<Content
|
|
48
|
+
<Content {...props} />
|
|
46
49
|
</div>
|
|
47
50
|
)
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
function Content({
|
|
51
54
|
onBack,
|
|
55
|
+
accessCode,
|
|
52
56
|
device,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}): JSX.Element {
|
|
56
|
-
const [name, setName] = useState('')
|
|
57
|
-
const [type, setType] = useState<AccessCode['type']>(
|
|
57
|
+
onSubmit,
|
|
58
|
+
isSubmitting,
|
|
59
|
+
}: Omit<AccessCodeFormProps, 'className'>): JSX.Element {
|
|
60
|
+
const [name, setName] = useState(accessCode?.name ?? '')
|
|
61
|
+
const [type, setType] = useState<AccessCode['type']>(
|
|
62
|
+
accessCode?.type ?? 'ongoing'
|
|
63
|
+
)
|
|
58
64
|
const [datePickerVisible, setDatePickerVisible] = useState(false)
|
|
59
|
-
const [timezone, setTimezone] = useState<string>(
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
const [timezone, setTimezone] = useState<string>(
|
|
66
|
+
getAccessCodeTimezone(accessCode) ?? getBrowserTimezone()
|
|
67
|
+
)
|
|
68
|
+
const [startDate, setStartDate] = useState<string>(
|
|
69
|
+
getAccessCodeDate('starts_at', accessCode) ?? getNow()
|
|
70
|
+
)
|
|
71
|
+
const [endDate, setEndDate] = useState<string>(
|
|
72
|
+
getAccessCodeDate('ends_at', accessCode) ?? get24HoursLater()
|
|
73
|
+
)
|
|
62
74
|
const [timezonePickerVisible, toggleTimezonePicker] = useToggle()
|
|
63
75
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
const submit = (): void => {
|
|
77
|
+
if (isSubmitting) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onSubmit({
|
|
82
|
+
name,
|
|
83
|
+
type,
|
|
84
|
+
device,
|
|
85
|
+
startDate,
|
|
86
|
+
endDate,
|
|
87
|
+
timezone,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
73
90
|
|
|
74
91
|
if (timezonePickerVisible) {
|
|
75
92
|
return (
|
|
@@ -100,12 +117,14 @@ function Content({
|
|
|
100
117
|
const nameError = name.length > 60 ? t.overCharacterLimitError : undefined
|
|
101
118
|
|
|
102
119
|
const isFormValid =
|
|
103
|
-
name.trim().length > 0 && nameError === undefined && !
|
|
120
|
+
name.trim().length > 0 && nameError === undefined && !isSubmitting
|
|
121
|
+
|
|
122
|
+
const title = accessCode == null ? t.addNewAccessCode : t.editAccessCode
|
|
104
123
|
|
|
105
124
|
return (
|
|
106
125
|
<>
|
|
107
126
|
<ContentHeader
|
|
108
|
-
title={
|
|
127
|
+
title={title}
|
|
109
128
|
subheading={device.properties.name}
|
|
110
129
|
onBack={onBack}
|
|
111
130
|
/>
|
|
@@ -161,62 +180,45 @@ function Content({
|
|
|
161
180
|
)
|
|
162
181
|
}
|
|
163
182
|
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
timezone: string
|
|
171
|
-
onSuccess?: () => void
|
|
172
|
-
}): {
|
|
173
|
-
submit: () => void
|
|
174
|
-
isLoading: boolean
|
|
175
|
-
} {
|
|
176
|
-
const { name, type, device, startDate, endDate, timezone, onSuccess } = params
|
|
177
|
-
|
|
178
|
-
const { mutate, isLoading } = useCreateAccessCode()
|
|
179
|
-
const submit = (): void => {
|
|
180
|
-
if (name === '') {
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
+
function getAccessCodeTimezone(
|
|
184
|
+
accessCode?: NonNullable<UseAccessCodeData>
|
|
185
|
+
): undefined | string {
|
|
186
|
+
if (accessCode == null) {
|
|
187
|
+
return undefined
|
|
188
|
+
}
|
|
183
189
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
190
|
+
if (accessCode.type === 'ongoing') {
|
|
191
|
+
return undefined
|
|
192
|
+
}
|
|
187
193
|
|
|
188
|
-
|
|
189
|
-
mutate(
|
|
190
|
-
{
|
|
191
|
-
name,
|
|
192
|
-
device_id: device.device_id,
|
|
193
|
-
starts_at: createIsoDate(startDate, timezone),
|
|
194
|
-
ends_at: createIsoDate(endDate, timezone),
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
onSuccess,
|
|
198
|
-
}
|
|
199
|
-
)
|
|
194
|
+
const date = accessCode.starts_at
|
|
200
195
|
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
const timezone = getTimezoneFromIsoDate(date)
|
|
197
|
+
if (timezone == null) {
|
|
198
|
+
return undefined
|
|
199
|
+
}
|
|
203
200
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
return timezone
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getAccessCodeDate(
|
|
205
|
+
date: 'starts_at' | 'ends_at',
|
|
206
|
+
accessCode?: NonNullable<UseAccessCodeData>
|
|
207
|
+
): string | undefined {
|
|
208
|
+
if (accessCode == null) {
|
|
209
|
+
return undefined
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (accessCode.type !== 'time_bound') {
|
|
213
|
+
return undefined
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
return
|
|
216
|
+
return accessCode[date]
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
const t = {
|
|
219
220
|
addNewAccessCode: 'Add new access code',
|
|
221
|
+
editAccessCode: 'Edit access code',
|
|
220
222
|
overCharacterLimitError: '60 characters max',
|
|
221
223
|
nameInputLabel: 'Name the new code',
|
|
222
224
|
cancel: 'Cancel',
|
package/src/lib/version.ts
CHANGED