@seamapi/react 1.41.0 → 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.
Files changed (82) hide show
  1. package/README.md +1 -1
  2. package/dist/elements.js +5008 -4833
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +10 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.min.css +1 -1
  7. package/dist/index.min.css.map +1 -1
  8. package/lib/dates.d.ts +8 -0
  9. package/lib/dates.js +8 -0
  10. package/lib/dates.js.map +1 -1
  11. package/lib/seam/access-codes/use-update-access-code.d.ts +6 -0
  12. package/lib/seam/access-codes/use-update-access-code.js +22 -0
  13. package/lib/seam/access-codes/use-update-access-code.js.map +1 -0
  14. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.d.ts +2 -1
  15. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js +5 -2
  16. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
  17. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +34 -18
  18. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
  19. package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.d.ts +7 -0
  20. package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js +49 -0
  21. package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js.map +1 -0
  22. package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.d.ts +7 -0
  23. package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js +60 -0
  24. package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js.map +1 -0
  25. package/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.js +1 -1
  26. package/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.js.map +1 -1
  27. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.d.ts +1 -1
  28. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.js +1 -5
  29. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.js.map +1 -1
  30. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.d.ts +1 -1
  31. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.js +25 -16
  32. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.js.map +1 -1
  33. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.d.ts +1 -1
  34. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.js +2 -3
  35. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.js.map +1 -1
  36. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.d.ts +1 -2
  37. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.js +7 -8
  38. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.js.map +1 -1
  39. package/lib/ui/AccessCodeForm/AccessCodeForm.d.ts +21 -0
  40. package/lib/ui/AccessCodeForm/AccessCodeForm.js +97 -0
  41. package/lib/ui/AccessCodeForm/AccessCodeForm.js.map +1 -0
  42. package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.js +6 -6
  43. package/lib/ui/AccessCodeForm/AccessCodeFormDatePicker.js.map +1 -0
  44. package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.js +3 -3
  45. package/lib/ui/AccessCodeForm/AccessCodeFormTimes.js.map +1 -0
  46. package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.js +4 -4
  47. package/lib/ui/AccessCodeForm/AccessCodeFormTimezonePicker.js.map +1 -0
  48. package/lib/version.d.ts +1 -1
  49. package/lib/version.js +1 -1
  50. package/package.json +3 -3
  51. package/src/lib/dates.ts +10 -0
  52. package/src/lib/seam/access-codes/use-update-access-code.ts +46 -0
  53. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.element.ts +1 -0
  54. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +14 -1
  55. package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +62 -13
  56. package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.element.ts +12 -0
  57. package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.tsx +94 -0
  58. package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.element.ts +12 -0
  59. package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.tsx +117 -0
  60. package/src/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.tsx +1 -1
  61. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.tsx +3 -7
  62. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.tsx +29 -30
  63. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.tsx +3 -4
  64. package/src/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.ts +10 -14
  65. package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeForm.tsx +86 -84
  66. package/src/lib/version.ts +1 -1
  67. package/src/styles/_access-code-details.scss +5 -0
  68. package/src/styles/_access-code-table.scss +1 -1
  69. package/src/styles/_menus.scss +6 -0
  70. package/lib/seam/components/AccessCodeForm/AccessCodeForm.d.ts +0 -7
  71. package/lib/seam/components/AccessCodeForm/AccessCodeForm.js +0 -106
  72. package/lib/seam/components/AccessCodeForm/AccessCodeForm.js.map +0 -1
  73. package/lib/seam/components/AccessCodeForm/AccessCodeFormDatePicker.js.map +0 -1
  74. package/lib/seam/components/AccessCodeForm/AccessCodeFormTimes.js.map +0 -1
  75. package/lib/seam/components/AccessCodeForm/AccessCodeFormTimezonePicker.js.map +0 -1
  76. package/src/lib/seam/components/AccessCodeForm/AccessCodeForm.element.ts +0 -12
  77. /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.d.ts +0 -0
  78. /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.d.ts +0 -0
  79. /package/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.d.ts +0 -0
  80. /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormDatePicker.tsx +0 -0
  81. /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimes.tsx +0 -0
  82. /package/src/lib/{seam/components → ui}/AccessCodeForm/AccessCodeFormTimezonePicker.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamapi/react",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "description": "Seam Components.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -122,7 +122,7 @@
122
122
  "@tanstack/react-query": "^4.28.0",
123
123
  "classnames": "^2.3.2",
124
124
  "luxon": "^3.3.0",
125
- "seamapi": "^8.1.0",
125
+ "seamapi": "^8.2.4",
126
126
  "uuid": "^9.0.0"
127
127
  },
128
128
  "devDependencies": {
@@ -133,7 +133,7 @@
133
133
  "@mui/icons-material": "^5.11.16",
134
134
  "@mui/material": "^5.12.2",
135
135
  "@rxfork/r2wc-react-to-web-component": "^2.2.3",
136
- "@seamapi/fake-seam-connect": "^1.0.0",
136
+ "@seamapi/fake-seam-connect": "^1.3.1",
137
137
  "@storybook/addon-designs": "^7.0.1",
138
138
  "@storybook/addon-essentials": "^7.0.2",
139
139
  "@storybook/addon-links": "^7.0.2",
package/src/lib/dates.ts CHANGED
@@ -128,3 +128,13 @@ export const createIsoDate = (date: string, timezone: string): string => {
128
128
  const offset = getTimezoneOffset(timezone)
129
129
  return `${date}.000${offset}`
130
130
  }
131
+
132
+ /**
133
+ * Takes a ISO datetime string (2023-07-20T00:00:00.000-07:00) and returns
134
+ * the IANA timezone (America/Los_angeles).
135
+ *
136
+ * @param date
137
+ * @returns string
138
+ */
139
+ export const getTimezoneFromIsoDate = (date: string): string | null =>
140
+ DateTime.fromISO(date).zoneName
@@ -0,0 +1,46 @@
1
+ import {
2
+ useMutation,
3
+ type UseMutationResult,
4
+ useQueryClient,
5
+ } from '@tanstack/react-query'
6
+ import type {
7
+ AccessCodeUpdateRequest,
8
+ AccessCodeUpdateResponse,
9
+ ActionAttempt,
10
+ SeamError,
11
+ } from 'seamapi'
12
+
13
+ import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
14
+
15
+ export type UseUpdateAccessCodeParams = never
16
+ export type UseUpdateAccessCodeData = ActionAttempt<'UPDATE_ACCESS_CODE'>
17
+ export type UseUpdateAccessCodeMutationParams = AccessCodeUpdateRequest
18
+
19
+ export function useUpdateAccessCode(): UseMutationResult<
20
+ UseUpdateAccessCodeData,
21
+ SeamError,
22
+ UseUpdateAccessCodeMutationParams
23
+ > {
24
+ const { client } = useSeamClient()
25
+ const queryClient = useQueryClient()
26
+
27
+ return useMutation<
28
+ AccessCodeUpdateResponse['action_attempt'],
29
+ SeamError,
30
+ AccessCodeUpdateRequest
31
+ >({
32
+ mutationFn: async (mutationParams: UseUpdateAccessCodeMutationParams) => {
33
+ if (client === null) throw new NullSeamClientError()
34
+
35
+ return await client.accessCodes.update(mutationParams)
36
+ },
37
+ onSuccess: (_data, variables) => {
38
+ void queryClient.invalidateQueries([
39
+ 'access_codes',
40
+ 'get',
41
+ { access_code_id: variables.access_code_id },
42
+ ])
43
+ void queryClient.invalidateQueries(['access_codes', 'list'])
44
+ },
45
+ })
46
+ }
@@ -8,6 +8,7 @@ export const props: ElementProps<AccessCodeDetailsProps> = {
8
8
  accessCodeId: 'string',
9
9
  disableLockUnlock: 'boolean',
10
10
  onBack: 'object',
11
+ onEdit: 'object',
11
12
  className: 'string',
12
13
  }
13
14
 
@@ -12,20 +12,25 @@ import { useAccessCode } from 'lib/seam/access-codes/use-access-code.js'
12
12
  import { AccessCodeDevice } from 'lib/seam/components/AccessCodeDetails/AccessCodeDevice.js'
13
13
  import { DeviceDetails } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
14
14
  import { Alerts } from 'lib/ui/Alert/Alerts.js'
15
+ import { Button } from 'lib/ui/Button.js'
15
16
  import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
16
17
  import { useIsDateInPast } from 'lib/ui/use-is-date-in-past.js'
17
18
 
19
+ const disableEditAccessCode = false
20
+
18
21
  export interface AccessCodeDetailsProps {
19
22
  accessCodeId: string
20
23
  disableLockUnlock?: boolean
21
24
  onBack?: () => void
25
+ onEdit: () => void
22
26
  className?: string
23
27
  }
24
28
 
25
29
  export function AccessCodeDetails({
26
30
  accessCodeId,
27
- onBack,
28
31
  disableLockUnlock = false,
32
+ onBack,
33
+ onEdit,
29
34
  className,
30
35
  }: AccessCodeDetailsProps): JSX.Element | null {
31
36
  const { accessCode } = useAccessCode(accessCodeId)
@@ -87,6 +92,13 @@ export function AccessCodeDetails({
87
92
  onSelectDevice={selectDevice}
88
93
  />
89
94
  </div>
95
+ {!disableEditAccessCode && (
96
+ <div className='seam-actions'>
97
+ <Button size='small' onClick={onEdit}>
98
+ {t.editCode}
99
+ </Button>
100
+ </div>
101
+ )}
90
102
  <div className='seam-details'>
91
103
  <div className='seam-row'>
92
104
  <div className='seam-heading'>{t.id}:</div>
@@ -211,4 +223,5 @@ const t = {
211
223
  ongoing: 'Ongoing',
212
224
  start: 'Start',
213
225
  end: 'End',
226
+ editCode: 'Edit code',
214
227
  }
@@ -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 [selectedAccessCodeId, setSelectedAccessCodeId] = useState<
96
+ const [selectedViewAccessCodeId, setSelectedViewAccessCodeId] = useState<
95
97
  string | null
96
98
  >(null)
97
99
 
98
100
  const [searchInputValue, setSearchInputValue] = useState('')
99
- const [accessCodeFormVisible, toggleAddAccessCodeForm] = useToggle()
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
- setSelectedAccessCodeId(accessCodeId)
118
+ setSelectedViewAccessCodeId(accessCodeId)
114
119
  },
115
120
  [
116
121
  onAccessCodeClick,
117
122
  preventDefaultOnAccessCodeClick,
118
- setSelectedAccessCodeId,
123
+ setSelectedViewAccessCodeId,
119
124
  ]
120
125
  )
121
126
 
122
- if (selectedAccessCodeId != null) {
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={selectedAccessCodeId}
150
+ accessCodeId={selectedViewAccessCodeId}
127
151
  onBack={() => {
128
- setSelectedAccessCodeId(null)
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 (accessCodeFormVisible) {
162
+ if (addAccessCodeFormVisible) {
136
163
  return (
137
- <AccessCodeForm
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 { accessCodes, onAccessCodeClick } = props
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
+ }
@@ -36,7 +36,7 @@ export function FilterCategoryMenu({
36
36
  onAllOptionSelect,
37
37
  buttonLabel,
38
38
  }: FilterCategoryMenuProps): JSX.Element {
39
- const usableOptions = hideAllOption ? options : ['All', ...options]
39
+ const usableOptions = hideAllOption ? options : [allLabel, ...options]
40
40
 
41
41
  return (
42
42
  <div className='seam-supported-device-table-filter-menu-wrap'>
@@ -13,7 +13,7 @@ interface SupportedDeviceContentProps {
13
13
  filterValue: string
14
14
  resetFilterValue: () => void
15
15
  filters: DeviceModelFilters
16
- brands: string[]
16
+ brands: string[] | null
17
17
  }
18
18
 
19
19
  export function SupportedDeviceContent({
@@ -71,12 +71,8 @@ export function SupportedDeviceContent({
71
71
  )
72
72
  }
73
73
 
74
- // If there are no active filters or search, show all the rows without any brand sections.
75
- const hasFilters =
76
- filterValue.trim() !== '' ||
77
- filters.supportedOnly ||
78
- filters.category !== null ||
79
- filters.brand !== null
74
+ const hasFilters = filterValue.trim() !== '' || filters.brand !== null
75
+
80
76
  if (hasFilters) {
81
77
  return (
82
78
  <div className='seam-supported-device-table-content'>