@isoftdata/svelte-user-configuration 2.0.0 → 2.0.1

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.
@@ -1,289 +1,289 @@
1
- <script lang="ts">
2
- import type { i18n } from 'i18next'
3
- import type { Merge } from 'type-fest'
4
- import type { IconName, SiteLabel, HTMLDivAttributes } from './'
5
- import type { Snippet } from 'svelte'
6
-
7
- import { getContext } from 'svelte'
8
- import camelCase from 'just-camel-case'
9
- import Icon from '@isoftdata/svelte-icon'
10
- import Button from '@isoftdata/svelte-button'
11
- import Select from '@isoftdata/svelte-select'
12
- import { getEventValue } from '@isoftdata/browser-event'
13
- import { Table, Td, type Column } from '@isoftdata/svelte-table'
14
- import { translate as defaultTranslate } from '@isoftdata/utility-string'
15
- import { SvelteSet, SvelteMap } from 'svelte/reactivity'
16
-
17
- const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
18
-
19
- type Permission = {
20
- id: number
21
- displayName: string
22
- category: string
23
- codeName: string
24
- }
25
- type ComputedPermission = Merge<
26
- Permission,
27
- { value: PermissionValue; groupValue?: PermissionValue | undefined; computedValue?: PermissionValue }
28
- >
29
- type ComputedPermissionColumns = Array<Column<ComputedPermission>>
30
- type PermissionValue = keyof typeof permissionValueList
31
- type PermissionValueMap = Map<number, PermissionValue>
32
-
33
- interface Props extends HTMLDivAttributes {
34
- permissions: Array<Permission>
35
- siteLabel?: SiteLabel
36
- permissionValueChange?:
37
- | ((change: { value: PermissionValue; permissionIds: Array<number> }) => void | Promise<void>)
38
- | undefined
39
- icon?: IconName
40
- cardHeaderTitle?: string
41
- permissionValueMap: PermissionValueMap
42
- groupPermissionValueMap?: PermissionValueMap | undefined
43
- children?: Snippet
44
- }
45
-
46
- let {
47
- permissions,
48
- siteLabel = 'Site',
49
- permissionValueChange = undefined,
50
- icon = 'user-lock',
51
- cardHeaderTitle = translate('common:permissions', 'Permissions'),
52
- permissionValueMap = $bindable(new SvelteMap()),
53
- groupPermissionValueMap = undefined,
54
- children,
55
- ...rest
56
- }: Props = $props()
57
-
58
- let selectedPermissionIds: SvelteSet<number> = $state(new SvelteSet())
59
- let selectedPermissionValue: string | null = $state(null)
60
-
61
- const permissionColumns: ComputedPermissionColumns = getColumns(groupPermissionValueMap)
62
- const permissionValueList = {
63
- NONE: { label: translate('common:permissionLevel.none', 'None'), value: 'NONE', color: 'danger' },
64
- SITE: {
65
- label: translate(`common:permissionLevel.${siteLabel.toLowerCase()}`, siteLabel),
66
- value: 'SITE',
67
- color: 'primary',
68
- },
69
- GLOBAL: { label: translate('common:permissionLevel.global', 'Global'), value: 'GLOBAL', color: 'success' },
70
- }
71
-
72
- function getColumns(groupPermissionValueMap: PermissionValueMap | undefined): ComputedPermissionColumns {
73
- let columns: ComputedPermissionColumns = [
74
- {
75
- property: 'value',
76
- name: groupPermissionValueMap ? translate('common:user', 'User') : translate('common:permission', 'Permission'),
77
- width: '1rem',
78
- },
79
- { property: 'category', name: translate('common:category', 'Category') },
80
- { property: 'displayName', name: translate('common:name', 'Name') },
81
- ]
82
-
83
- if (groupPermissionValueMap) {
84
- columns.splice(
85
- 1,
86
- 0,
87
- {
88
- property: 'groupValue',
89
- name: translate('common:group', 'Group'),
90
- align: 'center',
91
- width: '1rem',
92
- minWidth: '25%',
93
- },
94
- {
95
- property: 'computedValue',
96
- name: translate('common:access', 'Access'),
97
- align: 'center',
98
- width: '1rem',
99
- minWidth: '25%',
100
- },
101
- )
102
- }
103
-
104
- return columns
105
- }
106
-
107
- function selectPermissions(permissionId: number) {
108
- selectedPermissionIds.has(permissionId)
109
- ? selectedPermissionIds.delete(permissionId)
110
- : selectedPermissionIds.add(permissionId)
111
- }
112
-
113
- function selectAndDeselectAllPermissions() {
114
- if (selectedPermissionIds.size === computedPermissions.length) {
115
- selectedPermissionIds.clear()
116
- selectedPermissionValue = null
117
- } else {
118
- selectedPermissionIds = new SvelteSet(permissions.map(permission => permission.id))
119
- }
120
- }
121
-
122
- const getButtonColor = (optionValue: string, permissionValue: PermissionValue) =>
123
- optionValue === permissionValue ? permissionValueList[optionValue].color : 'outline-secondary'
124
-
125
- const isValidPermissionValue = (value: string): value is PermissionValue =>
126
- Object.keys(permissionValueList).includes(value)
127
-
128
- async function updatePermissionValues(event: Event, permissionIds: SvelteSet<number>) {
129
- const newValue = getEventValue(event)
130
- if (isValidPermissionValue(newValue)) {
131
- const permissionIdsArray = Array.from(permissionIds)
132
- for (const id of permissionIdsArray) {
133
- permissionValueMap.set(id, newValue)
134
- }
135
- permissionValueMap = permissionValueMap
136
- await permissionValueChange?.({
137
- value: newValue,
138
- permissionIds: permissionIdsArray,
139
- })
140
- }
141
- }
142
-
143
- function getComputedPermissions(
144
- permissions: Array<Permission>,
145
- permissionValueMap: PermissionValueMap,
146
- groupPermissionValueMap: PermissionValueMap | undefined,
147
- ): Array<ComputedPermission> {
148
- return permissions.map(permission => {
149
- const value = permissionValueMap.get(permission.id) ?? 'NONE'
150
- const groupValue = groupPermissionValueMap?.get(permission.id)
151
-
152
- return {
153
- ...permission,
154
- displayName: translate(`permissions:${camelCase(permission.codeName)}.displayName`, permission.displayName),
155
- category: translate(`permissions:categories.${camelCase(permission.category)}`, permission.category),
156
- value,
157
- groupValue,
158
- computedValue:
159
- (value === 'NONE' && groupValue) || (value === 'SITE' && groupValue === 'GLOBAL') ? groupValue : value,
160
- }
161
- })
162
- }
163
-
164
- let computedPermissions = $derived.by(() =>
165
- getComputedPermissions(permissions, permissionValueMap, groupPermissionValueMap),
166
- )
167
-
168
- $inspect(computedPermissions)
169
- </script>
170
-
171
- <div
172
- class="card"
173
- {...rest}
174
- >
175
- <div class="card-header">
176
- <h5 class="mb-0">
177
- <Icon
178
- {icon}
179
- class="mr-1 me-1"
180
- />
181
- {cardHeaderTitle}
182
- </h5>
183
- </div>
184
- <div class="card-body">
185
- <Table
186
- class="mb-0"
187
- parentClass="mh-60vh overflow-y-auto"
188
- filterPlaceholder={translate('common:filterPermissions', 'Filter Permissions')}
189
- filterColumnClass="col-6 col-lg-4 align-self-end"
190
- rows={computedPermissions}
191
- columns={permissionColumns}
192
- responsive
193
- filterEnabled
194
- >
195
- {#snippet children({ row: permission })}
196
- <tr
197
- onclick={() => selectPermissions(permission.id)}
198
- class:table-primary={selectedPermissionIds.has(permission.id)}
199
- class="cursor-pointer"
200
- >
201
- <Td property="value">
202
- <div
203
- class="btn-group btn-group-toggle"
204
- data-toggle="buttons"
205
- >
206
- {#each Object.values(permissionValueList) as permissionValue}
207
- <label class="btn btn-sm btn-{getButtonColor(permissionValue.value, permission.value)} cursor-pointer">
208
- <input
209
- class="btn-check"
210
- type="radio"
211
- autocomplete="off"
212
- name={permission.value}
213
- value={permissionValue.value}
214
- onchange={e => updatePermissionValues(e, new SvelteSet([permission.id]))}
215
- />
216
- {permissionValue.label}
217
- </label>
218
- {/each}
219
- </div>
220
- </Td>
221
- {#if groupPermissionValueMap}
222
- <Td
223
- property="groupValue"
224
- class="px-4"
225
- >
226
- <span class="badge badge-pill bg-{permissionValueList[permission.groupValue ?? 'NONE'].color}"
227
- >{permissionValueList[permission.groupValue ?? 'NONE'].label}</span
228
- >
229
- </Td>
230
- <Td
231
- property="computedValue"
232
- class="px-4"
233
- >
234
- <span class="badge badge-pill bg-{permissionValueList[permission.computedValue ?? 'NONE'].color}"
235
- >{permissionValueList[permission.computedValue ?? 'NONE'].label}</span
236
- >
237
- </Td>
238
- {/if}
239
- <Td property="category">{permission.category}</Td>
240
- <Td property="displayName">{permission.displayName}</Td>
241
- </tr>
242
- {/snippet}
243
- </Table>
244
- {@render children?.()}
245
- </div>
246
- <div class="card-footer">
247
- <div class="d-flex justify-content-between align-items-end">
248
- <Select
249
- label="{translate('common:setSelectedTo', 'Set Selected To')}:"
250
- bind:value={selectedPermissionValue}
251
- showEmptyOption={selectedPermissionValue === null}
252
- emptyValue={null}
253
- emptyText={translate('common:selectValue', 'Select a Value')}
254
- labelParentClass="form-inline mr-2 me-2"
255
- labelClass="mr-2 me-2 p-0"
256
- disabled={selectedPermissionIds.size === 0}
257
- onchange={e => {
258
- updatePermissionValues(e, selectedPermissionIds)
259
- selectedPermissionIds.clear()
260
- selectedPermissionValue = null
261
- }}
262
- >
263
- {#each Object.values(permissionValueList) as permission}
264
- <option value={permission.value}>{permission.label}</option>
265
- {/each}
266
- </Select>
267
- <Button
268
- class="mb-1"
269
- outline
270
- size="sm"
271
- iconClass="check-double"
272
- onclick={selectAndDeselectAllPermissions}
273
- >
274
- <span class:d-none={selectedPermissionIds.size !== computedPermissions.length}
275
- >{translate('common:deselectAll', 'Deselect All')}</span
276
- >
277
- <span class:d-none={selectedPermissionIds.size === computedPermissions.length}
278
- >{translate('common:selectAll', 'Select All')}</span
279
- >
280
- </Button>
281
- </div>
282
- </div>
283
- </div>
284
-
285
- <style>
286
- .cursor-pointer {
287
- cursor: pointer;
288
- }
289
- </style>
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+ import type { Merge } from 'type-fest'
4
+ import type { IconName, SiteLabel, HTMLDivAttributes } from './'
5
+ import type { Snippet } from 'svelte'
6
+
7
+ import { getContext } from 'svelte'
8
+ import camelCase from 'just-camel-case'
9
+ import Icon from '@isoftdata/svelte-icon'
10
+ import Button from '@isoftdata/svelte-button'
11
+ import Select from '@isoftdata/svelte-select'
12
+ import { getEventValue } from '@isoftdata/browser-event'
13
+ import { Table, Td, type Column } from '@isoftdata/svelte-table'
14
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
15
+ import { SvelteSet, SvelteMap } from 'svelte/reactivity'
16
+
17
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
18
+
19
+ type Permission = {
20
+ id: number
21
+ displayName: string
22
+ category: string
23
+ codeName: string
24
+ }
25
+ type ComputedPermission = Merge<
26
+ Permission,
27
+ { value: PermissionValue; groupValue?: PermissionValue | undefined; computedValue?: PermissionValue }
28
+ >
29
+ type ComputedPermissionColumns = Array<Column<ComputedPermission>>
30
+ type PermissionValue = keyof typeof permissionValueList
31
+ type PermissionValueMap = Map<number, PermissionValue>
32
+
33
+ interface Props extends HTMLDivAttributes {
34
+ permissions: Array<Permission>
35
+ siteLabel?: SiteLabel
36
+ permissionValueChange?:
37
+ | ((change: { value: PermissionValue; permissionIds: Array<number> }) => void | Promise<void>)
38
+ | undefined
39
+ icon?: IconName
40
+ cardHeaderTitle?: string
41
+ permissionValueMap: PermissionValueMap
42
+ groupPermissionValueMap?: PermissionValueMap | undefined
43
+ children?: Snippet
44
+ }
45
+
46
+ let {
47
+ permissions,
48
+ siteLabel = 'Site',
49
+ permissionValueChange = undefined,
50
+ icon = 'user-lock',
51
+ cardHeaderTitle = translate('common:permissions', 'Permissions'),
52
+ permissionValueMap = $bindable(new SvelteMap()),
53
+ groupPermissionValueMap = undefined,
54
+ children,
55
+ ...rest
56
+ }: Props = $props()
57
+
58
+ let selectedPermissionIds: SvelteSet<number> = $state(new SvelteSet())
59
+ let selectedPermissionValue: string | null = $state(null)
60
+
61
+ const permissionColumns: ComputedPermissionColumns = getColumns(groupPermissionValueMap)
62
+ const permissionValueList = {
63
+ NONE: { label: translate('common:permissionLevel.none', 'None'), value: 'NONE', color: 'danger' },
64
+ SITE: {
65
+ label: translate(`common:permissionLevel.${siteLabel.toLowerCase()}`, siteLabel),
66
+ value: 'SITE',
67
+ color: 'primary',
68
+ },
69
+ GLOBAL: { label: translate('common:permissionLevel.global', 'Global'), value: 'GLOBAL', color: 'success' },
70
+ }
71
+
72
+ function getColumns(groupPermissionValueMap: PermissionValueMap | undefined): ComputedPermissionColumns {
73
+ let columns: ComputedPermissionColumns = [
74
+ {
75
+ property: 'value',
76
+ name: groupPermissionValueMap ? translate('common:user', 'User') : translate('common:permission', 'Permission'),
77
+ width: '1rem',
78
+ },
79
+ { property: 'category', name: translate('common:category', 'Category') },
80
+ { property: 'displayName', name: translate('common:name', 'Name') },
81
+ ]
82
+
83
+ if (groupPermissionValueMap) {
84
+ columns.splice(
85
+ 1,
86
+ 0,
87
+ {
88
+ property: 'groupValue',
89
+ name: translate('common:group', 'Group'),
90
+ align: 'center',
91
+ width: '1rem',
92
+ minWidth: '25%',
93
+ },
94
+ {
95
+ property: 'computedValue',
96
+ name: translate('common:access', 'Access'),
97
+ align: 'center',
98
+ width: '1rem',
99
+ minWidth: '25%',
100
+ },
101
+ )
102
+ }
103
+
104
+ return columns
105
+ }
106
+
107
+ function selectPermissions(permissionId: number) {
108
+ selectedPermissionIds.has(permissionId)
109
+ ? selectedPermissionIds.delete(permissionId)
110
+ : selectedPermissionIds.add(permissionId)
111
+ }
112
+
113
+ function selectAndDeselectAllPermissions() {
114
+ if (selectedPermissionIds.size === computedPermissions.length) {
115
+ selectedPermissionIds.clear()
116
+ selectedPermissionValue = null
117
+ } else {
118
+ selectedPermissionIds = new SvelteSet(permissions.map(permission => permission.id))
119
+ }
120
+ }
121
+
122
+ const getButtonColor = (optionValue: string, permissionValue: PermissionValue) =>
123
+ optionValue === permissionValue ? permissionValueList[optionValue].color : 'outline-secondary'
124
+
125
+ const isValidPermissionValue = (value: string): value is PermissionValue =>
126
+ Object.keys(permissionValueList).includes(value)
127
+
128
+ async function updatePermissionValues(event: Event, permissionIds: SvelteSet<number>) {
129
+ const newValue = getEventValue(event)
130
+ if (isValidPermissionValue(newValue)) {
131
+ const permissionIdsArray = Array.from(permissionIds)
132
+ for (const id of permissionIdsArray) {
133
+ permissionValueMap.set(id, newValue)
134
+ }
135
+ permissionValueMap = permissionValueMap
136
+ await permissionValueChange?.({
137
+ value: newValue,
138
+ permissionIds: permissionIdsArray,
139
+ })
140
+ }
141
+ }
142
+
143
+ function getComputedPermissions(
144
+ permissions: Array<Permission>,
145
+ permissionValueMap: PermissionValueMap,
146
+ groupPermissionValueMap: PermissionValueMap | undefined,
147
+ ): Array<ComputedPermission> {
148
+ return permissions.map(permission => {
149
+ const value = permissionValueMap.get(permission.id) ?? 'NONE'
150
+ const groupValue = groupPermissionValueMap?.get(permission.id)
151
+
152
+ return {
153
+ ...permission,
154
+ displayName: translate(`permissions:${camelCase(permission.codeName)}.displayName`, permission.displayName),
155
+ category: translate(`permissions:categories.${camelCase(permission.category)}`, permission.category),
156
+ value,
157
+ groupValue,
158
+ computedValue:
159
+ (value === 'NONE' && groupValue) || (value === 'SITE' && groupValue === 'GLOBAL') ? groupValue : value,
160
+ }
161
+ })
162
+ }
163
+
164
+ let computedPermissions = $derived.by(() =>
165
+ getComputedPermissions(permissions, permissionValueMap, groupPermissionValueMap),
166
+ )
167
+
168
+ $inspect(computedPermissions)
169
+ </script>
170
+
171
+ <div
172
+ class="card"
173
+ {...rest}
174
+ >
175
+ <div class="card-header">
176
+ <h5 class="mb-0">
177
+ <Icon
178
+ {icon}
179
+ class="mr-1 me-1"
180
+ />
181
+ {cardHeaderTitle}
182
+ </h5>
183
+ </div>
184
+ <div class="card-body">
185
+ <Table
186
+ class="mb-0"
187
+ parentClass="mh-60vh overflow-y-auto"
188
+ filterPlaceholder={translate('common:filterPermissions', 'Filter Permissions')}
189
+ filterColumnClass="col-6 col-lg-4 align-self-end"
190
+ rows={computedPermissions}
191
+ columns={permissionColumns}
192
+ responsive
193
+ filterEnabled
194
+ >
195
+ {#snippet children({ row: permission })}
196
+ <tr
197
+ onclick={() => selectPermissions(permission.id)}
198
+ class:table-primary={selectedPermissionIds.has(permission.id)}
199
+ class="cursor-pointer"
200
+ >
201
+ <Td property="value">
202
+ <div
203
+ class="btn-group btn-group-toggle"
204
+ data-toggle="buttons"
205
+ >
206
+ {#each Object.values(permissionValueList) as permissionValue}
207
+ <label class="btn btn-sm btn-{getButtonColor(permissionValue.value, permission.value)} cursor-pointer">
208
+ <input
209
+ class="btn-check"
210
+ type="radio"
211
+ autocomplete="off"
212
+ name={permission.value}
213
+ value={permissionValue.value}
214
+ onchange={e => updatePermissionValues(e, new SvelteSet([permission.id]))}
215
+ />
216
+ {permissionValue.label}
217
+ </label>
218
+ {/each}
219
+ </div>
220
+ </Td>
221
+ {#if groupPermissionValueMap}
222
+ <Td
223
+ property="groupValue"
224
+ class="px-4"
225
+ >
226
+ <span class="badge badge-pill bg-{permissionValueList[permission.groupValue ?? 'NONE'].color}"
227
+ >{permissionValueList[permission.groupValue ?? 'NONE'].label}</span
228
+ >
229
+ </Td>
230
+ <Td
231
+ property="computedValue"
232
+ class="px-4"
233
+ >
234
+ <span class="badge badge-pill bg-{permissionValueList[permission.computedValue ?? 'NONE'].color}"
235
+ >{permissionValueList[permission.computedValue ?? 'NONE'].label}</span
236
+ >
237
+ </Td>
238
+ {/if}
239
+ <Td property="category">{permission.category}</Td>
240
+ <Td property="displayName">{permission.displayName}</Td>
241
+ </tr>
242
+ {/snippet}
243
+ </Table>
244
+ {@render children?.()}
245
+ </div>
246
+ <div class="card-footer">
247
+ <div class="d-flex justify-content-between align-items-end">
248
+ <Select
249
+ label="{translate('common:setSelectedTo', 'Set Selected To')}:"
250
+ bind:value={selectedPermissionValue}
251
+ showEmptyOption={selectedPermissionValue === null}
252
+ emptyValue={null}
253
+ emptyText={translate('common:selectValue', 'Select a Value')}
254
+ labelParentClass="form-inline mr-2 me-2"
255
+ labelClass="mr-2 me-2 p-0"
256
+ disabled={selectedPermissionIds.size === 0}
257
+ onchange={e => {
258
+ updatePermissionValues(e, selectedPermissionIds)
259
+ selectedPermissionIds.clear()
260
+ selectedPermissionValue = null
261
+ }}
262
+ >
263
+ {#each Object.values(permissionValueList) as permission}
264
+ <option value={permission.value}>{permission.label}</option>
265
+ {/each}
266
+ </Select>
267
+ <Button
268
+ class="mb-1"
269
+ outline
270
+ size="sm"
271
+ iconClass="check-double"
272
+ onclick={selectAndDeselectAllPermissions}
273
+ >
274
+ <span class:d-none={selectedPermissionIds.size !== computedPermissions.length}
275
+ >{translate('common:deselectAll', 'Deselect All')}</span
276
+ >
277
+ <span class:d-none={selectedPermissionIds.size === computedPermissions.length}
278
+ >{translate('common:selectAll', 'Select All')}</span
279
+ >
280
+ </Button>
281
+ </div>
282
+ </div>
283
+ </div>
284
+
285
+ <style>
286
+ .cursor-pointer {
287
+ cursor: pointer;
288
+ }
289
+ </style>
@@ -104,13 +104,12 @@
104
104
  isLoading = false
105
105
  await accountInfoChanged?.()
106
106
  } catch (err) {
107
- console.log(err)
108
- if (err instanceof Error) {
109
- await error?.({
110
- heading: translate('configuration.messageHeading.failedToGenerateNewPIN', 'Failed To Generate New PIN'),
111
- message: err.message,
112
- })
113
- }
107
+ console.error(err)
108
+ await error?.({
109
+ heading: translate('configuration.messageHeading.failedToGenerateNewPIN', 'Failed To Generate New PIN'),
110
+ message:
111
+ err instanceof Error ? err.message : translate('workOrder.unknownError', 'An unknown error occurred'),
112
+ })
114
113
  }
115
114
  }
116
115
  }
@@ -400,12 +399,10 @@
400
399
  await accountInfoChanged?.()
401
400
  } catch (err) {
402
401
  console.error(err)
403
- if (err instanceof Error) {
404
- await error?.({
405
- heading: translate('configuration.user.messageHeading.failedToDeactivateUser', 'Failed To Deactivate User'),
406
- message: err.message,
407
- })
408
- }
402
+ await error?.({
403
+ heading: translate('configuration.user.messageHeading.failedToDeactivateUser', 'Failed To Deactivate User'),
404
+ message: err instanceof Error ? err.message : translate('workOrder.unknownError', 'An unknown error occurred'),
405
+ })
409
406
  }
410
407
  }}
411
408
  />
@@ -423,12 +420,12 @@
423
420
  message: translate('configuration.user.passwordChangeSuccessMessage', 'Password changed successfully'),
424
421
  })
425
422
  } catch (err) {
423
+ console.error(err)
426
424
  await error?.({
427
425
  heading: translate('configuration.user.passwordChangeErrorHeading', 'Failed To Change Password'),
428
426
  message:
429
427
  err instanceof Error ? err.message : translate('workOrder.unknownError', 'An unknown error occurred'),
430
428
  })
431
- console.error(err)
432
429
  throw err
433
430
  }
434
431
  } else {