@drax/identity-vue 0.5.4 → 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.
- package/package.json +5 -5
- package/src/combobox/RoleCombobox.vue +9 -1
- package/src/combobox/TenantCombobox.vue +11 -9
- package/src/components/IdentityChangeOwnPassword/IdentityChangeOwnPassword.vue +4 -4
- package/src/components/IdentityProfileDrawer/IdentityProfileDrawer.vue +2 -2
- package/src/cruds/role-crud/RoleCrud.ts +9 -1
- package/src/cruds/role-crud/RoleForm.vue +116 -0
- package/src/cruds/tenant-crud/TenantCrud.ts +8 -0
- package/src/cruds/user-crud/PasswordUpdateButton.vue +25 -0
- package/src/cruds/user-crud/UserCrud.ts +11 -0
- package/src/cruds/user-crud/UserCrud.vue +4 -17
- package/src/cruds/user-crud/UserForm.vue +177 -0
- package/src/cruds/user-crud/UserPasswordDialog.vue +94 -0
- package/src/{forms → cruds/user-crud}/UserPasswordForm.vue +4 -2
- package/src/index.ts +7 -21
- package/src/pages/ProfilePage.vue +1 -1
- package/src/pages/{RoleCrudPage.vue → crud/RoleCrudPage.vue} +11 -10
- package/src/pages/{TenantCrudPage.vue → crud/TenantCrudPage.vue} +1 -1
- package/src/pages/{UserApiKeyCrudPage.vue → crud/UserApiKeyCrudPage.vue} +1 -1
- package/src/pages/crud/UserCrudPage.vue +61 -0
- package/src/routes/IdentityRoutes.ts +5 -35
- package/src/cruds/role-crud/RoleCrud.vue +0 -171
- package/src/cruds/role-crud/RoleList.vue +0 -129
- package/src/cruds/tenant-crud/TenantCrud.vue +0 -168
- package/src/cruds/tenant-crud/TenantList.vue +0 -108
- package/src/cruds/user-crud/UserList.vue +0 -110
- package/src/forms/RoleForm.vue +0 -89
- package/src/forms/RoleFormOld.vue +0 -57
- package/src/forms/TenantForm.vue +0 -48
- package/src/forms/UserCreateForm.vue +0 -115
- package/src/forms/UserEditForm.vue +0 -101
- package/src/pages/RoleCrudCustomPage.vue +0 -12
- package/src/pages/TenantCrudCustomPage.vue +0 -12
- package/src/pages/UserCrudCustomPage.vue +0 -12
- package/src/pages/UserCrudPage.vue +0 -18
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.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.5.
|
|
28
|
-
"@drax/common-vue": "^0.5.
|
|
27
|
+
"@drax/common-front": "^0.5.5",
|
|
28
|
+
"@drax/common-vue": "^0.5.5",
|
|
29
29
|
"@drax/crud-front": "^0.5.3",
|
|
30
|
-
"@drax/crud-share": "^0.5.
|
|
30
|
+
"@drax/crud-share": "^0.5.5",
|
|
31
31
|
"@drax/identity-share": "^0.5.1"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"vue-tsc": "^2.1.6",
|
|
66
66
|
"vuetify": "^3.7.1"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "c89751dd8eb2ede7d565b596f698c2b83e0c2fed"
|
|
69
69
|
}
|
|
@@ -3,12 +3,19 @@ import {ref, onMounted, defineModel} from 'vue'
|
|
|
3
3
|
import type {PropType} from 'vue'
|
|
4
4
|
import type {IRole} from "@drax/identity-share";
|
|
5
5
|
import {useRole} from "../composables/useRole";
|
|
6
|
+
import {useI18n} from "vue-i18n";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const {t,te} = useI18n()
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
defineProps({
|
|
8
13
|
errorMessages: {type: String as PropType<string | string[] | undefined>},
|
|
14
|
+
rules: {type: Array as PropType<any[]>, default: () => []},
|
|
9
15
|
multiple: {type: Boolean, default: false},
|
|
10
16
|
clearable: {type: Boolean, default: false},
|
|
11
17
|
readonly: {type: Boolean, default: false},
|
|
18
|
+
label: {type: String, default: 'role.entity'},
|
|
12
19
|
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
13
20
|
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
14
21
|
})
|
|
@@ -26,7 +33,7 @@ onMounted(async () => {
|
|
|
26
33
|
<template>
|
|
27
34
|
<v-select
|
|
28
35
|
v-model="model"
|
|
29
|
-
label="
|
|
36
|
+
:label="te(label) ? t(label) : label"
|
|
30
37
|
:items="items"
|
|
31
38
|
item-title="name"
|
|
32
39
|
item-value="id"
|
|
@@ -36,6 +43,7 @@ onMounted(async () => {
|
|
|
36
43
|
:clearable="clearable"
|
|
37
44
|
:readonly="readonly"
|
|
38
45
|
:density="density"
|
|
46
|
+
:rules="rules"
|
|
39
47
|
></v-select>
|
|
40
48
|
</template>
|
|
41
49
|
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
import {ref, onMounted, defineModel} from 'vue'
|
|
4
4
|
import type { PropType } from 'vue'
|
|
5
5
|
import {useI18n} from "vue-i18n";
|
|
6
|
-
const {t} = useI18n()
|
|
6
|
+
const {t,te} = useI18n()
|
|
7
7
|
defineProps({
|
|
8
|
-
errorMessages: {
|
|
9
|
-
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
8
|
+
errorMessages: {type: String as PropType<string | string[] | undefined>,},
|
|
9
|
+
clearable: {type: Boolean, default: false},
|
|
10
|
+
readonly: {type: Boolean, default: false},
|
|
11
|
+
rules: {type: Array as PropType<any[]>, default: () => []},
|
|
12
|
+
label: {type: String, default: 'tenant.entity'},
|
|
13
|
+
density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
|
|
14
|
+
variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
const model = defineModel<any>()
|
|
@@ -29,13 +29,15 @@ onMounted(async () => {
|
|
|
29
29
|
<template>
|
|
30
30
|
<v-select
|
|
31
31
|
v-model="model"
|
|
32
|
-
:label="
|
|
32
|
+
:label="te(label) ? t(label) : label"
|
|
33
33
|
:items="items"
|
|
34
34
|
item-title="name"
|
|
35
35
|
item-value="id"
|
|
36
36
|
variant="outlined"
|
|
37
37
|
:error-messages="errorMessages"
|
|
38
38
|
:clearable="clearable"
|
|
39
|
+
:rules="rules"
|
|
40
|
+
:readonly="readonly"
|
|
39
41
|
></v-select>
|
|
40
42
|
</template>
|
|
41
43
|
|
|
@@ -65,14 +65,14 @@ async function submitChangePassowrd() {
|
|
|
65
65
|
|
|
66
66
|
<v-form @submit.prevent="submitChangePassowrd">
|
|
67
67
|
<v-card variant="elevated">
|
|
68
|
-
<v-card-title class="pa-4 text-center">{{ t('user.changeOwnPassword') }}</v-card-title>
|
|
68
|
+
<v-card-title class="pa-4 text-center">{{ t('user.action.changeOwnPassword') }}</v-card-title>
|
|
69
69
|
<v-card-text v-if="errorMsg">
|
|
70
70
|
<v-alert type="error">
|
|
71
71
|
{{ te(errorMsg) ?t(errorMsg) : errorMsg }}
|
|
72
72
|
</v-alert>
|
|
73
73
|
</v-card-text>
|
|
74
74
|
<v-card-text>
|
|
75
|
-
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.currentPassword') }}</div>
|
|
75
|
+
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.field.currentPassword') }}</div>
|
|
76
76
|
<v-text-field
|
|
77
77
|
variant="outlined"
|
|
78
78
|
id="current-password-input"
|
|
@@ -85,7 +85,7 @@ async function submitChangePassowrd() {
|
|
|
85
85
|
autocomplete="new-password"
|
|
86
86
|
:error-messages="$ta(inputErrors?.currentPassword)"
|
|
87
87
|
></v-text-field>
|
|
88
|
-
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.newPassword') }}</div>
|
|
88
|
+
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.field.newPassword') }}</div>
|
|
89
89
|
<v-text-field
|
|
90
90
|
variant="outlined"
|
|
91
91
|
id="new-password-input"
|
|
@@ -98,7 +98,7 @@ async function submitChangePassowrd() {
|
|
|
98
98
|
autocomplete="new-password"
|
|
99
99
|
:error-messages="$ta(inputErrors?.newPassword)"
|
|
100
100
|
></v-text-field>
|
|
101
|
-
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.confirmPassword') }}</div>
|
|
101
|
+
<div class="text-subtitle-1 text-medium-emphasis">{{ t('user.field.confirmPassword') }}</div>
|
|
102
102
|
<v-text-field
|
|
103
103
|
variant="outlined"
|
|
104
104
|
id="confirm-password-input"
|
|
@@ -37,7 +37,7 @@ const valueModel = defineModel<boolean>()
|
|
|
37
37
|
<v-list-item
|
|
38
38
|
@click="auth.logout()"
|
|
39
39
|
prepend-icon="mdi-logout"
|
|
40
|
-
:title="t('user.logout')"
|
|
40
|
+
:title="t('user.action.logout')"
|
|
41
41
|
>
|
|
42
42
|
</v-list-item>
|
|
43
43
|
</v-list>
|
|
@@ -48,7 +48,7 @@ const valueModel = defineModel<boolean>()
|
|
|
48
48
|
<v-list-item
|
|
49
49
|
href="/login"
|
|
50
50
|
prepend-icon="mdi-login"
|
|
51
|
-
:title="t('user.login')"
|
|
51
|
+
:title="t('user.action.login')"
|
|
52
52
|
>
|
|
53
53
|
</v-list-item>
|
|
54
54
|
</v-list>
|
|
@@ -73,7 +73,15 @@ class RoleCrud extends EntityCrud implements IEntityCrud {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
get exportHeaders(){
|
|
76
|
-
return ['_id', 'name']
|
|
76
|
+
return ['_id', 'name','permissions','childRoles','readonly']
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get isExportable(){
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get isImportable(){
|
|
84
|
+
return false
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
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
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
<v-form v-model="valid" ref="formRef" @submit.prevent="submit">
|
|
46
|
+
<v-card flat>
|
|
47
|
+
<v-alert v-if="valueModel.readonly && store.operation != 'view'" type="warning">
|
|
48
|
+
{{ t('role.readonly') }}
|
|
49
|
+
</v-alert>
|
|
50
|
+
|
|
51
|
+
<template v-if="!valueModel.readonly || store.operation == 'view'">
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
<v-card-subtitle v-if="valueModel._id">ID: {{ valueModel._id }}</v-card-subtitle>
|
|
55
|
+
|
|
56
|
+
<v-card-text v-if="store.error">
|
|
57
|
+
<v-alert color="error">{{ te(store.error) ? t(store.error) : store.error }}</v-alert>
|
|
58
|
+
</v-card-text>
|
|
59
|
+
|
|
60
|
+
<v-card-text>
|
|
61
|
+
|
|
62
|
+
<v-text-field
|
|
63
|
+
id="name-input"
|
|
64
|
+
name="name"
|
|
65
|
+
:label="$t('role.field.name')"
|
|
66
|
+
v-model="valueModel.name"
|
|
67
|
+
prepend-inner-icon="mdi-card-account-details"
|
|
68
|
+
:variant="variant"
|
|
69
|
+
:error-messages="$ta(store.inputErrors?.name)"
|
|
70
|
+
:rules="[v => !!v || 'validation.required']"
|
|
71
|
+
density="default"
|
|
72
|
+
:readonly="readonly"
|
|
73
|
+
:clearable="false"
|
|
74
|
+
></v-text-field>
|
|
75
|
+
|
|
76
|
+
<RoleCombobox
|
|
77
|
+
v-model="valueModel.childRoles"
|
|
78
|
+
label="role.field.childRoles"
|
|
79
|
+
:error-messages="$ta(store.inputErrors?.role)"
|
|
80
|
+
multiple
|
|
81
|
+
:readonly="readonly"
|
|
82
|
+
:variant="variant"
|
|
83
|
+
></RoleCombobox>
|
|
84
|
+
|
|
85
|
+
<PermissionSelector
|
|
86
|
+
v-model="valueModel.permissions"
|
|
87
|
+
:readonly="readonly"
|
|
88
|
+
></PermissionSelector>
|
|
89
|
+
|
|
90
|
+
</v-card-text>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<v-card-actions>
|
|
94
|
+
<v-spacer></v-spacer>
|
|
95
|
+
<v-btn
|
|
96
|
+
variant="text"
|
|
97
|
+
color="grey"
|
|
98
|
+
@click="cancel">
|
|
99
|
+
{{ t('action.cancel') }}
|
|
100
|
+
</v-btn>
|
|
101
|
+
<v-btn
|
|
102
|
+
v-if="!valueModel.readonly && store.operation != 'view'"
|
|
103
|
+
variant="flat"
|
|
104
|
+
:color="submitColor"
|
|
105
|
+
@click="submit"
|
|
106
|
+
>
|
|
107
|
+
{{ store.operation ? t('action.' + store.operation) : t('action.sent') }}
|
|
108
|
+
</v-btn>
|
|
109
|
+
</v-card-actions>
|
|
110
|
+
</v-card>
|
|
111
|
+
</v-form>
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<style scoped>
|
|
115
|
+
|
|
116
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {useI18n} from "vue-i18n";
|
|
3
|
+
const {t} = useI18n()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<v-tooltip location="top">
|
|
8
|
+
<template v-slot:activator="{ props}">
|
|
9
|
+
<v-btn
|
|
10
|
+
v-bind="{ ...$attrs, ...props }"
|
|
11
|
+
icon="mdi-form-textbox-password"
|
|
12
|
+
class="mr-1"
|
|
13
|
+
variant="text"
|
|
14
|
+
color="orange"
|
|
15
|
+
slim
|
|
16
|
+
>
|
|
17
|
+
</v-btn>
|
|
18
|
+
</template>
|
|
19
|
+
{{ t('user.action.changePassword')}}
|
|
20
|
+
</v-tooltip>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
|
|
25
|
+
</style>
|
|
@@ -95,6 +95,17 @@ class UserCrud extends EntityCrud implements IEntityCrud {
|
|
|
95
95
|
return ['_id', 'name','username','email','phone','role.name','tenant.name','active']
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
get exportFormats(){
|
|
99
|
+
return ['CSV']
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get isExportable(){
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get isImportable(){
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
export default UserCrud
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import {computed, ref} from 'vue'
|
|
3
|
-
import UserList from "./UserList.vue";
|
|
4
3
|
import {useUser} from "../../composables/useUser";
|
|
5
4
|
import type {IUser, IUserCreate, IUserUpdate} from "@drax/identity-share";
|
|
6
5
|
import type {IUserPassword} from "@drax/identity-front";
|
|
7
|
-
import UserCreateForm from "
|
|
8
|
-
import
|
|
9
|
-
import UserPasswordForm from "../../forms/UserPasswordForm.vue";
|
|
6
|
+
import UserCreateForm from "./UserForm.vue";
|
|
7
|
+
import UserPasswordForm from "./UserPasswordForm.vue";
|
|
10
8
|
import UserView from "../../views/UserView.vue";
|
|
11
9
|
import {useI18n} from "vue-i18n";
|
|
12
10
|
|
|
@@ -154,13 +152,7 @@ function toChangePassword(item: IUser) {
|
|
|
154
152
|
</v-btn>
|
|
155
153
|
</v-toolbar>
|
|
156
154
|
<v-theme-provider with-background class="pa-2 rounded-b">
|
|
157
|
-
|
|
158
|
-
ref="userList"
|
|
159
|
-
@toEdit="toEdit"
|
|
160
|
-
@toDelete="toDelete"
|
|
161
|
-
@toChangePassword="toChangePassword"
|
|
162
|
-
:filterEnable="filterEnable"
|
|
163
|
-
/>
|
|
155
|
+
|
|
164
156
|
</v-theme-provider>
|
|
165
157
|
</v-card>
|
|
166
158
|
|
|
@@ -181,12 +173,7 @@ function toChangePassword(item: IUser) {
|
|
|
181
173
|
@formSubmit="save"
|
|
182
174
|
/>
|
|
183
175
|
|
|
184
|
-
|
|
185
|
-
v-if="dialogMode === 'edit'"
|
|
186
|
-
v-model="editForm"
|
|
187
|
-
:inputErrors="inputErrors"
|
|
188
|
-
@formSubmit="save"
|
|
189
|
-
/>
|
|
176
|
+
|
|
190
177
|
|
|
191
178
|
<UserView v-if="dialogMode === 'delete'
|
|
192
179
|
&& target" :user="target"
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {useFormUtils, useCrudStore} from "@drax/crud-vue";
|
|
3
|
+
import {defineEmits, defineModel, defineProps, ref} from "vue";
|
|
4
|
+
import {useI18nValidation} from "@drax/common-vue";
|
|
5
|
+
import RoleCombobox from "../../combobox/RoleCombobox.vue";
|
|
6
|
+
import TenantCombobox from "../../combobox/TenantCombobox.vue";
|
|
7
|
+
import {useI18n} from "vue-i18n";
|
|
8
|
+
|
|
9
|
+
const {$ta} = useI18nValidation()
|
|
10
|
+
const {t, te} = useI18n()
|
|
11
|
+
|
|
12
|
+
defineProps({
|
|
13
|
+
enablePassword: {type: Boolean, default: false}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const valueModel = defineModel({type: [Object]})
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits(['submit', 'cancel'])
|
|
19
|
+
|
|
20
|
+
const store = useCrudStore()
|
|
21
|
+
|
|
22
|
+
const valid = ref()
|
|
23
|
+
const formRef = ref()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async function submit() {
|
|
27
|
+
store.resetErrors()
|
|
28
|
+
await formRef.value.validate()
|
|
29
|
+
if (valid.value) {
|
|
30
|
+
emit('submit', valueModel.value)
|
|
31
|
+
} else {
|
|
32
|
+
console.log('Invalid form')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function cancel() {
|
|
37
|
+
emit('cancel')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
variant, submitColor, readonly
|
|
42
|
+
} = useFormUtils(store.operation)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
let passwordVisibility = ref(false)
|
|
46
|
+
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<v-form v-model="valid" ref="formRef" @submit.prevent="submit">
|
|
51
|
+
<v-card flat>
|
|
52
|
+
|
|
53
|
+
<v-alert v-if="valueModel.readonly && store.operation != 'view'" type="warning">
|
|
54
|
+
{{ t('role.readonly') }}
|
|
55
|
+
</v-alert>
|
|
56
|
+
|
|
57
|
+
<v-card-text v-if="store.error">
|
|
58
|
+
<v-alert color="error">{{ te(store.error) ? t(store.error) : store.error }}</v-alert>
|
|
59
|
+
</v-card-text>
|
|
60
|
+
|
|
61
|
+
<v-card-text>
|
|
62
|
+
|
|
63
|
+
<v-text-field
|
|
64
|
+
id="name-input"
|
|
65
|
+
:label="$t('user.field.name')"
|
|
66
|
+
v-model="valueModel.name"
|
|
67
|
+
prepend-inner-icon="mdi-card-account-details"
|
|
68
|
+
:variant="variant"
|
|
69
|
+
:rules="[v => !!v || t('validation.required')]"
|
|
70
|
+
:error-messages="$ta(store.inputErrors?.name)"
|
|
71
|
+
:readonly="readonly"
|
|
72
|
+
></v-text-field>
|
|
73
|
+
|
|
74
|
+
<v-text-field
|
|
75
|
+
id="username-input"
|
|
76
|
+
:label="$t('user.field.username')"
|
|
77
|
+
v-model="valueModel.username"
|
|
78
|
+
prepend-inner-icon="mdi-account-question"
|
|
79
|
+
:variant="variant"
|
|
80
|
+
:rules="[v => !!v || t('validation.required')]"
|
|
81
|
+
autocomplete="new-username"
|
|
82
|
+
:error-messages="$ta(store.inputErrors?.username)"
|
|
83
|
+
:readonly="readonly"
|
|
84
|
+
></v-text-field>
|
|
85
|
+
|
|
86
|
+
<v-text-field
|
|
87
|
+
v-if="enablePassword"
|
|
88
|
+
id="password-input"
|
|
89
|
+
:label="$t('user.field.password')"
|
|
90
|
+
v-model="valueModel.password"
|
|
91
|
+
:type="passwordVisibility ? 'text': 'password'"
|
|
92
|
+
:variant="variant"
|
|
93
|
+
:rules="[v => !!v || t('validation.required')]"
|
|
94
|
+
prepend-inner-icon="mdi-lock-outline"
|
|
95
|
+
:append-inner-icon="passwordVisibility ? 'mdi-eye-off': 'mdi-eye'"
|
|
96
|
+
@click:append-inner="passwordVisibility = !passwordVisibility"
|
|
97
|
+
autocomplete="new-password"
|
|
98
|
+
:error-messages="$ta(store.inputErrors?.password)"
|
|
99
|
+
:readonly="readonly"
|
|
100
|
+
></v-text-field>
|
|
101
|
+
|
|
102
|
+
<RoleCombobox
|
|
103
|
+
v-model="valueModel.role"
|
|
104
|
+
:label="$t('user.field.role')"
|
|
105
|
+
:variant="variant"
|
|
106
|
+
:rules="[(v:any) => !!v || t('validation.required')]"
|
|
107
|
+
:error-messages="$ta(store.inputErrors?.role)"
|
|
108
|
+
:readonly="readonly"
|
|
109
|
+
></RoleCombobox>
|
|
110
|
+
|
|
111
|
+
<TenantCombobox
|
|
112
|
+
v-model="valueModel.tenant"
|
|
113
|
+
:label="$t('user.field.tenant')"
|
|
114
|
+
:variant="variant"
|
|
115
|
+
:error-messages="$ta(store.inputErrors?.tenant)"
|
|
116
|
+
clearable
|
|
117
|
+
:readonly="readonly"
|
|
118
|
+
></TenantCombobox>
|
|
119
|
+
|
|
120
|
+
<v-text-field
|
|
121
|
+
v-model="valueModel.email"
|
|
122
|
+
:variant="variant"
|
|
123
|
+
id="email-input"
|
|
124
|
+
:label="$t('user.field.email')"
|
|
125
|
+
prepend-inner-icon="mdi-email"
|
|
126
|
+
:rules="[(v:any) => !!v || t('validation.required')]"
|
|
127
|
+
:error-messages="$ta(store.inputErrors?.email)"
|
|
128
|
+
:readonly="readonly"
|
|
129
|
+
></v-text-field>
|
|
130
|
+
|
|
131
|
+
<v-text-field
|
|
132
|
+
v-model="valueModel.phone"
|
|
133
|
+
:variant="variant"
|
|
134
|
+
id="phone-input"
|
|
135
|
+
:label="$t('user.field.phone')"
|
|
136
|
+
prepend-inner-icon="mdi-phone"
|
|
137
|
+
:rules="[(v:any) => !!v || t('validation.required')]"
|
|
138
|
+
:error-messages="$ta(store.inputErrors?.phone)"
|
|
139
|
+
:readonly="readonly"
|
|
140
|
+
></v-text-field>
|
|
141
|
+
|
|
142
|
+
<v-switch
|
|
143
|
+
id="active-input"
|
|
144
|
+
v-model="valueModel.active"
|
|
145
|
+
color="primary"
|
|
146
|
+
label="Active"
|
|
147
|
+
:true-value="true"
|
|
148
|
+
:false-value="false"
|
|
149
|
+
:readonly="readonly"
|
|
150
|
+
></v-switch>
|
|
151
|
+
|
|
152
|
+
</v-card-text>
|
|
153
|
+
|
|
154
|
+
<v-card-actions>
|
|
155
|
+
<v-spacer></v-spacer>
|
|
156
|
+
<v-btn
|
|
157
|
+
variant="text"
|
|
158
|
+
color="grey"
|
|
159
|
+
@click="cancel">
|
|
160
|
+
{{ t('action.cancel') }}
|
|
161
|
+
</v-btn>
|
|
162
|
+
<v-btn
|
|
163
|
+
v-if="!valueModel.readonly && store.operation != 'view'"
|
|
164
|
+
variant="flat"
|
|
165
|
+
:color="submitColor"
|
|
166
|
+
@click="submit"
|
|
167
|
+
>
|
|
168
|
+
{{ store.operation ? t('action.' + store.operation) : t('action.sent') }}
|
|
169
|
+
</v-btn>
|
|
170
|
+
</v-card-actions>
|
|
171
|
+
</v-card>
|
|
172
|
+
</v-form>
|
|
173
|
+
</template>
|
|
174
|
+
|
|
175
|
+
<style scoped>
|
|
176
|
+
|
|
177
|
+
</style>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import UserPasswordForm from "./UserPasswordForm.vue";
|
|
3
|
+
import {defineModel, defineProps, type PropType, ref} from "vue";
|
|
4
|
+
import type {IUserPassword} from "@drax/identity-front";
|
|
5
|
+
import type {IUser} from "@drax/identity-share";
|
|
6
|
+
import {UserSystemFactory} from "@drax/identity-front";
|
|
7
|
+
import {ClientError, type IClientInputError} from "@drax/common-front";
|
|
8
|
+
import {useI18n} from "vue-i18n";
|
|
9
|
+
|
|
10
|
+
const {t} = useI18n()
|
|
11
|
+
|
|
12
|
+
const valueModel = defineModel({type: Boolean, default: false})
|
|
13
|
+
|
|
14
|
+
const {user} = defineProps({
|
|
15
|
+
user: {type: Object as PropType<IUser>, required: true},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const userSystem = UserSystemFactory.getInstance()
|
|
19
|
+
|
|
20
|
+
let passwordForm = ref<IUserPassword>({newPassword: "", confirmPassword: ""})
|
|
21
|
+
let passwordChanged = ref(false);
|
|
22
|
+
let inputErrors = ref<IClientInputError>()
|
|
23
|
+
let loading = ref(false);
|
|
24
|
+
let userError = ref<string>('')
|
|
25
|
+
|
|
26
|
+
async function savePassword() {
|
|
27
|
+
if (passwordForm.value.newPassword === passwordForm.value.confirmPassword) {
|
|
28
|
+
passwordChanged.value = await changeUserPassword(user.id, passwordForm.value.newPassword)
|
|
29
|
+
return
|
|
30
|
+
} else {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function changeUserPassword(id: string, newPassword: string) {
|
|
36
|
+
try {
|
|
37
|
+
loading.value = true
|
|
38
|
+
return await userSystem.changeUserPassword(id, newPassword)
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err instanceof ClientError) {
|
|
41
|
+
inputErrors.value = err.inputErrors
|
|
42
|
+
}
|
|
43
|
+
if (err instanceof Error) {
|
|
44
|
+
userError.value = err.message
|
|
45
|
+
}
|
|
46
|
+
throw err
|
|
47
|
+
} finally {
|
|
48
|
+
loading.value = false
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<v-dialog v-model="valueModel" max-width="800">
|
|
56
|
+
<v-card>
|
|
57
|
+
<v-card-title>{{t('user.operation.changePassword')}}</v-card-title>
|
|
58
|
+
<v-card-subtitle>{{t('user.field.username')}}: {{user.username}}</v-card-subtitle>
|
|
59
|
+
<v-card-text>
|
|
60
|
+
<user-password-form
|
|
61
|
+
v-model="passwordForm"
|
|
62
|
+
:inputErrors="inputErrors"
|
|
63
|
+
:passwordChanged="passwordChanged"
|
|
64
|
+
@formSubmit="savePassword"
|
|
65
|
+
/>
|
|
66
|
+
</v-card-text>
|
|
67
|
+
|
|
68
|
+
<v-card-actions>
|
|
69
|
+
<v-spacer></v-spacer>
|
|
70
|
+
<v-btn
|
|
71
|
+
variant="text"
|
|
72
|
+
color="grey"
|
|
73
|
+
@click="valueModel = false">
|
|
74
|
+
{{ passwordChanged ? t('action.close') : t('action.cancel') }}
|
|
75
|
+
</v-btn>
|
|
76
|
+
<v-btn
|
|
77
|
+
v-if="!passwordChanged"
|
|
78
|
+
variant="flat"
|
|
79
|
+
color="primary"
|
|
80
|
+
@click="savePassword"
|
|
81
|
+
:loading="loading"
|
|
82
|
+
>
|
|
83
|
+
{{ t('action.change') }}
|
|
84
|
+
</v-btn>
|
|
85
|
+
</v-card-actions>
|
|
86
|
+
|
|
87
|
+
</v-card>
|
|
88
|
+
|
|
89
|
+
</v-dialog>
|
|
90
|
+
</template>
|
|
91
|
+
|
|
92
|
+
<style scoped>
|
|
93
|
+
|
|
94
|
+
</style>
|