@drax/identity-vue 0.0.10

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/README.md +61 -0
  2. package/package.json +65 -0
  3. package/src/assets/base.css +86 -0
  4. package/src/assets/logo.svg +1 -0
  5. package/src/assets/main.css +35 -0
  6. package/src/combobox/PermissionCombobox.vue +38 -0
  7. package/src/combobox/RoleCombobox.vue +37 -0
  8. package/src/components/IdentityChangeOwnPassword/IdentityChangeOwnPassword.vue +139 -0
  9. package/src/components/IdentityLogin/IdentityLogin.vue +124 -0
  10. package/src/components/IdentityProfileAvatar/IdentityProfileAvatar.vue +38 -0
  11. package/src/components/IdentityProfileDrawer/IdentityProfileDrawer.vue +82 -0
  12. package/src/components/IdentityProfileView/IdentityProfileView.vue +31 -0
  13. package/src/components/PermissionSelector/PermissionSelector.vue +89 -0
  14. package/src/composables/useAuth.ts +53 -0
  15. package/src/composables/useI18nValidation.ts +11 -0
  16. package/src/composables/useRole.ts +93 -0
  17. package/src/composables/useUser.ts +96 -0
  18. package/src/cruds/role-crud/RoleCrud.vue +165 -0
  19. package/src/cruds/role-crud/RoleList.vue +110 -0
  20. package/src/cruds/user-crud/UserCrud.vue +222 -0
  21. package/src/cruds/user-crud/UserList.vue +100 -0
  22. package/src/forms/RoleForm.vue +44 -0
  23. package/src/forms/UserCreateForm.vue +101 -0
  24. package/src/forms/UserEditForm.vue +87 -0
  25. package/src/forms/UserPasswordForm.vue +81 -0
  26. package/src/i18n/I18n.ts +10 -0
  27. package/src/i18n/I18nMessages.ts +21 -0
  28. package/src/icons/IconCommunity.vue +7 -0
  29. package/src/index.ts +51 -0
  30. package/src/pages/RoleCrudPage.vue +12 -0
  31. package/src/pages/UserCrudPage.vue +12 -0
  32. package/src/stores/auth/AuthStore.ts +38 -0
  33. package/src/views/RoleView.vue +32 -0
  34. package/src/views/UserView.vue +34 -0
@@ -0,0 +1,82 @@
1
+ <script setup lang="ts">
2
+ import {useAuth} from "../../composables/useAuth";
3
+ import {defineModel, ref} from "vue";
4
+ import IdentityProfileView from "../IdentityProfileView/IdentityProfileView.vue";
5
+ import IdentityChangeOwnPassword from "../../components/IdentityChangeOwnPassword/IdentityChangeOwnPassword.vue";
6
+
7
+ const auth = useAuth()
8
+ const valueModel = defineModel()
9
+ const profile = ref(false)
10
+ const changePassword = ref(false)
11
+
12
+
13
+ </script>
14
+
15
+ <template>
16
+
17
+
18
+ <v-navigation-drawer
19
+ v-model="valueModel"
20
+ location="right"
21
+ >
22
+
23
+ <template v-if="auth.isAuthenticated()">
24
+ <identity-profile-view></identity-profile-view>
25
+ <v-divider></v-divider>
26
+
27
+ <v-list>
28
+ <v-list-item
29
+ @click="profile = !profile"
30
+ prepend-icon="mdi-account-cog"
31
+ :title="$t('user.profile')"
32
+ >
33
+ </v-list-item>
34
+ <v-list-item
35
+ @click="auth.logout()"
36
+ prepend-icon="mdi-logout"
37
+ :title="$t('user.logout')"
38
+ >
39
+ </v-list-item>
40
+ </v-list>
41
+ </template>
42
+
43
+ <template v-else>
44
+ <v-list>
45
+ <v-list-item
46
+ href="/login"
47
+ prepend-icon="mdi-login"
48
+ :title="$t('user.login')"
49
+ >
50
+ </v-list-item>
51
+ </v-list>
52
+ </template>
53
+
54
+ <v-dialog v-model="profile" fullscreen :on-after-leave="changePassword=false">
55
+ <v-toolbar>
56
+ <v-toolbar-title>
57
+ {{$t('user.profile')}}
58
+ </v-toolbar-title>
59
+ <v-spacer></v-spacer>
60
+ <v-btn icon @click="profile = !profile">
61
+ <v-icon>mdi-close</v-icon>
62
+ </v-btn>
63
+ </v-toolbar>
64
+ <v-card >
65
+ <v-card-text class="flex-shrink-1 flex-grow-0">
66
+ <identity-profile-view></identity-profile-view>
67
+ </v-card-text>
68
+ <v-card-text class="text-center flex-grow-1">
69
+ <IdentityChangeOwnPassword v-if="changePassword"></IdentityChangeOwnPassword>
70
+ <v-btn v-else color="blue" variant="tonal" @click="changePassword = true">{{$t('user.changeOwnPassword')}}</v-btn>
71
+
72
+ </v-card-text>
73
+ </v-card>
74
+ </v-dialog>
75
+
76
+ </v-navigation-drawer>
77
+
78
+ </template>
79
+
80
+ <style scoped>
81
+
82
+ </style>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import {useAuthStore} from "../../stores/auth/AuthStore.js";
3
+ import IdentityProfileAvatar from "../IdentityProfileAvatar/IdentityProfileAvatar.vue";
4
+ const emit = defineEmits(['click'])
5
+ const authStore = useAuthStore()
6
+ const onClick = () => {
7
+ emit('click')
8
+ }
9
+ </script>
10
+
11
+ <template>
12
+ <v-sheet class="position-relative d-flex justify-center align-center" height="150">
13
+ <v-sheet class="position-absolute bg-surface-light w-100 top-0" height="65"></v-sheet>
14
+ <v-sheet class="text-center">
15
+ <identity-profile-avatar :avatar-size="70"></identity-profile-avatar>
16
+ <h6 class="text-h6">{{ authStore.authUser?.username }}</h6>
17
+ </v-sheet>
18
+ </v-sheet>
19
+
20
+ <v-divider></v-divider>
21
+ <v-sheet class="d-flex justify-center align-center" height="50">
22
+ <v-sheet class="text-center">
23
+ <h6 class="text-caption">{{ authStore.authUser?.email }}</h6>
24
+ <h6 class="text-subtitle-2">role: {{ authStore.authUser?.role?.name }}</h6>
25
+ </v-sheet>
26
+ </v-sheet>
27
+ </template>
28
+
29
+ <style scoped>
30
+
31
+ </style>
@@ -0,0 +1,89 @@
1
+ <script setup lang="ts">
2
+
3
+ import {ref, onMounted, defineModel, computed} from 'vue'
4
+ import type {PropType} from 'vue'
5
+
6
+ const props = defineProps({
7
+ errorMessages: {
8
+ type: String as PropType<string | string[] | undefined>,
9
+ }
10
+ })
11
+
12
+ const model = defineModel()
13
+ import {useRole} from "../../composables/useRole";
14
+
15
+ const {fetchPermissions} = useRole()
16
+ let items = ref([])
17
+
18
+ onMounted(async () => {
19
+ items.value = await fetchPermissions()
20
+ })
21
+
22
+ interface PermissionGroups {
23
+ [key: string]: string[];
24
+ }
25
+
26
+ const permissionGroups = computed(() => {
27
+ const groups = items.value.reduce((acc: PermissionGroups, permission: string) => {
28
+ if (permission.includes(':')) {
29
+ const [entity, action] = permission.split(':');
30
+ if (!acc[entity]) {
31
+ acc[entity] = [];
32
+ }
33
+ acc[entity].push(permission);
34
+ } else {
35
+ // Si el permiso no sigue el formato entidad:acción, lo asignamos al grupo 'general'
36
+ const generalGroup = 'general';
37
+ if (!acc[generalGroup]) {
38
+ acc[generalGroup] = [];
39
+ }
40
+ acc[generalGroup].push(permission);
41
+ }
42
+ return acc;
43
+ }, {});
44
+
45
+ return groups;
46
+ })
47
+
48
+
49
+ </script>
50
+
51
+ <template>
52
+ <v-sheet class="pa-2">
53
+ <h5 class="text-h5 mb-2">{{$t ? $t('permission.permissions') : 'Permissions'}}</h5>
54
+ <v-item-group
55
+ v-model="model"
56
+ variant="outlined"
57
+ divided
58
+ multiple
59
+ color="green"
60
+ >
61
+ <template v-for="(permissions, entity) in permissionGroups" :key="entity">
62
+ <v-card class="mb-2" variant="outlined">
63
+ <v-card-title class="text-capitalize">
64
+ {{ $t ? $t('permission.'+entity) : entity }}
65
+ </v-card-title>
66
+ <v-card-text>
67
+ <v-item
68
+ v-for="permission in permissions"
69
+ v-slot="{ isSelected, toggle }"
70
+ :value="permission"
71
+ >
72
+ <v-btn
73
+ :color="isSelected? 'green' : 'grey-darken-3'" class=""
74
+ @click="toggle" variant="flat" :rounded="false" border
75
+ >
76
+ {{ $t ? $t('permission.'+permission) : permission }}
77
+ </v-btn>
78
+ </v-item>
79
+ </v-card-text>
80
+ </v-card>
81
+ </template>
82
+ </v-item-group>
83
+
84
+ </v-sheet>
85
+ </template>
86
+
87
+ <style scoped>
88
+
89
+ </style>
@@ -0,0 +1,53 @@
1
+ import {useAuthStore} from "../stores/auth/AuthStore";
2
+ import {AuthHelper, AuthSystem} from "@drax/identity-front";
3
+ import {inject} from "vue";
4
+
5
+ export function useAuth() {
6
+
7
+ const authStore = useAuthStore()
8
+
9
+ const authSystem = inject('AuthSystem') as AuthSystem
10
+
11
+ const login = async (username:string, password:string) => {
12
+ const {accessToken} = await authSystem.login(username, password)
13
+ authStore.setAccessToken(accessToken)
14
+ const authUser = await authSystem.me()
15
+ authStore.setAuthUser(authUser)
16
+ }
17
+
18
+ const changeOwnPassword = async (currentPassword:string, newPassword:string) => {
19
+ return await authSystem.changeOwnPassword(currentPassword, newPassword)
20
+ }
21
+
22
+ async function fetchAuthUser() {
23
+ const authUser = await authSystem.me()
24
+ authStore.setAuthUser(authUser)
25
+ return authUser
26
+ }
27
+
28
+ function logout(){
29
+ authSystem.logout()
30
+ authStore.clearAuth()
31
+ }
32
+
33
+ function hasPermission(permission:string) {
34
+ return authStore?.authUser?.role?.permissions ? authStore.authUser.role.permissions.includes(permission) : false
35
+ }
36
+
37
+ function tokenIsValid(){
38
+ return authStore.accessToken ? AuthHelper.isJWTValid(authStore.accessToken) : false
39
+ }
40
+
41
+ function isAuthenticated(){
42
+ if(tokenIsValid()){
43
+ return true
44
+ }else{
45
+ logout()
46
+ return false
47
+ }
48
+
49
+ }
50
+
51
+ return {hasPermission, logout, tokenIsValid, isAuthenticated, fetchAuthUser, login, changeOwnPassword}
52
+
53
+ }
@@ -0,0 +1,11 @@
1
+ import {useI18n} from "vue-i18n";
2
+
3
+ export function useI18nValidation() {
4
+ const {t} = useI18n()
5
+
6
+ function $ta(inputError: string[] | undefined): string | undefined {
7
+ return inputError ? inputError.map(error => t(error)).join(", ") : undefined
8
+ }
9
+
10
+ return {$ta}
11
+ }
@@ -0,0 +1,93 @@
1
+ import {inject, ref} from "vue";
2
+ import type {IRole, RoleSystem} from "@drax/identity-front";
3
+ import {ClientError} from "@drax/common-front";
4
+ import type { IClientInputError} from "@drax/common-front";
5
+
6
+
7
+ export function useRole() {
8
+
9
+ const roleSystem = inject('RoleSystem') as RoleSystem
10
+
11
+ let roleError = ref<string>('')
12
+ let inputErrors = ref<IClientInputError>()
13
+ let loading = ref(false);
14
+
15
+ async function fetchPermissions(page = 1, perPage = 5) {
16
+ loading.value = true
17
+ let permissions = roleSystem.fetchPermissions()
18
+ loading.value = false
19
+ return permissions
20
+ }
21
+
22
+ async function fetchRole(page = 1, perPage = 5) {
23
+ loading.value = true
24
+ let roles = roleSystem.fetchRole()
25
+ loading.value = false
26
+ return roles
27
+ }
28
+
29
+ async function paginateRole(page = 1, perPage = 5, string = "") {
30
+ loading.value = true
31
+ let paginatedrole = roleSystem.paginateRole(page, perPage,string)
32
+ loading.value = false
33
+ return paginatedrole
34
+ }
35
+
36
+ async function createRole(roleData: IRole) {
37
+ try {
38
+ loading.value = true
39
+ let role: IRole = await roleSystem.createRole(roleData)
40
+ return role
41
+ } catch (err) {
42
+ if (err instanceof ClientError) {
43
+ inputErrors.value = err.inputErrors
44
+ }if(err instanceof Error) {
45
+ roleError.value = err.message
46
+ }
47
+ throw err
48
+ }finally {
49
+ loading.value = false
50
+ }
51
+
52
+ }
53
+
54
+ async function editRole(id: string, roleData: IRole) {
55
+ try {
56
+ loading.value = true
57
+ let role: IRole = await roleSystem.editRole(id, roleData)
58
+ return role
59
+ } catch (err) {
60
+
61
+ if (err instanceof ClientError) {
62
+ inputErrors.value = err.inputErrors
63
+ }
64
+ if(err instanceof Error) {
65
+ roleError.value = err.message
66
+ }
67
+ throw err
68
+ }finally {
69
+ loading.value = false
70
+ }
71
+ }
72
+
73
+ async function deleteRole(id: string) {
74
+ try {
75
+ loading.value = true
76
+ await roleSystem.deleteRole(id)
77
+ } catch (err) {
78
+ console.log("composable deleteRole error: ", err, )
79
+ if (err instanceof ClientError) {
80
+ inputErrors.value = err.inputErrors
81
+ }
82
+ if(err instanceof Error) {
83
+ roleError.value = err.message
84
+ }
85
+ throw err
86
+ }finally {
87
+ loading.value = false
88
+ }
89
+ }
90
+
91
+ return {fetchRole, fetchPermissions, paginateRole, createRole, editRole, deleteRole, loading, roleError, inputErrors}
92
+
93
+ }
@@ -0,0 +1,96 @@
1
+ import {inject, ref} from "vue";
2
+ import type {IUserPassword, UserSystem} from "@drax/identity-front";
3
+ import type {IUser} from "@drax/identity-front";
4
+ import type {IClientInputError} from "@drax/common-front";
5
+ import {ClientError} from "@drax/common-front";
6
+ import type {IUserCreate, IUserUpdate} from "@drax/identity-front";
7
+
8
+
9
+ export function useUser() {
10
+
11
+ const userSystem = inject('UserSystem') as UserSystem
12
+
13
+ let userError = ref<string>('')
14
+ let inputErrors = ref<IClientInputError>()
15
+ let loading = ref(false);
16
+
17
+ async function paginateUser(page = 1, perPage = 5, search = "") {
18
+ loading.value = true
19
+ let paginatedUser = userSystem.paginateUser(page, perPage, search)
20
+ loading.value = false
21
+ return paginatedUser
22
+ }
23
+
24
+ async function createUser(userData: IUserCreate) {
25
+ try {
26
+ loading.value = true
27
+ let user: IUser = await userSystem.createUser(userData)
28
+ return user
29
+ } catch (err) {
30
+ if (err instanceof ClientError) {
31
+ inputErrors.value = err.inputErrors
32
+ }if(err instanceof Error) {
33
+ userError.value = err.message
34
+ }
35
+ throw err
36
+ }finally {
37
+ loading.value = false
38
+ }
39
+
40
+ }
41
+
42
+ async function changeUserPassword(id: string, newPassword: string){
43
+ try {
44
+ loading.value = true
45
+ return await userSystem.changeUserPassword(id, newPassword)
46
+ } catch (err) {
47
+ if (err instanceof ClientError) {
48
+ inputErrors.value = err.inputErrors
49
+ }
50
+ if(err instanceof Error) {
51
+ userError.value = err.message
52
+ }
53
+ throw err
54
+ }finally {
55
+ loading.value = false
56
+ }
57
+ }
58
+
59
+ async function editUser(id: string, userData: IUserUpdate) {
60
+ try {
61
+ loading.value = true
62
+ let user: IUser = await userSystem.editUser(id, userData)
63
+ return user
64
+ } catch (err) {
65
+ if (err instanceof ClientError) {
66
+ inputErrors.value = err.inputErrors
67
+ }
68
+ if(err instanceof Error) {
69
+ userError.value = err.message
70
+ }
71
+ throw err
72
+ }finally {
73
+ loading.value = false
74
+ }
75
+ }
76
+
77
+ async function deleteUser(id: string) {
78
+ try {
79
+ loading.value = true
80
+ await userSystem.deleteUser(id)
81
+ } catch (err) {
82
+ console.log("composable deleteUser error: ", err, )
83
+ if (err instanceof ClientError) {
84
+ inputErrors.value = err.inputErrors
85
+ }
86
+ if(err instanceof Error) {
87
+ userError.value = err.message
88
+ }
89
+ throw err
90
+ }finally {
91
+ loading.value = false
92
+ }
93
+ }
94
+
95
+ return {paginateUser, createUser, editUser, changeUserPassword, deleteUser, loading, userError, inputErrors}
96
+ }
@@ -0,0 +1,165 @@
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} from "@drax/identity-front";
6
+ import RoleForm from "../../forms/RoleForm.vue";
7
+ import RoleView from "../../views/RoleView.vue";
8
+
9
+ const {createRole, editRole, deleteRole, loading, roleError, inputErrors} = useRole()
10
+
11
+ interface RoleList {
12
+ loadItems: () => void;
13
+ items: IRole[];
14
+ }
15
+
16
+ type DialogMode = 'create' | 'edit' | 'delete' | null;
17
+
18
+
19
+ let dialog = ref(false);
20
+ let dialogMode = ref<DialogMode>(null);
21
+ let dialogTitle = ref('');
22
+ const roleList = ref<RoleList | null>(null);
23
+ let form = ref<IRole>({name: "", permissions: [], readonly: false})
24
+ let target = ref<IRole>();
25
+ let targetId = ref<string>('');
26
+ let filterEnable = ref(false);
27
+
28
+ function cancel() {
29
+ dialog.value = false
30
+ inputErrors.value = {}
31
+ roleError.value = '';
32
+ dialogMode.value = null
33
+ dialogTitle.value = ''
34
+ targetId.value = ''
35
+ target.value = undefined
36
+ }
37
+
38
+ async function save() {
39
+
40
+ try {
41
+ if (dialogMode.value === 'create') {
42
+ await createRole(form.value)
43
+ } else if (dialogMode.value === 'edit') {
44
+ await editRole(targetId.value, form.value)
45
+ } else if (dialogMode.value === 'delete') {
46
+ await deleteRole(targetId.value)
47
+ }
48
+ dialog.value = false
49
+ inputErrors.value = {}
50
+ roleError.value = '';
51
+ if (roleList.value !== null) {
52
+ roleList.value.loadItems()
53
+ }
54
+ } catch (e) {
55
+ console.error(e)
56
+ if (e instanceof Error) {
57
+ roleError.value = e.message
58
+ }
59
+ }
60
+ }
61
+
62
+ let buttonText = computed(() => {
63
+ switch (dialogMode.value) {
64
+ case 'create':
65
+ return 'action.create'
66
+ case 'edit':
67
+ return 'action.update'
68
+ case 'delete':
69
+ return 'action.delete'
70
+ default:
71
+ return 'action.sent'
72
+ }
73
+ })
74
+
75
+ function toCreate() {
76
+ dialogMode.value = 'create';
77
+ dialogTitle.value = 'role.creating';
78
+ form.value = {name: "", permissions: [], readonly: false}
79
+ dialog.value = true;
80
+ }
81
+
82
+ function toEdit(item: IRole) {
83
+ console.log('toEdit', item)
84
+ dialogMode.value = 'edit';
85
+ dialogTitle.value = 'role.updating';
86
+ const {id, ...rest} = item;
87
+ targetId.value = id;
88
+ form.value = {...rest}
89
+ dialog.value = true;
90
+ }
91
+
92
+ function toDelete(item: IRole) {
93
+ console.log('toDelete', item)
94
+ dialogMode.value = 'delete';
95
+ dialogTitle.value = 'role.deleting';
96
+ target.value = item
97
+ const {id} = item;
98
+ targetId.value = id;
99
+ dialog.value = true;
100
+ }
101
+
102
+ </script>
103
+
104
+ <template>
105
+ <v-container>
106
+
107
+ <v-sheet border rounded>
108
+ <v-toolbar>
109
+ <v-toolbar-title>{{$t('role.managing')}}</v-toolbar-title>
110
+ <v-spacer></v-spacer>
111
+ <v-btn icon @click="filterEnable = !filterEnable">
112
+ <v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
113
+ </v-btn>
114
+ <v-btn color="primary" @click="toCreate">
115
+ {{$t('action.create') }}
116
+ </v-btn>
117
+ </v-toolbar>
118
+ <v-theme-provider with-background class="pa-2 rounded-b">
119
+ <RoleList
120
+ ref="roleList"
121
+ @toEdit="toEdit"
122
+ @toDelete="toDelete"
123
+ :filterEnable="filterEnable"
124
+ />
125
+ </v-theme-provider>
126
+ </v-sheet>
127
+
128
+ <v-dialog v-model="dialog" max-width="800">
129
+ <v-sheet border>
130
+ <v-toolbar>
131
+ <v-toolbar-title>{{ $t(dialogTitle) }}</v-toolbar-title>
132
+ </v-toolbar>
133
+ <v-card class="pa-10">
134
+ <v-card-text v-if="roleError">
135
+ <v-alert type="error">{{ roleError }}</v-alert>
136
+ </v-card-text>
137
+ <v-card-text>
138
+ <RoleForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
139
+ v-model="form"
140
+ :inputErrors="inputErrors"
141
+ />
142
+ <RoleView v-if="dialogMode === 'delete' && target" :role="target"></RoleView>
143
+ </v-card-text>
144
+ <v-card-actions>
145
+ <v-spacer></v-spacer>
146
+ <v-btn variant="text" @click="cancel" :loading="loading">Cancelar</v-btn>
147
+ <v-btn variant="flat"
148
+ :color="dialogMode==='delete' ? 'red' : 'primary'"
149
+ @click="save"
150
+ :loading="loading"
151
+ >
152
+ {{ $t(buttonText) }}
153
+ </v-btn>
154
+ </v-card-actions>
155
+
156
+ </v-card>
157
+ </v-sheet>
158
+ </v-dialog>
159
+
160
+ </v-container>
161
+ </template>
162
+
163
+ <style scoped>
164
+
165
+ </style>