@7365admin1/layer-common 3.2.1-staging.60 → 3.2.1-staging.62
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.
|
@@ -30,21 +30,28 @@
|
|
|
30
30
|
<v-row no-gutters>
|
|
31
31
|
<InputLabel title="Site" />
|
|
32
32
|
<v-col cols="12">
|
|
33
|
-
<v-text-field
|
|
33
|
+
<v-text-field
|
|
34
|
+
:model-value="siteName"
|
|
35
|
+
density="comfortable"
|
|
36
|
+
readonly
|
|
37
|
+
disabled
|
|
38
|
+
:loading="loadSite"
|
|
39
|
+
/>
|
|
34
40
|
</v-col>
|
|
35
41
|
</v-row>
|
|
36
42
|
</v-col>
|
|
37
43
|
|
|
38
|
-
<v-col cols="12" class="mt-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
<v-col cols="12" class="mt-3">
|
|
45
|
+
<div class="d-flex align-center ga-4 text-caption">
|
|
46
|
+
<span class="d-flex align-center ga-2">
|
|
47
|
+
<v-icon color="amber-darken-2" size="14">mdi-circle</v-icon>
|
|
48
|
+
Web and Mobile
|
|
49
|
+
</span>
|
|
50
|
+
<span class="d-flex align-center ga-2">
|
|
51
|
+
<v-icon color="blue-darken-2" size="14">mdi-circle</v-icon>
|
|
52
|
+
Web only
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
48
55
|
</v-col>
|
|
49
56
|
|
|
50
57
|
<v-col cols="12" class="mt-2">
|
|
@@ -54,7 +61,7 @@
|
|
|
54
61
|
class="text-none mb-2"
|
|
55
62
|
@click="() => toggleSelectAll(!isAllSelected)"
|
|
56
63
|
>
|
|
57
|
-
{{ isAllSelected ?
|
|
64
|
+
{{ isAllSelected ? "Unselect All" : "Select All" }}
|
|
58
65
|
</v-btn>
|
|
59
66
|
</v-col>
|
|
60
67
|
|
|
@@ -69,7 +76,12 @@
|
|
|
69
76
|
<!-- Category row -->
|
|
70
77
|
<v-list-item density="compact" :ripple="false">
|
|
71
78
|
<template #prepend>
|
|
72
|
-
<v-chip
|
|
79
|
+
<v-chip
|
|
80
|
+
class="mr-3"
|
|
81
|
+
size="small"
|
|
82
|
+
label
|
|
83
|
+
:color="resourceColor(String(resourceKey))"
|
|
84
|
+
>
|
|
73
85
|
{{ selectedActionCount(String(resourceKey)) }}
|
|
74
86
|
</v-chip>
|
|
75
87
|
</template>
|
|
@@ -97,14 +109,18 @@
|
|
|
97
109
|
<!-- Expanded checkboxes when toggled on -->
|
|
98
110
|
<template v-if="isCategoryEnabled(String(resourceKey))">
|
|
99
111
|
<template
|
|
100
|
-
v-for="
|
|
112
|
+
v-for="[actionKey, action] in Object.entries(actions || {})"
|
|
101
113
|
:key="actionKey"
|
|
102
114
|
>
|
|
103
115
|
<v-divider />
|
|
104
116
|
<v-list-item density="compact" :ripple="false">
|
|
105
117
|
<template #prepend>
|
|
106
118
|
<v-checkbox-btn
|
|
107
|
-
:model-value="
|
|
119
|
+
:model-value="
|
|
120
|
+
permState.selected.includes(
|
|
121
|
+
`${String(resourceKey)}:${String(actionKey)}`
|
|
122
|
+
)
|
|
123
|
+
"
|
|
108
124
|
@update:model-value="
|
|
109
125
|
toggleAction(
|
|
110
126
|
String(resourceKey),
|
|
@@ -207,17 +223,20 @@ const props = defineProps({
|
|
|
207
223
|
type: Boolean,
|
|
208
224
|
default: true,
|
|
209
225
|
},
|
|
226
|
+
webOnlyResources: {
|
|
227
|
+
type: Array as PropType<string[]>,
|
|
228
|
+
default: () => [],
|
|
229
|
+
},
|
|
210
230
|
cancelText: {
|
|
211
231
|
type: String,
|
|
212
|
-
default: "Cancel"
|
|
213
|
-
}
|
|
232
|
+
default: "Cancel",
|
|
233
|
+
},
|
|
214
234
|
});
|
|
215
235
|
|
|
216
236
|
const emit = defineEmits(["cancel", "success", "success:create-more"]);
|
|
217
237
|
|
|
218
238
|
const validForm = ref(false);
|
|
219
239
|
const name = ref("");
|
|
220
|
-
const platform = ref("web");
|
|
221
240
|
const disable = ref(false);
|
|
222
241
|
const message = ref("");
|
|
223
242
|
|
|
@@ -229,73 +248,81 @@ const { getSiteById } = useSite();
|
|
|
229
248
|
const permState = shallowRef({
|
|
230
249
|
selected: [] as string[],
|
|
231
250
|
enabled: [] as string[],
|
|
232
|
-
})
|
|
251
|
+
});
|
|
233
252
|
|
|
234
253
|
const resourceLabels: Record<string, string> = {
|
|
235
254
|
"access-card-mgmt": "Access Mgmt",
|
|
236
255
|
};
|
|
237
256
|
|
|
238
257
|
function formatResourceLabel(key: string): string {
|
|
239
|
-
|
|
258
|
+
const label = resourceLabels[key] ?? key.replace(/-/g, " ");
|
|
259
|
+
return props.webOnlyResources.includes(key) ? `${label} (Web Only)` : label;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function resourceColor(resource: string) {
|
|
263
|
+
return props.webOnlyResources.includes(resource)
|
|
264
|
+
? "blue-darken-2"
|
|
265
|
+
: "amber-darken-2";
|
|
240
266
|
}
|
|
241
267
|
|
|
242
268
|
function isCategoryEnabled(resource: string) {
|
|
243
|
-
return permState.value.enabled.includes(resource)
|
|
269
|
+
return permState.value.enabled.includes(resource);
|
|
244
270
|
}
|
|
245
271
|
|
|
246
272
|
function selectedActionCount(resource: string) {
|
|
247
|
-
return permState.value.selected.filter(p => p.startsWith(`${resource}:`))
|
|
273
|
+
return permState.value.selected.filter((p) => p.startsWith(`${resource}:`))
|
|
274
|
+
.length;
|
|
248
275
|
}
|
|
249
276
|
|
|
250
277
|
function toggleCategory(resource: string, value: boolean | null) {
|
|
251
|
-
const { selected, enabled } = permState.value
|
|
278
|
+
const { selected, enabled } = permState.value;
|
|
252
279
|
if (value) {
|
|
253
280
|
if (!enabled.includes(resource)) {
|
|
254
|
-
permState.value = { selected, enabled: [...enabled, resource] }
|
|
281
|
+
permState.value = { selected, enabled: [...enabled, resource] };
|
|
255
282
|
}
|
|
256
283
|
} else {
|
|
257
284
|
permState.value = {
|
|
258
|
-
selected: selected.filter(p => !p.startsWith(`${resource}:`)),
|
|
259
|
-
enabled: enabled.filter(r => r !== resource),
|
|
260
|
-
}
|
|
285
|
+
selected: selected.filter((p) => !p.startsWith(`${resource}:`)),
|
|
286
|
+
enabled: enabled.filter((r) => r !== resource),
|
|
287
|
+
};
|
|
261
288
|
}
|
|
262
289
|
}
|
|
263
290
|
|
|
264
291
|
function toggleAction(resource: string, action: string, value: boolean | null) {
|
|
265
|
-
if (value !== true && value !== false) return
|
|
266
|
-
const key = `${resource}:${action}
|
|
267
|
-
const { selected, enabled } = permState.value
|
|
292
|
+
if (value !== true && value !== false) return;
|
|
293
|
+
const key = `${resource}:${action}`;
|
|
294
|
+
const { selected, enabled } = permState.value;
|
|
268
295
|
if (value) {
|
|
269
296
|
if (!selected.includes(key)) {
|
|
270
|
-
permState.value = { selected: [...selected, key], enabled }
|
|
297
|
+
permState.value = { selected: [...selected, key], enabled };
|
|
271
298
|
}
|
|
272
299
|
} else {
|
|
273
|
-
permState.value = { selected: selected.filter(p => p !== key), enabled }
|
|
300
|
+
permState.value = { selected: selected.filter((p) => p !== key), enabled };
|
|
274
301
|
}
|
|
275
302
|
}
|
|
276
303
|
|
|
277
304
|
function toggleSelectAll(value: boolean) {
|
|
278
305
|
if (!value) {
|
|
279
|
-
permState.value = { selected: [], enabled: [] }
|
|
280
|
-
return
|
|
306
|
+
permState.value = { selected: [], enabled: [] };
|
|
307
|
+
return;
|
|
281
308
|
}
|
|
282
309
|
|
|
283
|
-
const perms = safePermissions.value
|
|
284
|
-
if (!perms) return
|
|
310
|
+
const perms = safePermissions.value;
|
|
311
|
+
if (!perms) return;
|
|
285
312
|
|
|
286
|
-
const allKeys: string[] = []
|
|
287
|
-
const categories: string[] = []
|
|
313
|
+
const allKeys: string[] = [];
|
|
314
|
+
const categories: string[] = [];
|
|
288
315
|
|
|
289
316
|
for (const [resource, actions] of Object.entries(perms)) {
|
|
290
|
-
if (!resource || !actions || typeof actions !==
|
|
291
|
-
categories.push(resource)
|
|
317
|
+
if (!resource || !actions || typeof actions !== "object") continue;
|
|
318
|
+
categories.push(resource);
|
|
292
319
|
for (const action of Object.keys(actions)) {
|
|
293
|
-
if (action) allKeys.push(`${resource}:${action}`)
|
|
320
|
+
if (action) allKeys.push(`${resource}:${action}`);
|
|
294
321
|
}
|
|
295
322
|
}
|
|
296
323
|
|
|
297
324
|
// 1 assignment to trigger reactivity only once
|
|
298
|
-
permState.value = { selected: allKeys, enabled: categories }
|
|
325
|
+
permState.value = { selected: allKeys, enabled: categories };
|
|
299
326
|
}
|
|
300
327
|
|
|
301
328
|
const siteName = ref("");
|
|
@@ -324,14 +351,13 @@ watch(
|
|
|
324
351
|
async function submit() {
|
|
325
352
|
disable.value = true;
|
|
326
353
|
try {
|
|
327
|
-
const platformValue = platform.value === "web" ? "website" : platform.value;
|
|
328
354
|
await createRole({
|
|
329
355
|
name: name.value,
|
|
330
356
|
permissions: permState.value.selected,
|
|
331
357
|
type: props.type,
|
|
332
358
|
org: props.org,
|
|
333
359
|
site: props.siteId,
|
|
334
|
-
platform:
|
|
360
|
+
platform: "website",
|
|
335
361
|
});
|
|
336
362
|
emit("success");
|
|
337
363
|
} catch (error: any) {
|
|
@@ -342,41 +368,41 @@ async function submit() {
|
|
|
342
368
|
}
|
|
343
369
|
|
|
344
370
|
function cancel() {
|
|
345
|
-
name.value = ""
|
|
346
|
-
permState.value = { selected: [], enabled: [] }
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
emit("cancel")
|
|
371
|
+
name.value = "";
|
|
372
|
+
permState.value = { selected: [], enabled: [] };
|
|
373
|
+
message.value = "";
|
|
374
|
+
emit("cancel");
|
|
350
375
|
}
|
|
351
376
|
|
|
352
377
|
const safePermissions = computed(() => {
|
|
353
|
-
const p = props.permissions || {}
|
|
354
|
-
const cleaned: Record<string, any> = {}
|
|
378
|
+
const p = props.permissions || {};
|
|
379
|
+
const cleaned: Record<string, any> = {};
|
|
355
380
|
|
|
356
381
|
for (const [k, v] of Object.entries(p)) {
|
|
357
|
-
if (v && typeof v ===
|
|
358
|
-
cleaned[k] = v
|
|
382
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
383
|
+
cleaned[k] = v;
|
|
359
384
|
} else {
|
|
360
|
-
cleaned[k] = {}
|
|
385
|
+
cleaned[k] = {};
|
|
361
386
|
}
|
|
362
387
|
}
|
|
363
388
|
|
|
364
|
-
return cleaned
|
|
365
|
-
})
|
|
389
|
+
return cleaned;
|
|
390
|
+
});
|
|
366
391
|
|
|
367
392
|
const isAllSelected = computed(() => {
|
|
368
|
-
const perms = safePermissions.value
|
|
369
|
-
const allKeys: string[] = []
|
|
393
|
+
const perms = safePermissions.value;
|
|
394
|
+
const allKeys: string[] = [];
|
|
370
395
|
|
|
371
396
|
Object.entries(perms).forEach(([resource, actions]) => {
|
|
372
|
-
if (!actions) return
|
|
397
|
+
if (!actions) return;
|
|
373
398
|
Object.keys(actions).forEach((action) => {
|
|
374
|
-
if (action) allKeys.push(`${resource}:${action}`)
|
|
375
|
-
})
|
|
376
|
-
})
|
|
377
|
-
|
|
378
|
-
return allKeys.length > 0 &&
|
|
379
|
-
allKeys.every(k => permState.value.selected.includes(k))
|
|
380
|
-
})
|
|
399
|
+
if (action) allKeys.push(`${resource}:${action}`);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
381
402
|
|
|
403
|
+
return (
|
|
404
|
+
allKeys.length > 0 &&
|
|
405
|
+
allKeys.every((k) => permState.value.selected.includes(k))
|
|
406
|
+
);
|
|
407
|
+
});
|
|
382
408
|
</script>
|
|
@@ -11,18 +11,104 @@
|
|
|
11
11
|
<v-form v-model="validForm" :disabled="disable">
|
|
12
12
|
<v-row no-gutters>
|
|
13
13
|
<v-col cols="12" class="mt-4">
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
<div class="d-flex align-center ga-4 text-caption mb-3">
|
|
15
|
+
<span class="d-flex align-center ga-2">
|
|
16
|
+
<v-icon color="amber-darken-2" size="14">mdi-circle</v-icon>
|
|
17
|
+
Web and Mobile
|
|
18
|
+
</span>
|
|
19
|
+
<span class="d-flex align-center ga-2">
|
|
20
|
+
<v-icon color="blue-darken-2" size="14">mdi-circle</v-icon>
|
|
21
|
+
Web only
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
<v-btn
|
|
25
|
+
v-if="edit"
|
|
26
|
+
size="small"
|
|
27
|
+
variant="tonal"
|
|
28
|
+
class="text-none mb-2"
|
|
29
|
+
@click="toggleSelectAll(!isAllSelected)"
|
|
30
|
+
>
|
|
31
|
+
{{ isAllSelected ? "Unselect All" : "Select All" }}
|
|
32
|
+
</v-btn>
|
|
33
|
+
<v-card variant="outlined" border="thin" rounded="lg">
|
|
34
|
+
<template
|
|
35
|
+
v-for="(actions, resourceKey, resourceIndex) in safePermissions"
|
|
36
|
+
:key="resourceKey"
|
|
37
|
+
>
|
|
38
|
+
<v-divider v-if="resourceIndex > 0" />
|
|
39
|
+
<v-list-item density="compact" :ripple="false">
|
|
40
|
+
<template #prepend>
|
|
41
|
+
<v-chip
|
|
42
|
+
class="mr-3"
|
|
43
|
+
size="small"
|
|
44
|
+
label
|
|
45
|
+
:color="resourceColor(String(resourceKey))"
|
|
46
|
+
>
|
|
47
|
+
{{ selectedActionCount(String(resourceKey)) }}
|
|
48
|
+
</v-chip>
|
|
49
|
+
</template>
|
|
50
|
+
<v-list-item-title
|
|
51
|
+
class="text-capitalize text-subtitle-2 font-weight-medium"
|
|
52
|
+
>
|
|
53
|
+
{{ formatResourceLabel(String(resourceKey)) }}
|
|
54
|
+
</v-list-item-title>
|
|
55
|
+
<template #append>
|
|
56
|
+
<v-switch
|
|
57
|
+
:model-value="isCategoryEnabled(String(resourceKey))"
|
|
58
|
+
:disabled="!edit"
|
|
59
|
+
color="green"
|
|
60
|
+
density="compact"
|
|
61
|
+
hide-details
|
|
62
|
+
inset
|
|
63
|
+
@update:model-value="
|
|
64
|
+
toggleCategory(String(resourceKey), $event)
|
|
65
|
+
"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
68
|
+
</v-list-item>
|
|
69
|
+
<template v-if="isCategoryEnabled(String(resourceKey))">
|
|
70
|
+
<template
|
|
71
|
+
v-for="[actionKey, action] in Object.entries(actions || {})"
|
|
72
|
+
:key="actionKey"
|
|
73
|
+
>
|
|
74
|
+
<v-divider />
|
|
75
|
+
<v-list-item density="compact" :ripple="false">
|
|
76
|
+
<template #prepend>
|
|
77
|
+
<v-checkbox-btn
|
|
78
|
+
:model-value="
|
|
79
|
+
definedModel.includes(
|
|
80
|
+
`${String(resourceKey)}:${String(actionKey)}`
|
|
81
|
+
)
|
|
82
|
+
"
|
|
83
|
+
:disabled="!edit"
|
|
84
|
+
color="primary"
|
|
85
|
+
density="compact"
|
|
86
|
+
class="mr-2"
|
|
87
|
+
@update:model-value="
|
|
88
|
+
toggleAction(
|
|
89
|
+
String(resourceKey),
|
|
90
|
+
String(actionKey),
|
|
91
|
+
$event
|
|
92
|
+
)
|
|
93
|
+
"
|
|
94
|
+
/>
|
|
95
|
+
</template>
|
|
96
|
+
<v-list-item-title
|
|
97
|
+
class="text-subtitle-2 font-weight-medium text-capitalize"
|
|
98
|
+
>
|
|
99
|
+
{{ String(actionKey).replace(/-/g, " ") }}
|
|
100
|
+
</v-list-item-title>
|
|
101
|
+
<v-list-item-subtitle class="text-caption">
|
|
102
|
+
{{ action.description }}
|
|
103
|
+
</v-list-item-subtitle>
|
|
104
|
+
</v-list-item>
|
|
105
|
+
</template>
|
|
106
|
+
</template>
|
|
107
|
+
</template>
|
|
108
|
+
</v-card>
|
|
109
|
+
<div v-if="errorMessages" class="text-caption text-error mt-2">
|
|
110
|
+
{{ errorMessages }}
|
|
111
|
+
</div>
|
|
26
112
|
</v-col>
|
|
27
113
|
|
|
28
114
|
<v-col v-if="edit" cols="12" class="my-2">
|
|
@@ -85,7 +171,7 @@
|
|
|
85
171
|
color="black"
|
|
86
172
|
class="text-none"
|
|
87
173
|
size="large"
|
|
88
|
-
:disabled="
|
|
174
|
+
:disabled="disable"
|
|
89
175
|
@click="edit = true"
|
|
90
176
|
>
|
|
91
177
|
Edit
|
|
@@ -99,7 +185,8 @@
|
|
|
99
185
|
color="black"
|
|
100
186
|
class="text-none"
|
|
101
187
|
size="large"
|
|
102
|
-
:disabled="
|
|
188
|
+
:disabled="Boolean(errorMessages) || disable"
|
|
189
|
+
:loading="disable"
|
|
103
190
|
@click="submit()"
|
|
104
191
|
>
|
|
105
192
|
Submit
|
|
@@ -111,8 +198,8 @@
|
|
|
111
198
|
</template>
|
|
112
199
|
|
|
113
200
|
<script setup lang="ts">
|
|
114
|
-
import useRole from
|
|
115
|
-
import useUtils from
|
|
201
|
+
import useRole from "../composables/useRole";
|
|
202
|
+
import useUtils from "../composables/useUtils";
|
|
116
203
|
|
|
117
204
|
const definedModel = defineModel<Array<string>>({
|
|
118
205
|
default: () => [],
|
|
@@ -135,11 +222,97 @@ const props = defineProps({
|
|
|
135
222
|
type: Object,
|
|
136
223
|
default: () => ({}),
|
|
137
224
|
},
|
|
225
|
+
webOnlyResources: {
|
|
226
|
+
type: Array as PropType<string[]>,
|
|
227
|
+
default: () => [],
|
|
228
|
+
},
|
|
138
229
|
});
|
|
139
230
|
|
|
140
231
|
const validForm = ref(false);
|
|
141
232
|
const message = ref("");
|
|
142
233
|
const disable = ref(false);
|
|
234
|
+
const expandedResources = ref<string[]>([]);
|
|
235
|
+
|
|
236
|
+
const resourceLabels: Record<string, string> = {
|
|
237
|
+
"access-card-mgmt": "Access Mgmt",
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
function formatResourceLabel(key: string) {
|
|
241
|
+
const label = resourceLabels[key] ?? key.replace(/-/g, " ");
|
|
242
|
+
return props.webOnlyResources.includes(key) ? `${label} (Web Only)` : label;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function resourceColor(resource: string) {
|
|
246
|
+
return props.webOnlyResources.includes(resource)
|
|
247
|
+
? "blue-darken-2"
|
|
248
|
+
: "amber-darken-2";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function selectedActionCount(resource: string) {
|
|
252
|
+
return definedModel.value.filter((permission) =>
|
|
253
|
+
permission.startsWith(`${resource}:`)
|
|
254
|
+
).length;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function isCategoryEnabled(resource: string) {
|
|
258
|
+
return (
|
|
259
|
+
expandedResources.value.includes(resource) ||
|
|
260
|
+
selectedActionCount(resource) > 0
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function toggleCategory(resource: string, value: boolean | null) {
|
|
265
|
+
if (!edit.value || value === null) return;
|
|
266
|
+
if (value) {
|
|
267
|
+
if (!expandedResources.value.includes(resource))
|
|
268
|
+
expandedResources.value.push(resource);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
expandedResources.value = expandedResources.value.filter(
|
|
272
|
+
(item) => item !== resource
|
|
273
|
+
);
|
|
274
|
+
definedModel.value = definedModel.value.filter(
|
|
275
|
+
(permission) => !permission.startsWith(`${resource}:`)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function toggleAction(resource: string, action: string, value: boolean | null) {
|
|
280
|
+
if (!edit.value || value === null) return;
|
|
281
|
+
const permission = `${resource}:${action}`;
|
|
282
|
+
if (value && !definedModel.value.includes(permission)) {
|
|
283
|
+
definedModel.value = [...definedModel.value, permission];
|
|
284
|
+
} else if (!value) {
|
|
285
|
+
definedModel.value = definedModel.value.filter(
|
|
286
|
+
(item) => item !== permission
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const safePermissions = computed(() => props.permissions || {});
|
|
292
|
+
|
|
293
|
+
const isAllSelected = computed(() => {
|
|
294
|
+
const keys = Object.entries(safePermissions.value).flatMap(
|
|
295
|
+
([resource, actions]) =>
|
|
296
|
+
Object.keys(actions || {}).map((action) => `${resource}:${action}`)
|
|
297
|
+
);
|
|
298
|
+
return (
|
|
299
|
+
keys.length > 0 && keys.every((key) => definedModel.value.includes(key))
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
function toggleSelectAll(value: boolean) {
|
|
304
|
+
if (!value) {
|
|
305
|
+
definedModel.value = [];
|
|
306
|
+
expandedResources.value = [];
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const permissions = Object.entries(safePermissions.value).flatMap(
|
|
310
|
+
([resource, actions]) =>
|
|
311
|
+
Object.keys(actions || {}).map((action) => `${resource}:${action}`)
|
|
312
|
+
);
|
|
313
|
+
definedModel.value = permissions;
|
|
314
|
+
expandedResources.value = Object.keys(safePermissions.value);
|
|
315
|
+
}
|
|
143
316
|
|
|
144
317
|
const emit = defineEmits(["success", "cancel"]);
|
|
145
318
|
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
:permissions="props.permissions"
|
|
85
85
|
:org="props.orgId"
|
|
86
86
|
:site-id="props.siteId"
|
|
87
|
+
:web-only-resources="props.webOnlyResources"
|
|
87
88
|
@success="success()"
|
|
88
89
|
:type="props.type"
|
|
89
90
|
@success:create-more="getRoles()"
|
|
@@ -100,6 +101,7 @@
|
|
|
100
101
|
v-model:edit="edit"
|
|
101
102
|
:name="name"
|
|
102
103
|
:id="roleId"
|
|
104
|
+
:web-only-resources="props.webOnlyResources"
|
|
103
105
|
/>
|
|
104
106
|
</v-dialog>
|
|
105
107
|
|
|
@@ -168,6 +170,10 @@ const props = defineProps({
|
|
|
168
170
|
required: true,
|
|
169
171
|
default: () => ({}),
|
|
170
172
|
},
|
|
173
|
+
webOnlyResources: {
|
|
174
|
+
type: Array as PropType<string[]>,
|
|
175
|
+
default: () => [],
|
|
176
|
+
},
|
|
171
177
|
headers: {
|
|
172
178
|
type: Array as PropType<Array<Record<string, any>>>,
|
|
173
179
|
default: () => [
|