@webitel/ui-datalist 26.12.5 → 26.12.6
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 +18 -0
- package/package.json +4 -4
- package/src/index.ts +5 -2
- package/src/modules/filters/modules/filterConfig/components/grantee/index.ts +1 -1
- package/src/modules/filters/modules/filterConfig/components/index.ts +8 -0
- package/src/modules/filters/modules/filterConfig/components/trigger-job-state/trigger-job-state-filter-value-field.vue +87 -0
- package/src/modules/filters/modules/filterConfig/components/trigger-job-state/trigger-job-state-filter-value-preview.vue +42 -0
- package/src/modules/filters/modules/filterConfig/enums/FilterOption.ts +2 -0
- package/src/modules/permissions/components/_internal/permissions-role-row.vue +25 -0
- package/src/modules/permissions/components/_internal/permissions-role-select.vue +35 -0
- package/src/modules/permissions/components/_internal/permissions-tab-content.vue +161 -0
- package/src/modules/permissions/components/_internal/permissions-tab-role-popup.vue +94 -0
- package/src/modules/{permissions-page → permissions}/components/permissions-tab.vue +5 -5
- package/src/modules/permissions/composables/useAccessModeOptions.ts +48 -0
- package/src/modules/permissions/index.ts +40 -0
- package/src/modules/permissions/stores/_internals/headers.ts +52 -0
- package/src/modules/{permissions-page → permissions}/stores/createPermissionsStore.ts +19 -44
- package/src/modules/{permissions-page → permissions}/types/Permission.types.ts +2 -2
- package/src/modules/permissions/types/PermissionsUserAccess.ts +10 -0
- package/types/.tsbuildinfo +1 -1
- package/types/index.d.ts +2 -2
- package/types/modules/filter-presets/stores/createFilterPresetsStore.d.ts +1 -1
- package/types/modules/filters/modules/filterConfig/components/grantee/index.d.ts +1 -1
- package/types/modules/filters/modules/filterConfig/components/index.d.ts +4 -2
- package/types/modules/filters/modules/filterConfig/components/trigger-job-state/trigger-job-state-filter-value-field.vue.d.ts +18 -0
- package/types/modules/filters/modules/filterConfig/components/trigger-job-state/trigger-job-state-filter-value-preview.vue.d.ts +6 -0
- package/types/modules/filters/modules/filterConfig/enums/FilterOption.d.ts +2 -0
- package/types/modules/permissions/components/_internal/permissions-role-row.vue.d.ts +17 -0
- package/types/modules/permissions/components/_internal/permissions-role-select.vue.d.ts +11 -0
- package/types/modules/permissions/components/_internal/permissions-tab-content.vue.d.ts +32 -0
- package/types/modules/permissions/components/_internal/permissions-tab-role-popup.vue.d.ts +10 -0
- package/types/modules/{permissions-page → permissions}/components/permissions-tab.vue.d.ts +1 -1
- package/types/modules/permissions/composables/useAccessModeOptions.d.ts +22 -0
- package/types/modules/permissions/index.d.ts +17 -0
- package/types/modules/permissions/stores/_internals/headers.d.ts +22 -0
- package/types/modules/{permissions-page → permissions}/stores/createPermissionsStore.d.ts +5 -3
- package/types/modules/{permissions-page → permissions}/types/Permission.types.d.ts +2 -2
- package/types/modules/permissions/types/PermissionsUserAccess.d.ts +3 -0
- package/src/modules/permissions-page/index.ts +0 -23
- package/src/modules/permissions-page/scripts/PermissionsApiModule.ts +0 -57
- package/src/modules/permissions-page/scripts/__tests__/PermissionsApiModule.spec.ts +0 -31
- package/types/modules/permissions-page/index.d.ts +0 -7
- package/types/modules/permissions-page/scripts/PermissionsApiModule.d.ts +0 -9
package/Readme.md
CHANGED
|
@@ -24,6 +24,7 @@ This package contains:
|
|
|
24
24
|
* card page tools (store, components, etc)
|
|
25
25
|
* table filters (store, components, etc)
|
|
26
26
|
* table filter presets (store, components, etc)
|
|
27
|
+
* permissions tab (store, components, etc)
|
|
27
28
|
|
|
28
29
|
## Exports
|
|
29
30
|
|
|
@@ -85,6 +86,23 @@ import {
|
|
|
85
86
|
} from '@webitel/ui-datalist/card';
|
|
86
87
|
```
|
|
87
88
|
|
|
89
|
+
### Permissions tools
|
|
90
|
+
|
|
91
|
+
Object ACL tab of an entity card. See docs: `pages/packages/ui-datalist/permissions`.
|
|
92
|
+
|
|
93
|
+
#### Import
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import {
|
|
97
|
+
createPermissionsStore, // pinia store factory, takes the entity api (getPermissionsList / patchPermissions)
|
|
98
|
+
PermissionsTab, // ready tab: register it in the router, pass store / access / parentId
|
|
99
|
+
headersWithCreate, // columns with "create" (x), for ACLs that have it
|
|
100
|
+
PermissionsRoleRow, // role with its role / user icon
|
|
101
|
+
PermissionsRoleSelect, // roles select
|
|
102
|
+
useAccessModeOptions, // access mode options + rows with localized rule names
|
|
103
|
+
} from '@webitel/ui-datalist/permissions';
|
|
104
|
+
```
|
|
105
|
+
|
|
88
106
|
## Versions
|
|
89
107
|
|
|
90
108
|
// TODO: describe new major/minor versions, if will be needed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webitel/ui-datalist",
|
|
3
|
-
"version": "26.12.
|
|
3
|
+
"version": "26.12.6",
|
|
4
4
|
"description": "Toolkit for building data lists in webitel ui system",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build:types": "vue-tsc -b tsconfig.build.json",
|
|
@@ -96,9 +96,9 @@
|
|
|
96
96
|
"types": "./types/modules/card/index.d.ts",
|
|
97
97
|
"import": "./src/modules/card/index.ts"
|
|
98
98
|
},
|
|
99
|
-
"./permissions
|
|
100
|
-
"types": "./types/modules/permissions
|
|
101
|
-
"import": "./src/modules/permissions
|
|
99
|
+
"./permissions": {
|
|
100
|
+
"types": "./types/modules/permissions/index.d.ts",
|
|
101
|
+
"import": "./src/modules/permissions/index.ts"
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { useNestedTableList } from './modules/table/composables/useNestedTableList';
|
|
2
2
|
import { createTableStore } from './modules/table/createTableStore.store';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
DatalistTableHeader,
|
|
5
|
+
PatchItemPropertyParams,
|
|
6
|
+
} from './modules/types/tableStore.types';
|
|
4
7
|
|
|
5
|
-
export type { DatalistTableHeader };
|
|
8
|
+
export type { DatalistTableHeader, PatchItemPropertyParams };
|
|
6
9
|
export { createTableStore, useNestedTableList };
|
|
@@ -135,6 +135,8 @@ import TeamFilter from './team/team-filter-value-field.vue';
|
|
|
135
135
|
import TeamFilterPreview from './team/team-filter-value-preview.vue';
|
|
136
136
|
import TotalDurationFilter from './total-duration/total-duration-filter-value-field.vue';
|
|
137
137
|
import TotalDurationFilterPreview from './total-duration/total-duration-filter-value-preview.vue';
|
|
138
|
+
import TriggerJobStateFilter from './trigger-job-state/trigger-job-state-filter-value-field.vue';
|
|
139
|
+
import TriggerJobStateFilterPreview from './trigger-job-state/trigger-job-state-filter-value-preview.vue';
|
|
138
140
|
import { createUserFilterConfig } from './user';
|
|
139
141
|
import UserFilter from './user/user-filter-value-field.vue';
|
|
140
142
|
import UserFilterPreview from './user/user-filter-value-preview.vue';
|
|
@@ -247,6 +249,8 @@ export {
|
|
|
247
249
|
TeamFilterPreview,
|
|
248
250
|
TotalDurationFilter,
|
|
249
251
|
TotalDurationFilterPreview,
|
|
252
|
+
TriggerJobStateFilter,
|
|
253
|
+
TriggerJobStateFilterPreview,
|
|
250
254
|
UserFilter,
|
|
251
255
|
UserFilterPreview,
|
|
252
256
|
UtilizationProgressFilter,
|
|
@@ -291,6 +295,7 @@ export const FilterOptionToValueComponentMap: Record<FilterOption, Component> =
|
|
|
291
295
|
[FilterOption.JoinedAt]: DateTimeOptionsFilterValueField,
|
|
292
296
|
[FilterOption.LeavingAt]: DateTimeOptionsFilterValueField,
|
|
293
297
|
[FilterOption.OfferingAt]: DateTimeOptionsFilterValueField,
|
|
298
|
+
[FilterOption.StartedAt]: DateTimeOptionsFilterValueField,
|
|
294
299
|
[FilterOption.Bucket]: BucketFilter,
|
|
295
300
|
[FilterOption.StopCause]: StopCauseFilter,
|
|
296
301
|
[FilterOption.MemberPriority]: RatingFromToFilter,
|
|
@@ -299,6 +304,7 @@ export const FilterOptionToValueComponentMap: Record<FilterOption, Component> =
|
|
|
299
304
|
[FilterOption.MemberName]: StringFilterValueField,
|
|
300
305
|
[FilterOption.MemberDestination]: StringFilterValueField,
|
|
301
306
|
[FilterOption.CallReportingResult]: CallReportingResultFilter,
|
|
307
|
+
[FilterOption.TriggerJobState]: TriggerJobStateFilter,
|
|
302
308
|
[FilterOption.Action]: ActionFilter,
|
|
303
309
|
[FilterOption.Object]: ObjectFilter,
|
|
304
310
|
[FilterOption.CaseStatus]: CaseStatusFilterValueField,
|
|
@@ -329,6 +335,7 @@ export const FilterOptionToPreviewComponentMap: Record<
|
|
|
329
335
|
[FilterOption.JoinedAt]: DateTimeOptionsFilterValuePreview,
|
|
330
336
|
[FilterOption.LeavingAt]: DateTimeOptionsFilterValuePreview,
|
|
331
337
|
[FilterOption.OfferingAt]: DateTimeOptionsFilterValuePreview,
|
|
338
|
+
[FilterOption.StartedAt]: DateTimeOptionsFilterValuePreview,
|
|
332
339
|
[FilterOption.Bucket]: BucketFilterPreview,
|
|
333
340
|
[FilterOption.StopCause]: StopCauseFilterPreview,
|
|
334
341
|
[FilterOption.MemberPriority]: RatingFromToFilterPreview,
|
|
@@ -337,6 +344,7 @@ export const FilterOptionToPreviewComponentMap: Record<
|
|
|
337
344
|
[FilterOption.MemberName]: StringFilterValuePreview,
|
|
338
345
|
[FilterOption.MemberDestination]: StringFilterValuePreview,
|
|
339
346
|
[FilterOption.CallReportingResult]: CallReportingResultFilterPreview,
|
|
347
|
+
[FilterOption.TriggerJobState]: TriggerJobStateFilterPreview,
|
|
340
348
|
[FilterOption.Action]: ActionFilterPreview,
|
|
341
349
|
[FilterOption.Object]: ObjectFilterPreview,
|
|
342
350
|
[FilterOption.Agent]: AgentFilterPreview,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-multi-select
|
|
3
|
+
:label="labelValue"
|
|
4
|
+
:options="options"
|
|
5
|
+
v-model:model-value="model"
|
|
6
|
+
:v="!disableValidation && v$.model"
|
|
7
|
+
chips-view
|
|
8
|
+
data-key="value"
|
|
9
|
+
option-label="label"
|
|
10
|
+
option-value="value"
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script lang="ts" setup>
|
|
15
|
+
import { useVuelidate } from '@vuelidate/core';
|
|
16
|
+
import { required } from '@vuelidate/validators';
|
|
17
|
+
import { EngineTriggerJobState } from '@webitel/api-services/gen/models';
|
|
18
|
+
import { WtMultiSelect } from '@webitel/ui-sdk/components';
|
|
19
|
+
import { computed, onMounted, watch } from 'vue';
|
|
20
|
+
import { useI18n } from 'vue-i18n';
|
|
21
|
+
|
|
22
|
+
import { WtSysTypeFilterConfig } from '../../classes/FilterConfig';
|
|
23
|
+
|
|
24
|
+
const model = defineModel<string[]>();
|
|
25
|
+
const { t } = useI18n();
|
|
26
|
+
|
|
27
|
+
const props = defineProps<{
|
|
28
|
+
filterConfig?: WtSysTypeFilterConfig;
|
|
29
|
+
disableValidation?: boolean;
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
const v$ = useVuelidate(
|
|
33
|
+
computed(() => ({
|
|
34
|
+
model: {
|
|
35
|
+
required,
|
|
36
|
+
},
|
|
37
|
+
})),
|
|
38
|
+
{
|
|
39
|
+
model,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
$autoDirty: true,
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
v$.value.$touch();
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits<{
|
|
49
|
+
'update:invalid': [
|
|
50
|
+
boolean,
|
|
51
|
+
];
|
|
52
|
+
}>();
|
|
53
|
+
|
|
54
|
+
const labelValue = computed(() =>
|
|
55
|
+
t(
|
|
56
|
+
`webitelUI.filters.${
|
|
57
|
+
props?.filterConfig?.showFilterName
|
|
58
|
+
? props?.filterConfig.name
|
|
59
|
+
: 'filterValue'
|
|
60
|
+
}`,
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/** [Claude] the states a trigger run ends up in, for the trigger logs' result filter */
|
|
65
|
+
const options = computed(() =>
|
|
66
|
+
Object.values(EngineTriggerJobState).map((value) => ({
|
|
67
|
+
value,
|
|
68
|
+
label: t(`objects.triggerJobState.${value}`),
|
|
69
|
+
})),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
onMounted(() => {
|
|
73
|
+
if (!props?.disableValidation) v$.value.$touch();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
watch(
|
|
77
|
+
() => v$.value.$invalid,
|
|
78
|
+
(invalid) => {
|
|
79
|
+
emit('update:invalid', invalid);
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
immediate: true,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<style scoped></style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ul>
|
|
3
|
+
<li
|
|
4
|
+
v-for="(label, index) of arrayValues"
|
|
5
|
+
:key="index"
|
|
6
|
+
>
|
|
7
|
+
{{ label }}
|
|
8
|
+
</li>
|
|
9
|
+
</ul>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script lang="ts" setup>
|
|
13
|
+
import { EngineTriggerJobState } from '@webitel/api-services/gen/models';
|
|
14
|
+
import { computed } from 'vue';
|
|
15
|
+
import { useI18n } from 'vue-i18n';
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{
|
|
18
|
+
value?: string[];
|
|
19
|
+
}>();
|
|
20
|
+
|
|
21
|
+
const { t } = useI18n();
|
|
22
|
+
|
|
23
|
+
const options = computed(() =>
|
|
24
|
+
Object.values(EngineTriggerJobState).map((value) => ({
|
|
25
|
+
value,
|
|
26
|
+
label: t(`objects.triggerJobState.${value}`),
|
|
27
|
+
})),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const arrayLabels = computed(() =>
|
|
31
|
+
options.value.reduce<Record<string, string>>((acc, { value, label }) => {
|
|
32
|
+
acc[value] = label;
|
|
33
|
+
return acc;
|
|
34
|
+
}, {}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const arrayValues = computed(() =>
|
|
38
|
+
(props.value ?? []).map((v) => arrayLabels.value[v] || v),
|
|
39
|
+
);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<style scoped></style>
|
|
@@ -55,6 +55,7 @@ export const FilterOption = {
|
|
|
55
55
|
JoinedAt: 'joinedAt',
|
|
56
56
|
LeavingAt: 'leavingAt',
|
|
57
57
|
OfferingAt: 'offeringAt',
|
|
58
|
+
StartedAt: 'startedAt',
|
|
58
59
|
UploadedAt: 'uploadedAt',
|
|
59
60
|
StopCause: 'stopCause',
|
|
60
61
|
MemberPriority: 'memberPriority',
|
|
@@ -62,6 +63,7 @@ export const FilterOption = {
|
|
|
62
63
|
MemberName: 'name',
|
|
63
64
|
MemberDestination: 'destination',
|
|
64
65
|
CallReportingResult: 'result' /** queue attempt CallReportingStatus */,
|
|
66
|
+
TriggerJobState: 'triggerJobState',
|
|
65
67
|
Action: 'action',
|
|
66
68
|
Object: 'object',
|
|
67
69
|
Date: 'date',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="permissions-role-row">
|
|
3
|
+
<wt-icon :icon="role.user ? 'user' : 'role'" />
|
|
4
|
+
<slot>
|
|
5
|
+
{{ role.name }}
|
|
6
|
+
</slot>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import type { PermissionGrantee } from '../../types/Permission.types';
|
|
12
|
+
|
|
13
|
+
defineProps<{
|
|
14
|
+
role: PermissionGrantee;
|
|
15
|
+
}>();
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<style lang="scss" scoped>
|
|
19
|
+
.permissions-role-row {
|
|
20
|
+
display: grid;
|
|
21
|
+
grid-template-columns: var(--icon-md-size) 1fr;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--spacing-sm);
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-single-select
|
|
3
|
+
class="permissions-role-select"
|
|
4
|
+
option-label="name"
|
|
5
|
+
v-bind="attrs"
|
|
6
|
+
v-model:model-value="model"
|
|
7
|
+
>
|
|
8
|
+
<template #value="{ value, getOptionLabel }">
|
|
9
|
+
<permissions-role-row :role="value">
|
|
10
|
+
{{ getOptionLabel(value) || value }}
|
|
11
|
+
</permissions-role-row>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<template #option="{ option, getOptionLabel }">
|
|
15
|
+
<permissions-role-row :role="option">
|
|
16
|
+
{{ getOptionLabel(option) || option }}
|
|
17
|
+
</permissions-role-row>
|
|
18
|
+
</template>
|
|
19
|
+
</wt-single-select>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { useAttrs } from 'vue';
|
|
24
|
+
|
|
25
|
+
import type { PermissionGrantee } from '../../types/Permission.types';
|
|
26
|
+
import PermissionsRoleRow from './permissions-role-row.vue';
|
|
27
|
+
|
|
28
|
+
const attrs = useAttrs();
|
|
29
|
+
|
|
30
|
+
const model = defineModel<PermissionGrantee | null>({
|
|
31
|
+
required: true,
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section
|
|
3
|
+
v-if="access.read"
|
|
4
|
+
class="table-section"
|
|
5
|
+
>
|
|
6
|
+
<permissions-tab-role-popup
|
|
7
|
+
v-if="access.create"
|
|
8
|
+
:existing-grantee-ids="existingGranteeIds"
|
|
9
|
+
:add-role-permissions="addRolePermissions"
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
<header class="table-title permissions-tab-content__header">
|
|
13
|
+
<wt-action-bar
|
|
14
|
+
:include="[IconAction.ADD, IconAction.REFRESH]"
|
|
15
|
+
:disabled:add="!access.create"
|
|
16
|
+
@click:add="openRolePopup"
|
|
17
|
+
@click:refresh="refresh"
|
|
18
|
+
/>
|
|
19
|
+
</header>
|
|
20
|
+
|
|
21
|
+
<div class="table-section__table-wrapper">
|
|
22
|
+
<wt-empty
|
|
23
|
+
v-show="showEmpty"
|
|
24
|
+
:image="imageEmpty"
|
|
25
|
+
:text="textEmpty"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<wt-loader v-show="isLoading" />
|
|
29
|
+
|
|
30
|
+
<div
|
|
31
|
+
v-show="dataList.length && !isLoading"
|
|
32
|
+
class="table-section__visible-scroll-wrapper"
|
|
33
|
+
>
|
|
34
|
+
<wt-table
|
|
35
|
+
:data="localizedDataList"
|
|
36
|
+
:grid-actions="access.update"
|
|
37
|
+
:headers="headers"
|
|
38
|
+
:selectable="false"
|
|
39
|
+
sortable
|
|
40
|
+
@sort="sort"
|
|
41
|
+
>
|
|
42
|
+
<template #grantee="{ item }">
|
|
43
|
+
<permissions-role-row :role="item.grantee" />
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<!-- [Claude] one select per rule column; "create" shows only where the headers have it, e.g. object classes OBAC -->
|
|
47
|
+
<template
|
|
48
|
+
v-for="(ruleName, column) of AccessRuleColumn"
|
|
49
|
+
:key="column"
|
|
50
|
+
#[column]="{ item }"
|
|
51
|
+
>
|
|
52
|
+
<wt-single-select
|
|
53
|
+
:show-clear="false"
|
|
54
|
+
:disabled="!access.update"
|
|
55
|
+
:options="accessOptions"
|
|
56
|
+
:model-value="item.access[ruleName]"
|
|
57
|
+
@update:model-value="changeAccessMode({ item, ruleName, mode: $event })"
|
|
58
|
+
/>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<template #actions="{ item }">
|
|
62
|
+
<wt-icon-action
|
|
63
|
+
action="delete"
|
|
64
|
+
@click="
|
|
65
|
+
changeAccessMode({
|
|
66
|
+
item,
|
|
67
|
+
ruleName: AccessRuleName.R,
|
|
68
|
+
mode: { id: AccessMode.Forbidden },
|
|
69
|
+
})
|
|
70
|
+
"
|
|
71
|
+
/>
|
|
72
|
+
</template>
|
|
73
|
+
</wt-table>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<slot name="pagination" />
|
|
77
|
+
</div>
|
|
78
|
+
</section>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script setup lang="ts">
|
|
82
|
+
import {
|
|
83
|
+
AccessMode,
|
|
84
|
+
AccessRuleColumn,
|
|
85
|
+
AccessRuleName,
|
|
86
|
+
} from '@webitel/api-services/enums';
|
|
87
|
+
import { IconAction } from '@webitel/ui-sdk/enums';
|
|
88
|
+
import { useTableEmpty } from '@webitel/ui-sdk/src/modules/TableComponentModule/composables/useTableEmpty';
|
|
89
|
+
import { computed } from 'vue';
|
|
90
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
91
|
+
|
|
92
|
+
import type { DatalistTableHeader } from '../../../types/tableStore.types';
|
|
93
|
+
import { useAccessModeOptions } from '../../composables/useAccessModeOptions';
|
|
94
|
+
import type {
|
|
95
|
+
ChangeAccessModePayload,
|
|
96
|
+
PermissionEntity,
|
|
97
|
+
PermissionGrantee,
|
|
98
|
+
} from '../../types/Permission.types';
|
|
99
|
+
import {
|
|
100
|
+
DEFAULT_PERMISSIONS_USER_ACCESS,
|
|
101
|
+
type PermissionsUserAccess,
|
|
102
|
+
} from '../../types/PermissionsUserAccess';
|
|
103
|
+
import PermissionsRoleRow from './permissions-role-row.vue';
|
|
104
|
+
import PermissionsTabRolePopup from './permissions-tab-role-popup.vue';
|
|
105
|
+
|
|
106
|
+
const props = withDefaults(
|
|
107
|
+
defineProps<{
|
|
108
|
+
dataList: PermissionEntity[];
|
|
109
|
+
isLoading?: boolean;
|
|
110
|
+
headers: DatalistTableHeader[];
|
|
111
|
+
error?: unknown;
|
|
112
|
+
refresh: () => unknown;
|
|
113
|
+
sort: (column: DatalistTableHeader) => unknown;
|
|
114
|
+
changeAccessMode: (payload: ChangeAccessModePayload) => unknown;
|
|
115
|
+
existingGranteeIds?: PermissionGrantee['id'][];
|
|
116
|
+
addRolePermissions: (role: PermissionGrantee) => Promise<unknown>;
|
|
117
|
+
access?: PermissionsUserAccess;
|
|
118
|
+
}>(),
|
|
119
|
+
{
|
|
120
|
+
isLoading: false,
|
|
121
|
+
existingGranteeIds: () => [],
|
|
122
|
+
access: () => DEFAULT_PERMISSIONS_USER_ACCESS,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const { accessOptions, localizedDataList } = useAccessModeOptions(
|
|
127
|
+
() => props.dataList,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const route = useRoute();
|
|
131
|
+
const router = useRouter();
|
|
132
|
+
|
|
133
|
+
/** [Claude] the role popup is opened by the route, so it survives a reload */
|
|
134
|
+
const openRolePopup = () =>
|
|
135
|
+
router.push({
|
|
136
|
+
name: route.name,
|
|
137
|
+
query: route.query,
|
|
138
|
+
hash: route.hash,
|
|
139
|
+
params: {
|
|
140
|
+
...route.params,
|
|
141
|
+
permissionId: 'new',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const {
|
|
146
|
+
showEmpty,
|
|
147
|
+
image: imageEmpty,
|
|
148
|
+
text: textEmpty,
|
|
149
|
+
} = useTableEmpty({
|
|
150
|
+
dataList: computed(() => props.dataList),
|
|
151
|
+
error: computed(() => props.error),
|
|
152
|
+
isLoading: computed(() => props.isLoading),
|
|
153
|
+
});
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<style scoped>
|
|
157
|
+
/* [Claude] no title in the header, so push the action bar to the right */
|
|
158
|
+
.permissions-tab-content__header {
|
|
159
|
+
justify-content: flex-end;
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-popup
|
|
3
|
+
:shown="shown"
|
|
4
|
+
overflow
|
|
5
|
+
size="sm"
|
|
6
|
+
@close="close"
|
|
7
|
+
>
|
|
8
|
+
<template #title>
|
|
9
|
+
{{ `${t('reusable.new')} ${t('objects.grantee', 1).toLowerCase()}` }}
|
|
10
|
+
</template>
|
|
11
|
+
<template #main>
|
|
12
|
+
<permissions-role-select
|
|
13
|
+
v-model:model-value="grantee"
|
|
14
|
+
:clearable="false"
|
|
15
|
+
:placeholder="t('objects.role', 1)"
|
|
16
|
+
:search-method="getAvailableGrantees"
|
|
17
|
+
/>
|
|
18
|
+
</template>
|
|
19
|
+
<template #actions>
|
|
20
|
+
<wt-button @click="save(grantee)">
|
|
21
|
+
{{ t('reusable.add') }}
|
|
22
|
+
</wt-button>
|
|
23
|
+
<wt-button
|
|
24
|
+
color="secondary"
|
|
25
|
+
@click="close"
|
|
26
|
+
>
|
|
27
|
+
{{ t('reusable.close') }}
|
|
28
|
+
</wt-button>
|
|
29
|
+
</template>
|
|
30
|
+
</wt-popup>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { RolesAPI } from '@webitel/api-services/api';
|
|
35
|
+
import { computed, ref } from 'vue';
|
|
36
|
+
import { useI18n } from 'vue-i18n';
|
|
37
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
38
|
+
|
|
39
|
+
import type { PermissionGrantee } from '../../types/Permission.types';
|
|
40
|
+
import PermissionsRoleSelect from './permissions-role-select.vue';
|
|
41
|
+
|
|
42
|
+
const props = withDefaults(
|
|
43
|
+
defineProps<{
|
|
44
|
+
existingGranteeIds?: PermissionGrantee['id'][];
|
|
45
|
+
addRolePermissions: (role: PermissionGrantee) => Promise<unknown>;
|
|
46
|
+
}>(),
|
|
47
|
+
{
|
|
48
|
+
existingGranteeIds: () => [],
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const { t } = useI18n();
|
|
53
|
+
const router = useRouter();
|
|
54
|
+
const route = useRoute();
|
|
55
|
+
|
|
56
|
+
const grantee = ref<PermissionGrantee | null>(null);
|
|
57
|
+
|
|
58
|
+
/** [Claude] opened by the route, the tab's "add" action sets `permissionId` */
|
|
59
|
+
const shown = computed(() => !!route.params.permissionId);
|
|
60
|
+
|
|
61
|
+
const close = () => {
|
|
62
|
+
const { query, hash, name } = route;
|
|
63
|
+
const { permissionId, ...params } = route.params;
|
|
64
|
+
|
|
65
|
+
grantee.value = null;
|
|
66
|
+
|
|
67
|
+
return router.push({
|
|
68
|
+
query,
|
|
69
|
+
hash,
|
|
70
|
+
name,
|
|
71
|
+
params,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const getAvailableGrantees = async (params: Record<string, unknown>) => {
|
|
76
|
+
const { items, ...rest } = await RolesAPI.getLookup(params);
|
|
77
|
+
return {
|
|
78
|
+
/** [Claude] acl grantee ids come as strings, role ids as numbers */
|
|
79
|
+
items: (items as PermissionGrantee[]).filter(
|
|
80
|
+
(role) =>
|
|
81
|
+
!props.existingGranteeIds.some((id) => String(id) === String(role.id)),
|
|
82
|
+
),
|
|
83
|
+
...rest,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const save = async (newGrantee: PermissionGrantee | null) => {
|
|
88
|
+
if (!newGrantee) return;
|
|
89
|
+
await props.addRolePermissions(newGrantee);
|
|
90
|
+
return close();
|
|
91
|
+
};
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<style lang="scss" scoped></style>
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
</template>
|
|
16
16
|
|
|
17
17
|
<script setup lang="ts">
|
|
18
|
-
import {
|
|
19
|
-
DEFAULT_PERMISSIONS_USER_ACCESS,
|
|
20
|
-
PermissionsTabContent,
|
|
21
|
-
type PermissionsUserAccess,
|
|
22
|
-
} from '@webitel/ui-sdk/modules/ObjectPermissions';
|
|
23
18
|
import type { Id } from '@webitel/ui-sdk/src/api/types/ApiModule';
|
|
24
19
|
import { storeToRefs } from 'pinia';
|
|
25
20
|
import { computed, onUnmounted } from 'vue';
|
|
26
21
|
import type { PermissionsPiniaStore } from '../stores/createPermissionsStore';
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_PERMISSIONS_USER_ACCESS,
|
|
24
|
+
type PermissionsUserAccess,
|
|
25
|
+
} from '../types/PermissionsUserAccess';
|
|
26
|
+
import PermissionsTabContent from './_internal/permissions-tab-content.vue';
|
|
27
27
|
|
|
28
28
|
const props = withDefaults(
|
|
29
29
|
defineProps<{
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AccessMode } from '@webitel/api-services/enums';
|
|
2
|
+
import { computed, type MaybeRefOrGetter, toValue } from 'vue';
|
|
3
|
+
import { useI18n } from 'vue-i18n';
|
|
4
|
+
|
|
5
|
+
interface AccessModeRow {
|
|
6
|
+
access: Record<
|
|
7
|
+
string,
|
|
8
|
+
{
|
|
9
|
+
id: number;
|
|
10
|
+
}
|
|
11
|
+
>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** [Claude] access mode select options, and the rows with a localized name on every rule */
|
|
15
|
+
export const useAccessModeOptions = <Row extends AccessModeRow>(
|
|
16
|
+
dataList: MaybeRefOrGetter<Row[]>,
|
|
17
|
+
) => {
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
|
|
20
|
+
const getAccessModeName = (mode: number) => t(`access.accessMode.${mode}`);
|
|
21
|
+
|
|
22
|
+
const accessOptions = computed(() =>
|
|
23
|
+
Object.values(AccessMode).map((mode) => ({
|
|
24
|
+
id: mode,
|
|
25
|
+
name: getAccessModeName(mode),
|
|
26
|
+
})),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const localizedDataList = computed(() =>
|
|
30
|
+
toValue(dataList).map((item) => ({
|
|
31
|
+
...item,
|
|
32
|
+
access: Object.fromEntries(
|
|
33
|
+
Object.entries(item.access).map(([rule, value]) => [
|
|
34
|
+
rule,
|
|
35
|
+
{
|
|
36
|
+
...value,
|
|
37
|
+
name: getAccessModeName(value.id),
|
|
38
|
+
},
|
|
39
|
+
]),
|
|
40
|
+
),
|
|
41
|
+
})),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
accessOptions,
|
|
46
|
+
localizedDataList,
|
|
47
|
+
};
|
|
48
|
+
};
|