@drax/identity-vue 0.5.3 → 0.5.5

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 (34) hide show
  1. package/package.json +6 -5
  2. package/src/combobox/RoleCombobox.vue +24 -19
  3. package/src/combobox/TenantCombobox.vue +11 -9
  4. package/src/components/IdentityChangeOwnPassword/IdentityChangeOwnPassword.vue +4 -4
  5. package/src/components/IdentityProfileDrawer/IdentityProfileDrawer.vue +2 -2
  6. package/src/components/PermissionSelector/PermissionSelector.vue +4 -7
  7. package/src/cruds/role-crud/RoleCrud.ts +91 -0
  8. package/src/cruds/role-crud/RoleForm.vue +116 -0
  9. package/src/cruds/tenant-crud/TenantCrud.ts +17 -3
  10. package/src/cruds/user-crud/PasswordUpdateButton.vue +25 -0
  11. package/src/cruds/user-crud/UserCrud.ts +112 -0
  12. package/src/cruds/user-crud/UserCrud.vue +4 -17
  13. package/src/cruds/user-crud/UserForm.vue +177 -0
  14. package/src/cruds/user-crud/UserPasswordDialog.vue +94 -0
  15. package/src/{forms → cruds/user-crud}/UserPasswordForm.vue +4 -2
  16. package/src/index.ts +7 -21
  17. package/src/pages/ProfilePage.vue +1 -1
  18. package/src/pages/crud/RoleCrudPage.vue +62 -0
  19. package/src/pages/{TenantCrudPage.vue → crud/TenantCrudPage.vue} +1 -1
  20. package/src/pages/{UserApiKeyCrudPage.vue → crud/UserApiKeyCrudPage.vue} +1 -1
  21. package/src/pages/crud/UserCrudPage.vue +61 -0
  22. package/src/routes/IdentityRoutes.ts +70 -64
  23. package/src/cruds/role-crud/RoleCrud.vue +0 -171
  24. package/src/cruds/role-crud/RoleList.vue +0 -129
  25. package/src/cruds/tenant-crud/TenantCrud.vue +0 -168
  26. package/src/cruds/tenant-crud/TenantList.vue +0 -108
  27. package/src/cruds/user-crud/UserList.vue +0 -110
  28. package/src/forms/RoleForm.vue +0 -57
  29. package/src/forms/TenantForm.vue +0 -48
  30. package/src/forms/UserCreateForm.vue +0 -115
  31. package/src/forms/UserEditForm.vue +0 -101
  32. package/src/pages/RoleCrudPage.vue +0 -12
  33. package/src/pages/TenantCrudPageOld.vue +0 -12
  34. package/src/pages/UserCrudPage.vue +0 -12
@@ -1,171 +0,0 @@
1
- <script setup lang="ts">
2
- import {computed, ref} from 'vue'
3
- import RoleList from "./RoleList.vue";
4
- import {useRole} from "../../composables/useRole";
5
- import type {IRole, IRoleBase} from "@drax/identity-share";
6
- import RoleForm from "../../forms/RoleForm.vue";
7
- import RoleView from "../../views/RoleView.vue";
8
- import {useI18n} from "vue-i18n";
9
- const {t} = useI18n()
10
- const {createRole, editRole, deleteRole, loading, roleError, inputErrors} = useRole()
11
-
12
- interface RoleList {
13
- loadItems: () => void;
14
- items: IRole[];
15
- }
16
-
17
- type DialogMode = 'create' | 'edit' | 'delete' | null;
18
-
19
-
20
- let dialog = ref(false);
21
- let dialogMode = ref<DialogMode>(null);
22
- let dialogTitle = ref('');
23
- const roleList = ref<RoleList | null>(null);
24
- let form = ref<IRoleBase>({name: "", permissions: [], childRoles:[], readonly: false})
25
- let target = ref<IRole>();
26
- let targetId = ref<string>('');
27
- let filterEnable = ref(false);
28
-
29
- function cancel() {
30
- dialog.value = false
31
- inputErrors.value = {}
32
- roleError.value = '';
33
- dialogMode.value = null
34
- dialogTitle.value = ''
35
- targetId.value = ''
36
- target.value = undefined
37
- }
38
-
39
- async function save() {
40
-
41
- try {
42
- if (dialogMode.value === 'create') {
43
- await createRole(form.value)
44
- } else if (dialogMode.value === 'edit') {
45
- await editRole(targetId.value, form.value)
46
- } else if (dialogMode.value === 'delete') {
47
- await deleteRole(targetId.value)
48
- }
49
- dialog.value = false
50
- inputErrors.value = {}
51
- roleError.value = '';
52
- if (roleList.value !== null) {
53
- roleList.value.loadItems()
54
- }
55
- } catch (e) {
56
- console.error(e)
57
- if (e instanceof Error) {
58
- roleError.value = e.message
59
- }
60
- }
61
- }
62
-
63
- let buttonText = computed(() => {
64
- switch (dialogMode.value) {
65
- case 'create':
66
- return 'action.create'
67
- case 'edit':
68
- return 'action.update'
69
- case 'delete':
70
- return 'action.delete'
71
- default:
72
- return 'action.sent'
73
- }
74
- })
75
-
76
- function toCreate() {
77
- dialogMode.value = 'create';
78
- dialogTitle.value = 'role.creating';
79
- form.value = {name: "", permissions: [], childRoles:[], readonly: false}
80
- dialog.value = true;
81
- }
82
-
83
- function toEdit(item: IRole) {
84
- dialogMode.value = 'edit';
85
- dialogTitle.value = 'role.updating';
86
- targetId.value = item.id;
87
- form.value = {
88
- name: item.name,
89
- permissions: item.permissions,
90
- childRoles: item.childRoles ? item.childRoles.map(c => c.id) : [],
91
- readonly: item.readonly
92
-
93
- }
94
- dialog.value = true;
95
- }
96
-
97
- function toDelete(item: IRole) {
98
- console.log('toDelete', item)
99
- dialogMode.value = 'delete';
100
- dialogTitle.value = 'role.deleting';
101
- target.value = item
102
- const {id} = item;
103
- targetId.value = id;
104
- dialog.value = true;
105
- }
106
-
107
- </script>
108
-
109
- <template>
110
- <v-container fluid>
111
-
112
- <v-card border rounded>
113
- <v-toolbar class="bg-toolbar">
114
- <v-toolbar-title>{{t('role.managing')}}</v-toolbar-title>
115
- <v-spacer></v-spacer>
116
- <v-btn icon @click="filterEnable = !filterEnable">
117
- <v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
118
- </v-btn>
119
- <v-btn color="primary" @click="toCreate">
120
- {{t('action.create') }}
121
- </v-btn>
122
- </v-toolbar>
123
- <v-theme-provider with-background class="pa-2 rounded-b">
124
- <RoleList
125
- ref="roleList"
126
- @toEdit="toEdit"
127
- @toDelete="toDelete"
128
- :filterEnable="filterEnable"
129
- />
130
- </v-theme-provider>
131
- </v-card>
132
-
133
- <v-dialog v-model="dialog" max-width="800">
134
- <v-sheet border>
135
- <v-toolbar>
136
- <v-toolbar-title>{{ dialogTitle ? t(dialogTitle) : '-' }}</v-toolbar-title>
137
- </v-toolbar>
138
- <v-card class="pa-10">
139
- <v-card-text v-if="roleError">
140
- <v-alert type="error">{{ roleError }}</v-alert>
141
- </v-card-text>
142
- <v-card-text>
143
- <RoleForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
144
- v-model="form"
145
- :inputErrors="inputErrors"
146
- @formSubmit="save"
147
- />
148
- <RoleView v-if="dialogMode === 'delete' && target" :role="target"></RoleView>
149
- </v-card-text>
150
- <v-card-actions>
151
- <v-spacer></v-spacer>
152
- <v-btn variant="text" @click="cancel" :loading="loading">Cancelar</v-btn>
153
- <v-btn variant="flat"
154
- :color="dialogMode==='delete' ? 'red' : 'primary'"
155
- @click="save"
156
- :loading="loading"
157
- >
158
- {{ t(buttonText) }}
159
- </v-btn>
160
- </v-card-actions>
161
-
162
- </v-card>
163
- </v-sheet>
164
- </v-dialog>
165
-
166
- </v-container>
167
- </template>
168
-
169
- <style scoped>
170
-
171
- </style>
@@ -1,129 +0,0 @@
1
- <script setup lang="ts">
2
-
3
- import {defineProps, type Ref, ref} from "vue";
4
- import {useRole} from "../../composables/useRole";
5
- import {useAuth} from "../../composables/useAuth";
6
- import {useI18n} from "vue-i18n";
7
- import type {IRole} from "@drax/identity-share";
8
-
9
- const {hasPermission} = useAuth()
10
- const {paginateRole} = useRole()
11
- const {t,te} = useI18n()
12
-
13
- defineProps({
14
- filterEnable: {
15
- type: Boolean,
16
- default: false,
17
- }
18
- })
19
-
20
- const itemsPerPage = ref(5)
21
- const page = ref(1)
22
- const headers = ref<any>([
23
- //{title: 'ID', align: 'start', sortable: false, key: 'id'},
24
- { title: t('role.name') as string, key: 'name', align: 'start' },
25
- { title: t('role.childRoles') as string, key: 'childRoles', align: 'start' },
26
- { title: t('role.permissions') as string, key: 'permissions', align: 'start' },
27
- { title: t('role.readonly') as string, key: 'readonly', align: 'start' },
28
- { title: '', key: 'actions', align: 'end', minWidth: '150px' },
29
- ])
30
-
31
- const serverItems: Ref<IRole[]> = ref([]);
32
- const totalItems = ref(0)
33
- const loading = ref(false)
34
- const search = ref('')
35
- const sortBy : Ref<any> = ref([])
36
-
37
- async function loadItems(){
38
- try{
39
- loading.value = true
40
- const r = await paginateRole({
41
- page: page.value,
42
- limit: itemsPerPage.value,
43
- orderBy: sortBy.value[0]?.key,
44
- order: sortBy.value[0]?.order,
45
- search: search.value})
46
- serverItems.value = r.items
47
- totalItems.value = r.total
48
- }catch (e){
49
- console.error(e)
50
- }finally {
51
- loading.value = false
52
- }
53
- }
54
-
55
-
56
-
57
- defineExpose({
58
- loadItems
59
- });
60
-
61
- </script>
62
-
63
- <template>
64
- <v-data-table-server
65
- v-if="hasPermission('user:manage')"
66
- v-model:items-per-page="itemsPerPage"
67
- :items-per-page-options="[5, 10, 20, 50]"
68
- v-model:page="page"
69
- v-model:sort-by="sortBy"
70
- :headers="headers"
71
- :items="serverItems"
72
- :items-length="totalItems"
73
- :loading="loading"
74
- :search="search"
75
- item-value="name"
76
- @update:options="loadItems"
77
- >
78
- <template v-slot:top>
79
- <v-toolbar density="compact" v-if="filterEnable">
80
- <v-toolbar-title>{{ t('action.filters') }}</v-toolbar-title>
81
- <v-spacer></v-spacer>
82
- <v-text-field v-model="search" hide-details
83
- density="compact" class="mr-2"
84
- variant="outlined"
85
- append-inner-icon="mdi-magnify"
86
- :label="t('action.search')"
87
- single-line clearable @click:clear="() => search = ''"
88
- />
89
-
90
- </v-toolbar>
91
- </template>
92
- <template v-slot:item.permissions="{ value }" >
93
- <v-chip v-for="permission in value"
94
- :key="permission" color="green"
95
- class="ma-1"
96
- >
97
- {{te(`permission.${permission}`) ? t(`permission.${permission}`) : permission }}
98
- </v-chip>
99
- </template>
100
-
101
- <template v-slot:item.childRoles="{ value }" >
102
- <v-chip v-for="role in value"
103
- :key="role" color="blue"
104
- class="ma-1"
105
- >
106
- {{role.name}}
107
- </v-chip>
108
- </template>
109
-
110
- <template v-slot:item.readonly="{ value }" >
111
- <v-chip v-if="value" color="red" >
112
- <v-icon class="mdi mdi-pencil-off-outline"></v-icon>
113
- </v-chip>
114
- <v-chip v-else color="green">
115
- <v-icon class="mdi mdi-pencil-outline"></v-icon>
116
- </v-chip>
117
- </template>
118
-
119
- <template v-slot:item.actions="{item}" >
120
- <v-btn v-if="hasPermission('role:update')" :disabled="!!item.readonly" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
121
- <v-btn v-if="hasPermission('role:delete')" :disabled="!!item.readonly" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
122
- </template>
123
-
124
- </v-data-table-server>
125
- </template>
126
-
127
- <style scoped>
128
-
129
- </style>
@@ -1,168 +0,0 @@
1
- <script setup lang="ts">
2
- import {computed, ref} from 'vue'
3
- import TenantList from "./TenantList.vue";
4
- import {useTenant} from "../../composables/useTenant";
5
- import type {ITenant, ITenantBase} from "@drax/identity-share";
6
- import TenantForm from "../../forms/TenantForm.vue";
7
- import TenantView from "../../views/TenantView.vue";
8
- import {useI18n} from "vue-i18n";
9
- const {t} = useI18n()
10
- const {createTenant, editTenant, deleteTenant, loading, tenantError, inputErrors} = useTenant()
11
-
12
- interface TenantList {
13
- loadItems: () => void;
14
- items: ITenant[];
15
- }
16
-
17
- type DialogMode = 'create' | 'edit' | 'delete' | null;
18
-
19
-
20
- let dialog = ref(false);
21
- let dialogMode = ref<DialogMode>(null);
22
- let dialogTitle = ref('');
23
- const tenantList = ref<TenantList | null>(null);
24
- let form = ref<ITenantBase>({name: ""})
25
- let target = ref<ITenant>();
26
- let targetId = ref<string>('');
27
- let filterEnable = ref(false);
28
-
29
- function cancel() {
30
- dialog.value = false
31
- inputErrors.value = {}
32
- tenantError.value = '';
33
- dialogMode.value = null
34
- dialogTitle.value = ''
35
- targetId.value = ''
36
- target.value = undefined
37
- }
38
-
39
- async function save() {
40
-
41
- try {
42
- if (dialogMode.value === 'create') {
43
- await createTenant(form.value)
44
- } else if (dialogMode.value === 'edit') {
45
- await editTenant(targetId.value, form.value)
46
- } else if (dialogMode.value === 'delete') {
47
- await deleteTenant(targetId.value)
48
- }
49
- dialog.value = false
50
- inputErrors.value = {}
51
- tenantError.value = '';
52
- if (tenantList.value !== null) {
53
- tenantList.value.loadItems()
54
- }
55
- } catch (e) {
56
- console.error(e)
57
- if (e instanceof Error) {
58
- tenantError.value = e.message
59
- }
60
- }
61
- }
62
-
63
- let buttonText = computed(() => {
64
- switch (dialogMode.value) {
65
- case 'create':
66
- return 'action.create'
67
- case 'edit':
68
- return 'action.update'
69
- case 'delete':
70
- return 'action.delete'
71
- default:
72
- return 'action.sent'
73
- }
74
- })
75
-
76
- function toCreate() {
77
- dialogMode.value = 'create';
78
- dialogTitle.value = 'tenant.creating';
79
- form.value = {name: ""}
80
- dialog.value = true;
81
- }
82
-
83
- function toEdit(item: ITenant) {
84
- console.log('toEdit', item)
85
- dialogMode.value = 'edit';
86
- dialogTitle.value = 'tenant.updating';
87
- targetId.value = item.id;
88
- form.value = {
89
- name: item.name
90
- }
91
- dialog.value = true;
92
- }
93
-
94
- function toDelete(item: ITenant) {
95
- console.log('toDelete', item)
96
- dialogMode.value = 'delete';
97
- dialogTitle.value = 'tenant.deleting';
98
- target.value = item
99
- const {id} = item;
100
- targetId.value = id;
101
- dialog.value = true;
102
- }
103
-
104
- </script>
105
-
106
- <template>
107
- <v-container fluid>
108
-
109
- <v-card border rounded >
110
- <v-toolbar class="bg-toolbar">
111
- <v-toolbar-title>{{ t('tenant.managing') }}</v-toolbar-title>
112
- <v-spacer></v-spacer>
113
- <v-btn icon @click="filterEnable = !filterEnable">
114
- <v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
115
- </v-btn>
116
- <v-btn class="font-weight-bold" color="primary" @click="toCreate">
117
- {{ t('action.create') }}
118
- </v-btn>
119
- </v-toolbar>
120
- <v-theme-provider with-background class="pa-2 rounded-b">
121
- <TenantList
122
- ref="tenantList"
123
- @toEdit="toEdit"
124
- @toDelete="toDelete"
125
- :filterEnable="filterEnable"
126
- />
127
- </v-theme-provider>
128
- </v-card>
129
-
130
- <v-dialog v-model="dialog" max-width="800">
131
- <v-sheet border>
132
- <v-toolbar>
133
- <v-toolbar-title>{{ dialogTitle ? t(dialogTitle) : '-' }}</v-toolbar-title>
134
- </v-toolbar>
135
- <v-card class="pa-10">
136
- <v-card-text v-if="tenantError">
137
- <v-alert type="error">{{ tenantError }}</v-alert>
138
- </v-card-text>
139
- <v-card-text>
140
- <TenantForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
141
- v-model="form"
142
- :inputErrors="inputErrors"
143
- @formSubmit="save"
144
- />
145
- <TenantView v-if="dialogMode === 'delete' && target" :tenant="target"></TenantView>
146
- </v-card-text>
147
- <v-card-actions>
148
- <v-spacer></v-spacer>
149
- <v-btn variant="text" @click="cancel" :loading="loading">Cancelar</v-btn>
150
- <v-btn variant="flat"
151
- :color="dialogMode==='delete' ? 'red' : 'primary'"
152
- @click="save"
153
- :loading="loading"
154
- >
155
- {{ t(buttonText) }}
156
- </v-btn>
157
- </v-card-actions>
158
-
159
- </v-card>
160
- </v-sheet>
161
- </v-dialog>
162
-
163
- </v-container>
164
- </template>
165
-
166
- <style scoped>
167
-
168
- </style>
@@ -1,108 +0,0 @@
1
- <script setup lang="ts">
2
- import {defineProps, type Ref, ref} from "vue";
3
- import {useTenant} from "../../composables/useTenant";
4
- import {useAuth} from "../../composables/useAuth";
5
- import {useI18n} from "vue-i18n";
6
- import type {ITenant} from "@drax/identity-share";
7
- import {formatDateTime} from "@drax/common-front";
8
-
9
- const {hasPermission} = useAuth()
10
- const {paginateTenant} = useTenant()
11
- const {t} = useI18n()
12
-
13
- defineProps({
14
- filterEnable: {
15
- type: Boolean,
16
- default: false,
17
- }
18
- })
19
-
20
- const itemsPerPage = ref(5)
21
- const page = ref(1)
22
- const serverItems: Ref<ITenant[]> = ref([]);
23
- const totalItems = ref(0)
24
- const loading = ref(false)
25
- const search = ref('')
26
- const sortBy : Ref<any> = ref([])
27
-
28
- const headers = ref<any>([
29
- { title: t('tenant.name') as string, key: 'name', align: 'start' },
30
- { title: t('tenant.createdAt') as string, key: 'createdAt', align: 'start' },
31
- { title: '', key: 'actions', align: 'end', minWidth: '150px' },
32
- ])
33
-
34
-
35
- async function loadItems(){
36
- try{
37
- loading.value = true
38
- const r = await paginateTenant({
39
- page: page.value,
40
- limit: itemsPerPage.value,
41
- orderBy: sortBy.value[0]?.key,
42
- order: sortBy.value[0]?.order,
43
- search: search.value})
44
- serverItems.value = r.items
45
- totalItems.value = r.total
46
- }catch (e){
47
- console.error(e)
48
- }finally {
49
- loading.value = false
50
- }
51
- }
52
-
53
-
54
-
55
- defineExpose({
56
- loadItems
57
- });
58
-
59
- </script>
60
-
61
- <template>
62
- <v-data-table-server
63
- class="border"
64
- v-if="hasPermission('user:manage')"
65
- v-model:items-per-page="itemsPerPage"
66
- :items-per-page-options="[5, 10, 20, 50]"
67
- v-model:page="page"
68
- v-model:sort-by="sortBy"
69
- :headers="headers"
70
- :items="serverItems"
71
- :items-length="totalItems"
72
- :loading="loading"
73
- :search="search"
74
- :multi-sort="false"
75
- item-value="name"
76
- @update:options="loadItems"
77
- >
78
- <template v-slot:top>
79
- <v-toolbar density="compact" v-if="filterEnable">
80
- <v-toolbar-title>{{ t('action.filters') }}</v-toolbar-title>
81
- <v-spacer></v-spacer>
82
- <v-text-field v-model="search" hide-details
83
- density="compact" class="mr-2"
84
- variant="outlined"
85
- append-inner-icon="mdi-magnify"
86
- :label="t('action.search')"
87
- single-line clearable @click:clear="() => search = ''"
88
- />
89
-
90
- </v-toolbar>
91
- </template>
92
-
93
- <template v-slot:item.createdAt="{ value }" >
94
- {{formatDateTime(value)}}
95
- </template>
96
-
97
-
98
- <template v-slot:item.actions="{item}" >
99
- <v-btn v-if="hasPermission('tenant:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
100
- <v-btn v-if="hasPermission('tenant:delete')" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
101
- </template>
102
-
103
- </v-data-table-server>
104
- </template>
105
-
106
- <style scoped>
107
-
108
- </style>
@@ -1,110 +0,0 @@
1
- <script setup lang="ts">
2
-
3
- import {ref, defineProps, type Ref} from "vue";
4
- import {useUser} from "../../composables/useUser";
5
- import {useAuth} from "../../composables/useAuth";
6
- import {useI18n} from "vue-i18n";
7
- import type {IUser} from "@drax/identity-share";
8
-
9
- const {hasPermission} = useAuth()
10
- const {paginateUser} = useUser()
11
- const {t} = useI18n()
12
-
13
- defineProps({
14
- filterEnable: {
15
- type: Boolean,
16
- default: false,
17
- }
18
- })
19
-
20
- const itemsPerPage = ref(5)
21
- const page = ref(1)
22
- const headers = ref<any>([
23
- //{title: 'ID', align: 'start', sortable: false, key: 'id'},
24
- { title: t('user.name') as string, key: 'name', align: 'start' },
25
- { title: t('user.username') as string, key: 'username', align: 'start' },
26
- { title: t('user.email') as string, key: 'email', align: 'start' },
27
- { title: t('user.role') as string, key: 'role.name', align: 'start' },
28
- { title: t('user.tenant') as string, key: 'tenant.name', align: 'start' },
29
- { title: t('user.active') as string, key: 'active', align: 'start' },
30
- { title: '', key: 'actions', align: 'end', fixed:true },
31
- ])
32
-
33
- const serverItems: Ref<IUser[]> = ref([]);
34
- const totalItems = ref(0)
35
- const loading = ref(false)
36
- const search = ref('')
37
- const sortBy : Ref<any> = ref([])
38
-
39
- async function loadItems(){
40
- try{
41
- loading.value = true
42
- const r = await paginateUser({
43
- page: page.value,
44
- limit: itemsPerPage.value,
45
- orderBy: sortBy.value[0]?.key,
46
- order: sortBy.value[0]?.order,
47
- search: search.value})
48
- serverItems.value = r.items
49
- totalItems.value = r.total
50
- }catch (e){
51
- console.error(e)
52
- }finally {
53
- loading.value = false
54
- }
55
- }
56
-
57
-
58
-
59
- defineExpose({
60
- loadItems
61
- });
62
-
63
- </script>
64
-
65
- <template>
66
- <v-data-table-server
67
- v-if="hasPermission('user:manage')"
68
- v-model:items-per-page="itemsPerPage"
69
- v-model:page="page"
70
- v-model:sort-by="sortBy"
71
- :items-per-page-options="[5, 10, 20, 50]"
72
- :headers="headers"
73
- :items="serverItems"
74
- :items-length="totalItems"
75
- :loading="loading"
76
- :search="search"
77
- item-value="name"
78
- @update:options="loadItems"
79
- >
80
- <template v-slot:top>
81
- <v-toolbar density="compact" v-if="filterEnable">
82
- <v-toolbar-title>{{ t('action.filters') }}</v-toolbar-title>
83
- <v-spacer></v-spacer>
84
- <v-text-field v-model="search" hide-details
85
- density="compact" class="mr-2"
86
- variant="outlined"
87
- append-inner-icon="mdi-magnify"
88
- :label="t('action.search')"
89
- single-line clearable @click:clear="() => search = ''"
90
- />
91
- </v-toolbar>
92
- </template>
93
- <template v-slot:item.active="{ value }" >
94
- <v-chip :color="value ? 'green':'red'" >
95
- {{ value ? 'Active' : 'Inactive' }}
96
- </v-chip>
97
- </template>
98
-
99
- <template v-slot:item.actions="{item}" >
100
- <v-btn v-if="hasPermission('user:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
101
- <v-btn v-if="hasPermission('user:update')" icon="mdi-lock" variant="text" color="orange" @click="$emit('toChangePassword', item)"></v-btn>
102
- <v-btn v-if="hasPermission('user:delete')" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
103
- </template>
104
-
105
- </v-data-table-server>
106
- </template>
107
-
108
- <style scoped>
109
-
110
- </style>