@drax/identity-vue 0.11.3 → 0.11.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.11.3",
6
+ "version": "0.11.5",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,12 +24,12 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^0.11.3",
28
- "@drax/common-vue": "^0.11.3",
27
+ "@drax/common-front": "^0.11.5",
28
+ "@drax/common-vue": "^0.11.5",
29
29
  "@drax/crud-front": "^0.11.3",
30
- "@drax/crud-share": "^0.11.3",
31
- "@drax/crud-vue": "^0.11.3",
32
- "@drax/identity-share": "^0.11.3"
30
+ "@drax/crud-share": "^0.11.5",
31
+ "@drax/crud-vue": "^0.11.5",
32
+ "@drax/identity-share": "^0.11.5"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "pinia": "^2.2.2",
@@ -66,5 +66,5 @@
66
66
  "vue-tsc": "^2.1.6",
67
67
  "vuetify": "^3.7.1"
68
68
  },
69
- "gitHead": "54216d94d68dac488969f9c95d3c6be780935f9d"
69
+ "gitHead": "e67f10f0af29468c9d30f16135cfdbdff166d916"
70
70
  }
@@ -73,7 +73,7 @@ class RoleCrud extends EntityCrud implements IEntityCrud {
73
73
  }
74
74
 
75
75
  get exportHeaders(){
76
- return ['_id', 'name','permissions','childRoles','readonly']
76
+ return ['id', 'name','permissions','childRoles','readonly']
77
77
  }
78
78
 
79
79
  get isExportable(){
@@ -51,7 +51,7 @@ const {
51
51
  <template v-if="!valueModel.readonly || store.operation == 'view'">
52
52
 
53
53
 
54
- <v-card-subtitle v-if="valueModel._id">ID: {{ valueModel._id }}</v-card-subtitle>
54
+ <v-card-subtitle v-if="valueModel.id">ID: {{ valueModel.id }}</v-card-subtitle>
55
55
 
56
56
  <v-card-text v-if="store.error">
57
57
  <v-alert color="error">{{ te(store.error) ? t(store.error) : store.error }}</v-alert>
@@ -70,7 +70,7 @@ class TenantCrud extends EntityCrud implements IEntityCrud {
70
70
  }
71
71
 
72
72
  get exportHeaders(){
73
- return ['_id', 'name']
73
+ return ['id', 'name']
74
74
  }
75
75
 
76
76
  get isExportable(){
@@ -81,6 +81,10 @@ class TenantCrud extends EntityCrud implements IEntityCrud {
81
81
  return false
82
82
  }
83
83
 
84
+ get dialogMaxWidth(){
85
+ return '800'
86
+ }
87
+
84
88
  }
85
89
 
86
90
  export default TenantCrud
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ import {useCopy} from "@drax/common-vue";
3
+ import {useI18n} from "vue-i18n";
4
+ import {defineModel, type PropType} from "vue";
5
+ import type {IUserApiKey} from "@drax/identity-share";
6
+
7
+ const {t} = useI18n()
8
+
9
+ const {copy} = useCopy()
10
+
11
+ defineProps({
12
+ userApiKey: {type: Object as PropType<IUserApiKey>, required: true},
13
+ })
14
+
15
+ const dialog = defineModel<boolean>({
16
+ type: Boolean
17
+ })
18
+
19
+ </script>
20
+
21
+ <template>
22
+
23
+ <v-dialog v-model="dialog" max-width="800" persistent>
24
+ <v-sheet border>
25
+ <v-toolbar>
26
+ <v-toolbar-title>{{t('userapikey.created')}}</v-toolbar-title>
27
+ </v-toolbar>
28
+ <v-card>
29
+
30
+ <v-card-title class="my-3 text-center">{{userApiKey.name}}</v-card-title>
31
+
32
+ <v-card-text>
33
+ <v-text-field
34
+ label="API KEY"
35
+ v-model="userApiKey.secret"
36
+ color="success"
37
+ base-color="success"
38
+ variant="outlined"
39
+ @click:append="copy(userApiKey.secret)"
40
+ :hint="t('userapikey.secretWarning')"
41
+ persistent-hint readonly
42
+ >
43
+ <template v-slot:append>
44
+ <v-btn icon class="text-success" @click="copy(userApiKey.secret)">
45
+ <v-icon>mdi mdi-content-copy</v-icon>
46
+ </v-btn>
47
+ </template>
48
+
49
+ </v-text-field>
50
+ </v-card-text>
51
+ <v-card-actions>
52
+ <v-spacer></v-spacer>
53
+ <v-btn @click="dialog = false">{{ t('action.close') }}</v-btn>
54
+ </v-card-actions>
55
+ </v-card>
56
+ </v-sheet>
57
+ </v-dialog>
58
+
59
+ </template>
60
+
61
+ <style scoped>
62
+
63
+ </style>
@@ -0,0 +1,94 @@
1
+
2
+ import {EntityCrud} from "@drax/crud-vue";
3
+ import {UserApiKeySystemFactory} from "@drax/identity-front";
4
+ import type {IEntityCrud, IEntityCrudField, IEntityCrudFilter, IEntityCrudHeader} from "@drax/crud-share";
5
+
6
+
7
+ class UserApiKeyCrud extends EntityCrud implements IEntityCrud {
8
+
9
+ static singleton: UserApiKeyCrud
10
+
11
+ constructor() {
12
+ super();
13
+ this.name = 'userApiKey'
14
+ }
15
+
16
+ static get instance(): UserApiKeyCrud {
17
+ if(!UserApiKeyCrud.singleton){
18
+ UserApiKeyCrud.singleton = new UserApiKeyCrud()
19
+ }
20
+ return UserApiKeyCrud.singleton
21
+ }
22
+
23
+ get permissions(){
24
+ return {
25
+ manage: 'userApiKey:manage',
26
+ view: 'userApiKey:viewMy',
27
+ create: 'userApiKey:createMy',
28
+ update: 'userApiKey:update',
29
+ delete: 'userApiKey:delete'
30
+ }
31
+ }
32
+
33
+ get headers():IEntityCrudHeader[] {
34
+ return [
35
+ //{title: 'id',key:'_id', align: 'start'},
36
+ {title: 'user',key:'user', align: 'start'},
37
+ {title: 'name',key:'name', align: 'start'},
38
+ {title: 'ipv4',key:'ipv4', align: 'start'},
39
+ {title: 'ipv6',key:'ipv6', align: 'start'},
40
+ // {title: 'createdBy',key:'createdBy', align: 'start'},
41
+ {title: 'createdAt',key:'createdAt', align: 'start'},
42
+ ]
43
+ }
44
+
45
+ get provider(){
46
+ return UserApiKeySystemFactory.getInstance()
47
+ }
48
+
49
+ get refs(){
50
+ return {
51
+
52
+ }
53
+ }
54
+
55
+ get rules(){
56
+ return {
57
+ name: [(v: any) => !!v || 'Requerido']
58
+ }
59
+ }
60
+
61
+ get fields(): IEntityCrudField[]{
62
+ return [
63
+ {name: 'name', type: 'string', label: 'name', default:'', prependInnerIcon:'mdi-text-short' },
64
+ {name: 'ipv4', type: 'string', label: 'name', default:[], prependInnerIcon:'mdi-text-short' },
65
+ {name: 'ipv6', type: 'string', label: 'name', default:[], prependInnerIcon:'mdi-text-short' },
66
+ ]
67
+ }
68
+
69
+ get filters():IEntityCrudFilter[]{
70
+ return [
71
+
72
+ ]
73
+ }
74
+
75
+ get dialogFullscreen(){
76
+ return false
77
+ }
78
+
79
+ get exportHeaders(){
80
+ return ['id', 'name']
81
+ }
82
+
83
+ get isExportable(){
84
+ return false
85
+ }
86
+
87
+ get isImportable(){
88
+ return false
89
+ }
90
+
91
+ }
92
+
93
+ export default UserApiKeyCrud
94
+ export { UserApiKeyCrud }
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import {useFormUtils, useCrudStore} from "@drax/crud-vue";
3
+ import {defineEmits, defineModel, ref} from "vue";
4
+ import {useI18nValidation} from "@drax/common-vue";
5
+ import PermissionSelector from "../../components/PermissionSelector/PermissionSelector.vue";
6
+ import RoleCombobox from "../../combobox/RoleCombobox.vue";
7
+ import {useI18n} from "vue-i18n";
8
+
9
+ const {$ta} = useI18nValidation()
10
+ const {t, te} = useI18n()
11
+
12
+ const valueModel = defineModel({type: [Object]})
13
+
14
+ const emit = defineEmits(['submit', 'cancel'])
15
+
16
+ const store = useCrudStore()
17
+
18
+ const valid = ref()
19
+ const formRef = ref()
20
+
21
+
22
+ async function submit() {
23
+ store.resetErrors()
24
+ await formRef.value.validate()
25
+ if (valid.value) {
26
+ emit('submit', valueModel.value)
27
+ } else {
28
+ console.log('Invalid form')
29
+ }
30
+ }
31
+
32
+ function cancel() {
33
+ emit('cancel')
34
+ }
35
+
36
+ const {
37
+ variant, submitColor, readonly
38
+ } = useFormUtils(store.operation)
39
+
40
+
41
+ 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]))/
42
+ const ipv4Regex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
43
+
44
+ const ipv4Address = [
45
+ (v: string[]) => !v.some(ip => !ipv4Regex.test(ip)) || t('validation.invalidIpv4'),
46
+ ]
47
+
48
+ const ipv6Address = [
49
+ (v: string[]) => !v.some(ip => !ipv6Regex.test(ip)) || t('validation.invalidIpv6'),
50
+ ]
51
+
52
+ </script>
53
+
54
+ <template>
55
+
56
+
57
+ <v-form v-model="valid" ref="formRef" @submit.prevent="submit">
58
+ <v-card flat>
59
+
60
+ <template v-if="!valueModel.readonly || store.operation == 'view'">
61
+
62
+
63
+ <v-card-subtitle v-if="valueModel._id">ID: {{ valueModel._id }}</v-card-subtitle>
64
+
65
+ <v-card-text v-if="store.error">
66
+ <v-alert color="error">{{ te(store.error) ? t(store.error) : store.error }}</v-alert>
67
+ </v-card-text>
68
+
69
+ <v-card-text>
70
+ <v-text-field
71
+ variant="outlined"
72
+ id="name-input"
73
+ :label="t('userapikey.field.name')"
74
+ v-model="valueModel.name"
75
+ prepend-inner-icon="mdi-card-account-details"
76
+ required
77
+ :error-messages="$ta(store.inputErrors?.name)"
78
+ ></v-text-field>
79
+
80
+ <v-combobox
81
+ v-model="valueModel.ipv4 as string[]"
82
+ :label="t('userapikey.field.ipv4')"
83
+ variant="outlined"
84
+ multiple chips
85
+ validate-on="blur"
86
+ :rules="ipv4Address"
87
+ :error-messages="$ta(store.inputErrors?.ipv4)"
88
+ ></v-combobox>
89
+
90
+ <v-combobox
91
+ v-model="valueModel.ipv6 as string[]"
92
+ :label="t('userapikey.field.ipv6')"
93
+ variant="outlined"
94
+ multiple chips
95
+ validate-on="blur"
96
+ :rules="ipv6Address"
97
+ :error-messages="$ta(store.inputErrors?.ipv6)"
98
+ ></v-combobox>
99
+
100
+
101
+ </v-card-text>
102
+ </template>
103
+
104
+ <v-card-actions>
105
+ <v-spacer></v-spacer>
106
+ <v-btn
107
+ variant="text"
108
+ color="grey"
109
+ @click="cancel">
110
+ {{ t('action.cancel') }}
111
+ </v-btn>
112
+ <v-btn
113
+ v-if="!valueModel.readonly && store.operation != 'view'"
114
+ variant="flat"
115
+ :color="submitColor"
116
+ @click="submit"
117
+ >
118
+ {{ store.operation ? t('action.' + store.operation) : t('action.sent') }}
119
+ </v-btn>
120
+ </v-card-actions>
121
+ </v-card>
122
+ </v-form>
123
+ </template>
124
+
125
+ <style scoped>
126
+
127
+ </style>
@@ -92,7 +92,7 @@ class UserCrud extends EntityCrud implements IEntityCrud {
92
92
  }
93
93
 
94
94
  get exportHeaders(){
95
- return ['_id', 'name','username','email','phone','role.name','tenant.name','active']
95
+ return ['id', 'name','username','email','phone','role.name','tenant.name','active']
96
96
  }
97
97
 
98
98
  get exportFormats(){
package/src/index.ts CHANGED
@@ -19,10 +19,9 @@ import TenantView from "./views/TenantView.vue";
19
19
  import TenantCrudPage from "./pages/crud/TenantCrudPage.vue";
20
20
 
21
21
 
22
- import UserApiKeyForm from "./forms/UserApiKeyForm.vue";
22
+ import UserApiKeyForm from "./cruds/user-api-key-crud/UserApiKeyForm.vue";
23
+ import UserApiKeyCreated from "./cruds/user-api-key-crud/UserApiKeyCreated.vue";
23
24
  import UserApiKeyView from "./views/UserApiKeyView.vue";
24
- import UserApiKeyCrud from "./cruds/user-api-key-crud/UserApiKeyCrud.vue";
25
- import UserApiKeyList from "./cruds/user-api-key-crud/UserApiKeyList.vue";
26
25
  import UserApiKeyCrudPage from "./pages/crud/UserApiKeyCrudPage.vue";
27
26
 
28
27
  import {useAuth} from "./composables/useAuth.js";
@@ -83,8 +82,7 @@ export {
83
82
  //UserApiKey
84
83
  UserApiKeyView,
85
84
  UserApiKeyForm,
86
- UserApiKeyCrud,
87
- UserApiKeyList,
85
+ UserApiKeyCreated,
88
86
  UserApiKeyCrudPage,
89
87
  useUserApiKey,
90
88
 
@@ -1,10 +1,79 @@
1
1
  <script setup lang="ts">
2
2
 
3
- import UserApiKeyCrud from "../../cruds/user-api-key-crud/UserApiKeyCrud.vue";
3
+ import UserApiKeyCrud from "../../cruds/user-api-key-crud/UserApiKeyCrud";
4
+ import UserApiKeyForm from "../../cruds/user-api-key-crud/UserApiKeyForm.vue";
5
+ import {Crud, useCrud} from "@drax/crud-vue";
6
+ import type {IUserApiKey} from "@drax/identity-share";
7
+ import {formatDateTime} from "@drax/common-front";
8
+ import UserApiKeyCreated from "../../cruds/user-api-key-crud/UserApiKeyCreated.vue";
9
+ import {ref} from "vue";
10
+
11
+ const {
12
+ onCancel, onSubmit,form
13
+ } = useCrud(UserApiKeyCrud.instance);
14
+
15
+ const userApiKeyCreated = ref<IUserApiKey>();
16
+ const userApiKeyCreatedDialog = ref<boolean>(false);
17
+
18
+ async function submit() {
19
+ let result = await onSubmit(form.value)
20
+ if(result.status === 'created'){
21
+ onCreated(result.item);
22
+ }
23
+ }
24
+
25
+ function onCreated(item:IUserApiKey) {
26
+ console.log("User API Key created:", item);
27
+ userApiKeyCreated.value = item;
28
+ userApiKeyCreatedDialog.value = true;
29
+ }
30
+
4
31
  </script>
5
32
 
6
33
  <template>
7
- <UserApiKeyCrud></UserApiKeyCrud>
34
+ <div>
35
+ <user-api-key-created
36
+ v-if="userApiKeyCreated"
37
+ :user-api-key="userApiKeyCreated"
38
+ v-model="userApiKeyCreatedDialog"
39
+ />
40
+
41
+ <crud :entity="UserApiKeyCrud.instance" @created="onCreated">
42
+
43
+ <template v-slot:form>
44
+ <user-api-key-form
45
+ v-model="form"
46
+ @submit="submit"
47
+ @cancel="onCancel"
48
+ />
49
+ </template>
50
+
51
+ <template v-slot:item.ipv4="{ value }" >
52
+ <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
53
+ </template>
54
+
55
+ <template v-slot:item.ipv6="{ value }" >
56
+ <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
57
+ </template>
58
+
59
+ <template v-slot:item.createdAt="{ value }" >
60
+ {{formatDateTime(value)}}
61
+ </template>
62
+
63
+ <template v-slot:item.createdBy="{ value }" >
64
+ {{value ? value.name : 'Unknown' }}
65
+ </template>
66
+
67
+
68
+ <template v-slot:item.user="{ value }" >
69
+ {{value.username }}
70
+ </template>
71
+
72
+ </crud>
73
+
74
+ </div>
75
+
76
+
8
77
  </template>
9
78
 
10
79
  <style scoped>
@@ -1,204 +0,0 @@
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
- import {useI18n} from "vue-i18n";
10
- const {t} = useI18n()
11
- const {createUserApiKey, editUserApiKey, deleteUserApiKey, loading, userApiKeyError, inputErrors} = useUserApiKey()
12
-
13
-
14
- const {copy} = useCopy()
15
-
16
- interface UserApiKeyList {
17
- loadItems: () => void;
18
- items: IUserApiKey[];
19
- }
20
-
21
- type DialogMode = 'create' | 'edit' | 'delete' | null;
22
-
23
-
24
- let dialog = ref(false);
25
- let success = ref(false);
26
- let dialogMode = ref<DialogMode>(null);
27
- let dialogTitle = ref('');
28
- const userApiKeyList = ref<UserApiKeyList | null>(null);
29
- let form = ref<IUserApiKeyBase>({name: "", ipv4: [], ipv6: []})
30
- let target = ref<IUserApiKey>();
31
- let targetId = ref<string>('');
32
- let filterEnable = ref(false);
33
-
34
- let userApiKeyCreated = ref<IUserApiKey>();
35
-
36
-
37
- function cancel() {
38
- dialog.value = false
39
- userApiKeyCreated.value = undefined
40
- success.value = false
41
- inputErrors.value = {}
42
- userApiKeyError.value = '';
43
- dialogMode.value = null
44
- dialogTitle.value = ''
45
- targetId.value = ''
46
- target.value = undefined
47
- }
48
-
49
- async function save() {
50
-
51
- try {
52
- if (dialogMode.value === 'create') {
53
- userApiKeyCreated.value = await createUserApiKey(form.value)
54
-
55
- } else if (dialogMode.value === 'edit') {
56
- await editUserApiKey(targetId.value, form.value)
57
- } else if (dialogMode.value === 'delete') {
58
- await deleteUserApiKey(targetId.value)
59
- }
60
- success.value = true
61
- inputErrors.value = {}
62
- userApiKeyError.value = '';
63
- if (userApiKeyList.value !== null) {
64
- userApiKeyList.value.loadItems()
65
- }
66
- } catch (e) {
67
- console.error(e)
68
- if (e instanceof Error) {
69
- userApiKeyError.value = e.message
70
- }
71
- }
72
- }
73
-
74
- let buttonText = computed(() => {
75
- switch (dialogMode.value) {
76
- case 'create':
77
- return 'action.create'
78
- case 'edit':
79
- return 'action.update'
80
- case 'delete':
81
- return 'action.delete'
82
- default:
83
- return 'action.sent'
84
- }
85
- })
86
-
87
- function toCreate() {
88
- dialogMode.value = 'create';
89
- dialogTitle.value = 'userApiKey.creating';
90
- form.value = {name: "", ipv4: [], ipv6: []}
91
- dialog.value = true;
92
- }
93
-
94
- function toEdit(item: IUserApiKey) {
95
- console.log('toEdit', item)
96
- dialogMode.value = 'edit';
97
- dialogTitle.value = 'userApiKey.updating';
98
- targetId.value = item.id;
99
- form.value = {
100
- name: item.name,
101
- ipv4: item.ipv4 ? item.ipv4 : [],
102
- ipv6: item.ipv6 ? item.ipv6 : [],
103
- }
104
- dialog.value = true;
105
- }
106
-
107
- function toDelete(item: IUserApiKey) {
108
- console.log('toDelete', item)
109
- dialogMode.value = 'delete';
110
- dialogTitle.value = 'userApiKey.deleting';
111
- target.value = item
112
- const {id} = item;
113
- targetId.value = id;
114
- dialog.value = true;
115
- }
116
-
117
- </script>
118
-
119
- <template>
120
- <v-container fluid>
121
-
122
- <v-card border rounded>
123
- <v-toolbar class="bg-toolbar">
124
- <v-toolbar-title>{{ t('userApiKey.managing') }}</v-toolbar-title>
125
- <v-spacer></v-spacer>
126
- <v-btn icon @click="filterEnable = !filterEnable">
127
- <v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
128
- </v-btn>
129
- <v-btn class="font-weight-bold" color="primary" @click="toCreate">
130
- {{ t('action.create') }}
131
- </v-btn>
132
- </v-toolbar>
133
- <v-theme-provider with-background class="pa-2 rounded-b">
134
- <UserApiKeyList
135
- ref="userApiKeyList"
136
- @toEdit="toEdit"
137
- @toDelete="toDelete"
138
- :filterEnable="filterEnable"
139
- />
140
- </v-theme-provider>
141
- </v-card>
142
-
143
- <v-dialog v-model="dialog" max-width="800">
144
- <v-sheet border>
145
- <v-toolbar>
146
- <v-toolbar-title>{{ dialogTitle ? t(dialogTitle) : '-' }}</v-toolbar-title>
147
- </v-toolbar>
148
- <v-card class="pa-10">
149
- <v-card-text v-if="userApiKeyError">
150
- <v-alert type="error">{{ userApiKeyError }}</v-alert>
151
- </v-card-text>
152
- <v-card-text v-if="success">
153
- <v-alert type="success">{{ t('action.success') }}</v-alert>
154
- </v-card-text>
155
- <v-card-text>
156
- <UserApiKeyForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
157
- v-model="form"
158
- :inputErrors="inputErrors"
159
- @formSubmit="save"
160
- />
161
- <v-text-field
162
- v-if="userApiKeyCreated"
163
- label="API KEY"
164
- v-model="userApiKeyCreated.secret"
165
- color="success"
166
- base-color="success"
167
- variant="outlined"
168
- @click:append="copy(userApiKeyCreated.secret)"
169
- :hint="t('userApiKey.secretWarning')"
170
- persistent-hint
171
- >
172
- <template v-slot:append>
173
- <v-btn icon class="text-success" @click="copy(userApiKeyCreated.secret)"><v-icon>mdi mdi-content-copy</v-icon></v-btn>
174
- </template>
175
-
176
- </v-text-field>
177
- <UserApiKeyView v-if="dialogMode === 'delete' && target" :userApiKey="target"></UserApiKeyView>
178
- </v-card-text>
179
- <v-card-actions>
180
- <v-spacer></v-spacer>
181
- <v-btn variant="text" @click="cancel" :loading="loading">
182
- {{success ? t('action.close') : t('action.cancel')}}
183
- </v-btn>
184
- <v-btn
185
- v-if="!success"
186
- variant="flat"
187
- :color="dialogMode==='delete' ? 'red' : 'primary'"
188
- @click="save"
189
- :loading="loading"
190
- >
191
- {{ t(buttonText) }}
192
- </v-btn>
193
- </v-card-actions>
194
-
195
- </v-card>
196
- </v-sheet>
197
- </v-dialog>
198
-
199
- </v-container>
200
- </template>
201
-
202
- <style scoped>
203
-
204
- </style>
@@ -1,118 +0,0 @@
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 {formatDateTime} from "@drax/common-front";
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
- const sortBy : Ref<any> = ref([])
28
-
29
- const headers = ref<any>([
30
- ...( hasPermission('userApiKey:view') ? [{ title: t('userApiKey.user') as string, key: 'user.username', align: 'start', sortable: false }] : []),
31
- { title: t('userApiKey.name') as string, key: 'name', align: 'start' },
32
- { title: t('userApiKey.ipv4') as string, key: 'ipv4', align: 'start' },
33
- { title: t('userApiKey.ipv6') as string, key: 'ipv6', align: 'start' },
34
- { title: t('userApiKey.createdAt') as string, key: 'createdAt', align: 'start' },
35
- { title: '', key: 'actions', align: 'end', minWidth: '150px' },
36
- ])
37
-
38
- async function loadItems(){
39
- try{
40
- loading.value = true
41
- const r = await paginateUserApiKey({
42
- page: page.value,
43
- limit: itemsPerPage.value,
44
- orderBy: sortBy.value[0]?.key,
45
- order: sortBy.value[0]?.order,
46
- search: search.value})
47
- serverItems.value = r.items
48
- totalItems.value = r.total
49
- }catch (e){
50
- console.error(e)
51
- }finally {
52
- loading.value = false
53
- }
54
- }
55
-
56
-
57
-
58
- defineExpose({
59
- loadItems
60
- });
61
-
62
- </script>
63
-
64
- <template>
65
- <v-data-table-server
66
- class="border"
67
- v-if="hasPermission('userApiKey:manage')"
68
- v-model:items-per-page="itemsPerPage"
69
- :items-per-page-options="[5, 10, 20, 50]"
70
- v-model:page="page"
71
- v-model:sort-by="sortBy"
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
-
92
- </v-toolbar>
93
- </template>
94
-
95
- <template v-slot:item.ipv4="{ value }" >
96
- <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
97
- </template>
98
-
99
- <template v-slot:item.ipv6="{ value }" >
100
- <v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
101
- </template>
102
-
103
- <template v-slot:item.createdAt="{ value }" >
104
- {{formatDateTime(value)}}
105
- </template>
106
-
107
-
108
- <template v-slot:item.actions="{item}" >
109
- <v-btn v-if="hasPermission('userApiKey:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
110
- <v-btn v-if="hasPermission('userApiKey:delete')" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
111
- </template>
112
-
113
- </v-data-table-server>
114
- </template>
115
-
116
- <style scoped>
117
-
118
- </style>
@@ -1,79 +0,0 @@
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>