@drax/identity-vue 0.0.31 → 0.1.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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.0.31",
6
+ "version": "0.1.5",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,10 +24,10 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^0.0.31",
28
- "@drax/common-share": "^0.0.31",
29
- "@drax/common-vue": "^0.0.31",
30
- "@drax/identity-share": "^0.0.31",
27
+ "@drax/common-front": "^0.1.0",
28
+ "@drax/common-share": "^0.1.0",
29
+ "@drax/common-vue": "^0.1.0",
30
+ "@drax/identity-share": "^0.1.0",
31
31
  "vue-i18n": "^9.13.1"
32
32
  },
33
33
  "peerDependencies": {
@@ -64,5 +64,5 @@
64
64
  "vue-tsc": "^2.0.11",
65
65
  "vuetify": "^3.6.4"
66
66
  },
67
- "gitHead": "14a8c465526ef0edc3179be38fb0aaa081a4ebc4"
67
+ "gitHead": "b9efcbc5785e3a02dbb6ca0255527ee2be1d16c0"
68
68
  }
@@ -0,0 +1,81 @@
1
+ import {inject, ref} from "vue";
2
+ import type {IUserApiKey, IUserApiKeyBase} from "@drax/identity-share";
3
+ import type { UserApiKeySystem} from "@drax/identity-front";
4
+ import {ClientError} from "@drax/common-front";
5
+ import type { IClientInputError} from "@drax/common-front";
6
+
7
+
8
+ export function useUserApiKey() {
9
+
10
+ const userApiKeySystem = inject('UserApiKeySystem') as UserApiKeySystem
11
+
12
+ let userApiKeyError = ref<string>('')
13
+ let inputErrors = ref<IClientInputError>()
14
+ let loading = ref(false);
15
+
16
+
17
+ async function paginateUserApiKey({page= 1, limit= 5, orderBy="", orderDesc=false, search = ""}) {
18
+ loading.value = true
19
+ let paginateduserApiKey = userApiKeySystem.paginate({page, limit, orderBy, orderDesc, search})
20
+ loading.value = false
21
+ return paginateduserApiKey
22
+ }
23
+
24
+ async function createUserApiKey(userApiKeyData: IUserApiKeyBase) {
25
+ try {
26
+ loading.value = true
27
+ let userApiKey: IUserApiKey = await userApiKeySystem.create(userApiKeyData)
28
+ return userApiKey
29
+ } catch (err) {
30
+ if (err instanceof ClientError) {
31
+ inputErrors.value = err.inputErrors
32
+ }if(err instanceof Error) {
33
+ userApiKeyError.value = err.message
34
+ }
35
+ throw err
36
+ }finally {
37
+ loading.value = false
38
+ }
39
+
40
+ }
41
+
42
+ async function editUserApiKey(id: string, userApiKeyData: IUserApiKeyBase) {
43
+ try {
44
+ loading.value = true
45
+ let userApiKey: IUserApiKey = await userApiKeySystem.update(id, userApiKeyData)
46
+ return userApiKey
47
+ } catch (err) {
48
+
49
+ if (err instanceof ClientError) {
50
+ inputErrors.value = err.inputErrors
51
+ }
52
+ if(err instanceof Error) {
53
+ userApiKeyError.value = err.message
54
+ }
55
+ throw err
56
+ }finally {
57
+ loading.value = false
58
+ }
59
+ }
60
+
61
+ async function deleteUserApiKey(id: string) {
62
+ try {
63
+ loading.value = true
64
+ await userApiKeySystem.delete(id)
65
+ } catch (err) {
66
+ console.log("composable delete error: ", err, )
67
+ if (err instanceof ClientError) {
68
+ inputErrors.value = err.inputErrors
69
+ }
70
+ if(err instanceof Error) {
71
+ userApiKeyError.value = err.message
72
+ }
73
+ throw err
74
+ }finally {
75
+ loading.value = false
76
+ }
77
+ }
78
+
79
+ return {paginateUserApiKey, createUserApiKey, editUserApiKey, deleteUserApiKey, loading, userApiKeyError, inputErrors}
80
+
81
+ }
@@ -5,6 +5,7 @@ import {useTenant} from "../../composables/useTenant";
5
5
  import {useAuth} from "../../composables/useAuth";
6
6
  import {useI18n} from "vue-i18n";
7
7
  import type {ITenant} from "@drax/identity-share";
8
+ import dayjs from "dayjs";
8
9
 
9
10
  const {hasPermission} = useAuth()
10
11
  const {paginateTenant} = useTenant()
@@ -25,6 +26,7 @@ const loading = ref(false)
25
26
  const search = ref('')
26
27
  const headers = ref<any>([
27
28
  { title: t('tenant.name') as string, key: 'name', align: 'start' },
29
+ { title: t('tenant.createdAt') as string, key: 'createdAt', align: 'start' },
28
30
  { title: '', key: 'actions', align: 'end', minWidth: '150px' },
29
31
  ])
30
32
 
@@ -81,6 +83,10 @@ defineExpose({
81
83
  </v-toolbar>
82
84
  </template>
83
85
 
86
+ <template v-slot:item.createdAt="{ value }" >
87
+ {{value ? dayjs(value).format('YYYY-MM-DD') : '' }}
88
+ </template>
89
+
84
90
 
85
91
  <template v-slot:item.actions="{item}" >
86
92
  <v-btn v-if="hasPermission('tenant:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
@@ -0,0 +1,203 @@
1
+ <script setup lang="ts">
2
+ import {computed, ref} from 'vue'
3
+ import UserApiKeyList from "./UserApiKeyList.vue";
4
+ import {useUserApiKey} from "../../composables/useUserApiKey";
5
+ import type {IUserApiKey, IUserApiKeyBase} from "@drax/identity-share";
6
+ import {useCopy} from "@drax/common-vue";
7
+ import UserApiKeyForm from "../../forms/UserApiKeyForm.vue";
8
+ import UserApiKeyView from "../../views/UserApiKeyView.vue";
9
+
10
+ const {createUserApiKey, editUserApiKey, deleteUserApiKey, loading, userApiKeyError, inputErrors} = useUserApiKey()
11
+
12
+
13
+ const {copy} = useCopy()
14
+
15
+ interface UserApiKeyList {
16
+ loadItems: () => void;
17
+ items: IUserApiKey[];
18
+ }
19
+
20
+ type DialogMode = 'create' | 'edit' | 'delete' | null;
21
+
22
+
23
+ let dialog = ref(false);
24
+ let success = ref(false);
25
+ let dialogMode = ref<DialogMode>(null);
26
+ let dialogTitle = ref('');
27
+ const userApiKeyList = ref<UserApiKeyList | null>(null);
28
+ let form = ref<IUserApiKeyBase>({name: "", ipv4: [], ipv6: []})
29
+ let target = ref<IUserApiKey>();
30
+ let targetId = ref<string>('');
31
+ let filterEnable = ref(false);
32
+
33
+ let userApiKeyCreated = ref<IUserApiKey>();
34
+
35
+
36
+ function cancel() {
37
+ dialog.value = false
38
+ userApiKeyCreated.value = undefined
39
+ success.value = false
40
+ inputErrors.value = {}
41
+ userApiKeyError.value = '';
42
+ dialogMode.value = null
43
+ dialogTitle.value = ''
44
+ targetId.value = ''
45
+ target.value = undefined
46
+ }
47
+
48
+ async function save() {
49
+
50
+ try {
51
+ if (dialogMode.value === 'create') {
52
+ userApiKeyCreated.value = await createUserApiKey(form.value)
53
+
54
+ } else if (dialogMode.value === 'edit') {
55
+ await editUserApiKey(targetId.value, form.value)
56
+ } else if (dialogMode.value === 'delete') {
57
+ await deleteUserApiKey(targetId.value)
58
+ }
59
+ success.value = true
60
+ inputErrors.value = {}
61
+ userApiKeyError.value = '';
62
+ if (userApiKeyList.value !== null) {
63
+ userApiKeyList.value.loadItems()
64
+ }
65
+ } catch (e) {
66
+ console.error(e)
67
+ if (e instanceof Error) {
68
+ userApiKeyError.value = e.message
69
+ }
70
+ }
71
+ }
72
+
73
+ let buttonText = computed(() => {
74
+ switch (dialogMode.value) {
75
+ case 'create':
76
+ return 'action.create'
77
+ case 'edit':
78
+ return 'action.update'
79
+ case 'delete':
80
+ return 'action.delete'
81
+ default:
82
+ return 'action.sent'
83
+ }
84
+ })
85
+
86
+ function toCreate() {
87
+ dialogMode.value = 'create';
88
+ dialogTitle.value = 'userApiKey.creating';
89
+ form.value = {name: "", ipv4: [], ipv6: []}
90
+ dialog.value = true;
91
+ }
92
+
93
+ function toEdit(item: IUserApiKey) {
94
+ console.log('toEdit', item)
95
+ dialogMode.value = 'edit';
96
+ dialogTitle.value = 'userApiKey.updating';
97
+ targetId.value = item.id;
98
+ form.value = {
99
+ name: item.name,
100
+ ipv4: item.ipv4 ? item.ipv4 : [],
101
+ ipv6: item.ipv6 ? item.ipv6 : [],
102
+ }
103
+ dialog.value = true;
104
+ }
105
+
106
+ function toDelete(item: IUserApiKey) {
107
+ console.log('toDelete', item)
108
+ dialogMode.value = 'delete';
109
+ dialogTitle.value = 'userApiKey.deleting';
110
+ target.value = item
111
+ const {id} = item;
112
+ targetId.value = id;
113
+ dialog.value = true;
114
+ }
115
+
116
+ </script>
117
+
118
+ <template>
119
+ <v-container fluid>
120
+
121
+ <v-card border rounded>
122
+ <v-toolbar class="bg-toolbar">
123
+ <v-toolbar-title>{{ $t('userApiKey.managing') }}</v-toolbar-title>
124
+ <v-spacer></v-spacer>
125
+ <v-btn icon @click="filterEnable = !filterEnable">
126
+ <v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
127
+ </v-btn>
128
+ <v-btn class="font-weight-bold" color="primary" @click="toCreate">
129
+ {{ $t('action.create') }}
130
+ </v-btn>
131
+ </v-toolbar>
132
+ <v-theme-provider with-background class="pa-2 rounded-b">
133
+ <UserApiKeyList
134
+ ref="userApiKeyList"
135
+ @toEdit="toEdit"
136
+ @toDelete="toDelete"
137
+ :filterEnable="filterEnable"
138
+ />
139
+ </v-theme-provider>
140
+ </v-card>
141
+
142
+ <v-dialog v-model="dialog" max-width="800">
143
+ <v-sheet border>
144
+ <v-toolbar>
145
+ <v-toolbar-title>{{ dialogTitle ? $t(dialogTitle) : '-' }}</v-toolbar-title>
146
+ </v-toolbar>
147
+ <v-card class="pa-10">
148
+ <v-card-text v-if="userApiKeyError">
149
+ <v-alert type="error">{{ userApiKeyError }}</v-alert>
150
+ </v-card-text>
151
+ <v-card-text v-if="success">
152
+ <v-alert type="success">{{ $t('action.success') }}</v-alert>
153
+ </v-card-text>
154
+ <v-card-text>
155
+ <UserApiKeyForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
156
+ v-model="form"
157
+ :inputErrors="inputErrors"
158
+ @formSubmit="save"
159
+ />
160
+ <v-text-field
161
+ v-if="userApiKeyCreated"
162
+ label="API KEY"
163
+ v-model="userApiKeyCreated.secret"
164
+ color="success"
165
+ base-color="success"
166
+ variant="outlined"
167
+ @click:append="copy(userApiKeyCreated.secret)"
168
+ :hint="$t('userApiKey.secretWarning')"
169
+ persistent-hint
170
+ >
171
+ <template v-slot:append>
172
+ <v-btn icon class="text-success" @click="copy(userApiKeyCreated.secret)"><v-icon>mdi mdi-content-copy</v-icon></v-btn>
173
+ </template>
174
+
175
+ </v-text-field>
176
+ <UserApiKeyView v-if="dialogMode === 'delete' && target" :userApiKey="target"></UserApiKeyView>
177
+ </v-card-text>
178
+ <v-card-actions>
179
+ <v-spacer></v-spacer>
180
+ <v-btn variant="text" @click="cancel" :loading="loading">
181
+ {{success ? $t('action.close') : $t('action.cancel')}}
182
+ </v-btn>
183
+ <v-btn
184
+ v-if="!success"
185
+ variant="flat"
186
+ :color="dialogMode==='delete' ? 'red' : 'primary'"
187
+ @click="save"
188
+ :loading="loading"
189
+ >
190
+ {{ $t(buttonText) }}
191
+ </v-btn>
192
+ </v-card-actions>
193
+
194
+ </v-card>
195
+ </v-sheet>
196
+ </v-dialog>
197
+
198
+ </v-container>
199
+ </template>
200
+
201
+ <style scoped>
202
+
203
+ </style>
@@ -0,0 +1,113 @@
1
+ <script setup lang="ts">
2
+
3
+ import {defineProps, type Ref, ref} from "vue";
4
+ import {useUserApiKey} from "../../composables/useUserApiKey";
5
+ import {useAuth} from "../../composables/useAuth";
6
+ import {useI18n} from "vue-i18n";
7
+ import type {IUserApiKey} from "@drax/identity-share";
8
+ import dayjs from "dayjs";
9
+
10
+ const {hasPermission} = useAuth()
11
+ const {paginateUserApiKey} = useUserApiKey()
12
+ const {t} = useI18n()
13
+
14
+ defineProps({
15
+ filterEnable: {
16
+ type: Boolean,
17
+ default: false,
18
+ }
19
+ })
20
+
21
+ const itemsPerPage = ref(5)
22
+ const page = ref(1)
23
+ const serverItems: Ref<IUserApiKey[]> = ref([]);
24
+ const totalItems = ref(0)
25
+ const loading = ref(false)
26
+ const search = ref('')
27
+
28
+ const headers = ref<any>([
29
+ ...( hasPermission('userApiKey:view') ? [{ title: t('userApiKey.user') as string, key: 'user.username', align: 'start' }] : []),
30
+ { title: t('userApiKey.name') as string, key: 'name', align: 'start' },
31
+ { title: t('userApiKey.ipv4') as string, key: 'ipv4', align: 'start' },
32
+ { title: t('userApiKey.ipv6') as string, key: 'ipv6', align: 'start' },
33
+ { title: t('userApiKey.createdAt') as string, key: 'createdAt', align: 'start' },
34
+ { title: '', key: 'actions', align: 'end', minWidth: '150px' },
35
+ ])
36
+
37
+ async function loadItems(){
38
+ try{
39
+ loading.value = true
40
+ const r = await paginateUserApiKey({
41
+ page: page.value,
42
+ limit: itemsPerPage.value,
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('userApiKey:manage')"
65
+ v-model:items-per-page="itemsPerPage"
66
+ v-model:page="page"
67
+ :headers="headers"
68
+ :items="serverItems"
69
+ :items-length="totalItems"
70
+ :loading="loading"
71
+ :search="search"
72
+ item-value="name"
73
+ @update:options="loadItems"
74
+ >
75
+ <template v-slot:top>
76
+ <v-toolbar density="compact" v-if="filterEnable">
77
+ <v-toolbar-title>{{ $t('action.filters') }}</v-toolbar-title>
78
+ <v-spacer></v-spacer>
79
+ <v-text-field v-model="search" hide-details
80
+ density="compact" class="mr-2"
81
+ variant="outlined"
82
+ append-inner-icon="mdi-magnify"
83
+ :label="$t('action.search')"
84
+ single-line clearable @click:clear="() => search = ''"
85
+ />
86
+
87
+ </v-toolbar>
88
+ </template>
89
+
90
+ <template v-slot:item.ipv4="{ value }" >
91
+ <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
92
+ </template>
93
+
94
+ <template v-slot:item.ipv6="{ value }" >
95
+ <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
96
+ </template>
97
+
98
+ <template v-slot:item.createdAt="{ value }" >
99
+ {{value ? dayjs(value).format('YYYY-MM-DD') : '' }}
100
+ </template>
101
+
102
+
103
+ <template v-slot:item.actions="{item}" >
104
+ <v-btn v-if="hasPermission('userApiKey:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
105
+ <v-btn v-if="hasPermission('userApiKey:delete')" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
106
+ </template>
107
+
108
+ </v-data-table-server>
109
+ </template>
110
+
111
+ <style scoped>
112
+
113
+ </style>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import {defineModel, type PropType} from "vue";
3
+ import type {IUserApiKeyBase} from "@drax/identity-share";
4
+ import {useI18nValidation} from "@drax/common-vue";
5
+ import type {IClientInputError} from "@drax/common-front";
6
+ import {useI18n} from "vue-i18n";
7
+
8
+ const {$ta} = useI18nValidation()
9
+ const {t} = useI18n()
10
+
11
+ defineProps({
12
+ inputErrors: {
13
+ type: Object as PropType<IClientInputError>,
14
+ default: () => ({name: "", ipv4: "", ipv6: ""})
15
+ }
16
+ })
17
+
18
+ const form = defineModel<IUserApiKeyBase>({
19
+ type: Object,
20
+ default: () => ({name: "", ipv4: [], ipv6: []})
21
+ })
22
+
23
+ // Define emits
24
+ const emits = defineEmits(['formSubmit'])
25
+
26
+ // Function to call when form is attempted to be submitted
27
+ const onSubmit = () => {
28
+ emits('formSubmit', form); // Emitting an event with the form data
29
+ }
30
+
31
+ const ipv6Regex = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/
32
+ const ipv4Regex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
33
+
34
+ const ipv4Address = [
35
+ (v: string[]) => !v.some(ip => !ipv4Regex.test(ip)) || t('validation.invalidIpv4'),
36
+ ]
37
+
38
+ const ipv6Address = [
39
+ (v: string[]) => !v.some(ip => !ipv6Regex.test(ip)) || t('validation.invalidIpv6'),
40
+ ]
41
+
42
+ </script>
43
+
44
+ <template>
45
+ <v-form @submit.prevent="onSubmit" validate-on="blur">
46
+ <v-text-field
47
+ variant="outlined"
48
+ id="name-input"
49
+ :label="$t('userApiKey.name')"
50
+ v-model="form.name"
51
+ prepend-inner-icon="mdi-card-account-details"
52
+ required
53
+ :error-messages="$ta(inputErrors.name)"
54
+ ></v-text-field>
55
+
56
+ <v-combobox
57
+ v-model="form.ipv4 as string[]"
58
+ :label="$t('userApiKey.ipv4')"
59
+ variant="outlined"
60
+ multiple chips
61
+ validate-on="blur"
62
+ :rules="ipv4Address"
63
+ ></v-combobox>
64
+
65
+ <v-combobox
66
+ v-model="form.ipv6 as string[]"
67
+ :label="$t('userApiKey.ipv6')"
68
+ variant="outlined"
69
+ multiple chips
70
+ validate-on="blur"
71
+ :rules="ipv6Address"
72
+ ></v-combobox>
73
+
74
+ </v-form>
75
+ </template>
76
+
77
+ <style scoped>
78
+
79
+ </style>
package/src/index.ts CHANGED
@@ -27,10 +27,18 @@ import TenantCrud from "./cruds/tenant-crud/TenantCrud.vue";
27
27
  import TenantList from "./cruds/tenant-crud/TenantList.vue";
28
28
  import TenantCrudPage from "./pages/TenantCrudPage.vue";
29
29
 
30
+
31
+ import UserApiKeyForm from "./forms/UserApiKeyForm.vue";
32
+ import UserApiKeyView from "./views/UserApiKeyView.vue";
33
+ import UserApiKeyCrud from "./cruds/user-api-key-crud/UserApiKeyCrud.vue";
34
+ import UserApiKeyList from "./cruds/user-api-key-crud/UserApiKeyList.vue";
35
+ import UserApiKeyCrudPage from "./pages/UserApiKeyCrudPage.vue";
36
+
30
37
  import {useAuth} from "./composables/useAuth.js";
31
38
  import {useUser} from "./composables/useUser.js";
32
39
  import {useRole} from "./composables/useRole.js";
33
40
  import {useTenant} from "./composables/useTenant.js";
41
+ import {useUserApiKey} from "./composables/useUserApiKey.js";
34
42
 
35
43
 
36
44
  import {useAuthStore} from "./stores/auth/AuthStore.js";
@@ -77,6 +85,14 @@ export {
77
85
  useTenant,
78
86
 
79
87
 
88
+ //UserApiKey
89
+ UserApiKeyView,
90
+ UserApiKeyForm,
91
+ UserApiKeyCrud,
92
+ UserApiKeyList,
93
+ UserApiKeyCrudPage,
94
+ useUserApiKey,
95
+
80
96
  //Stores
81
97
  useAuthStore,
82
98
 
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+
3
+ import UserApiKeyCrud from "../cruds/user-api-key-crud/UserApiKeyCrud.vue";
4
+ </script>
5
+
6
+ <template>
7
+ <UserApiKeyCrud></UserApiKeyCrud>
8
+ </template>
9
+
10
+ <style scoped>
11
+
12
+ </style>
@@ -4,6 +4,7 @@ import PasswordPage from '../pages/PasswordPage.vue'
4
4
  import UserCrudPage from '../pages/UserCrudPage.vue'
5
5
  import RoleCrudPage from '../pages/RoleCrudPage.vue'
6
6
  import TenantCrudPage from '../pages/TenantCrudPage.vue'
7
+ import UserApiKeyCrudPage from '../pages/UserApiKeyCrudPage.vue'
7
8
 
8
9
  const routes = [
9
10
  {
@@ -57,7 +58,15 @@ const routes = [
57
58
  permission: 'user:manage'
58
59
  }
59
60
  },
60
-
61
+ {
62
+ name: 'CrudUserApiKey',
63
+ path: '/crud/user-api-key',
64
+ component: UserApiKeyCrudPage,
65
+ meta: {
66
+ auth: true,
67
+ permission: 'userApiKey:manage'
68
+ }
69
+ },
61
70
  ]
62
71
 
63
72
 
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from "vue";
3
+ import type {IUserApiKey} from "@drax/identity-share";
4
+
5
+ defineProps({
6
+ userApiKey:{
7
+ type: Object as PropType<IUserApiKey>,
8
+ required: true,
9
+ }
10
+ })
11
+ </script>
12
+
13
+ <template>
14
+ <v-sheet border class="position-relative d-flex justify-center align-center" height="150">
15
+ <v-sheet class="text-center">
16
+ <h6 class="text-h6">{{$t ? $t('userApiKey.name') : 'Name'}} : {{ userApiKey.name }}</h6>
17
+ </v-sheet>
18
+ </v-sheet>
19
+
20
+ </template>
21
+
22
+ <style scoped>
23
+
24
+ </style>