update view GroupList to use TagsSelectize + improved code readability

This commit is contained in:
axolotle 2021-04-16 20:38:55 +02:00
parent 043f5acf0d
commit 17e2d726b9

View file

@ -2,7 +2,7 @@
<view-search <view-search
items-name="groups" items-name="groups"
:search.sync="search" :search.sync="search"
:items="normalGroups" :items="primaryGroups"
:filtered-items="filteredGroups" :filtered-items="filteredGroups"
:queries="queries" :queries="queries"
@queries-response="onQueriesResponse" @queries-response="onQueriesResponse"
@ -16,13 +16,13 @@
<!-- PRIMARY GROUPS CARDS --> <!-- PRIMARY GROUPS CARDS -->
<card <card
v-for="(group, name) in filteredGroups" :key="name" collapsable v-for="(group, groupName) in filteredGroups" :key="groupName" collapsable
:title="group.isSpecial ? $t('group_' + name) : `${$t('group')} '${name}'`" icon="group" :title="group.isSpecial ? $t('group_' + groupName) : `${$t('group')} '${groupName}'`" icon="group"
> >
<template #header-buttons> <template #header-buttons>
<!-- DELETE GROUP --> <!-- DELETE GROUP -->
<b-button <b-button
v-if="!group.isSpecial" @click="deleteGroup(name)" v-if="!group.isSpecial" @click="deleteGroup(groupName)"
size="sm" variant="danger" size="sm" variant="danger"
> >
<icon iname="trash-o" /> {{ $t('delete') }} <icon iname="trash-o" /> {{ $t('delete') }}
@ -37,18 +37,18 @@
<b-col> <b-col>
<template v-if="group.isSpecial"> <template v-if="group.isSpecial">
<p class="text-primary"> <p class="text-primary">
<icon iname="info-circle" /> {{ $t('group_explain_' + name) }} <icon iname="info-circle" /> {{ $t('group_explain_' + groupName) }}
</p> </p>
<p class="text-primary" v-if="name === 'visitors'"> <p class="text-primary" v-if="groupName === 'visitors'">
<em>{{ $t('group_explain_visitors_needed_for_external_client') }}</em> <em>{{ $t('group_explain_visitors_needed_for_external_client') }}</em>
</p> </p>
</template> </template>
<template v-else> <template v-else>
<zone-selectize <tags-selectize
:choices="group.availableMembers" :selected="group.members" v-model="group.members" :options="usersOptions"
item-icon="user" :id="groupName + '-users'" :label="$t('group_add_member')"
:label="$t('group_add_member')" tag-icon="user" items-name="users"
@change="onUserChanged({ ...$event, name })" @tag-update="onUserChanged({ ...$event, groupName })"
/> />
</template> </template>
</b-col> </b-col>
@ -60,50 +60,45 @@
<strong>{{ $t('permissions') }}</strong> <strong>{{ $t('permissions') }}</strong>
</b-col> </b-col>
<b-col> <b-col>
<zone-selectize <tags-selectize
item-icon="key-modern" v-model="group.permissions" :options="permissionsOptions"
:choices="group.availablePermissions" :id="groupName + '-perms'" :label="$t('group_add_permission')"
:selected="group.permissions" tag-icon="key-modern" items-name="permissions"
:label="$t('group_add_permission')" @tag-update="onPermissionChanged({ ...$event, groupName })"
:format="formatPermission" :disabled-items="group.disabledItems"
:removable="name === 'visitors' ? removable : null"
@change="onPermissionChanged({ ...$event, name, groupType: 'normal' })"
/> />
</b-col> </b-col>
</b-row> </b-row>
</card> </card>
<!-- GROUP SPECIFIC CARD --> <!-- USER GROUPS CARD -->
<card <card
v-if="userGroups" collapsable v-if="userGroups" collapsable
:title="$t('group_specific_permissions')" icon="group" :title="$t('group_specific_permissions')" icon="group"
> >
<template v-for="(name, index) in userGroupsNames"> <template v-for="(userName, index) in activeUserGroups">
<b-row :key="name"> <b-row :key="userName">
<b-col md="3" lg="2"> <b-col md="3" lg="2">
<icon iname="user" /> <strong>{{ name }}</strong> <icon iname="user" /> <strong>{{ userName }}</strong>
</b-col> </b-col>
<b-col> <b-col>
<zone-selectize <tags-selectize
item-icon="key-modern" item-variant="dark" v-model="userGroups[userName].permissions" :options="permissionsOptions"
:choices="userGroups[name].availablePermissions" :id="userName + '-perms'" :label="$t('group_add_permission')"
:selected="userGroups[name].permissions" tag-icon="key-modern" items-name="permissions"
:label="$t('group_add_permission')" @tag-update="onPermissionChanged({ ...$event, groupName: userName })"
:format="formatPermission"
@change="onPermissionChanged({ ...$event, name, groupType: 'user' })"
/> />
</b-col> </b-col>
</b-row> </b-row>
<hr :key="index"> <hr :key="index">
</template> </template>
<base-selectize <tags-selectize
v-if="availableMembers.length" v-model="activeUserGroups" :options="usersOptions"
:label="$t('group_add_member')" id="user-groups" :label="$t('group_add_member')"
:choices="availableMembers" no-tags items-name="users"
:selected="userGroupsNames" @tag-update="onSpecificUserAdded"
@selected="onSpecificUserAdded"
/> />
</card> </card>
</view-search> </view-search>
@ -114,8 +109,7 @@ import Vue from 'vue'
import api from '@/api' import api from '@/api'
import { isEmptyValue } from '@/helpers/commons' import { isEmptyValue } from '@/helpers/commons'
import ZoneSelectize from '@/components/ZoneSelectize' import TagsSelectize from '@/components/TagsSelectize'
import BaseSelectize from '@/components/BaseSelectize'
// TODO add global search with type (search by: group, user, permission) // TODO add global search with type (search by: group, user, permission)
// TODO add vuex store update on inputs ? // TODO add vuex store update on inputs ?
@ -123,8 +117,7 @@ export default {
name: 'GroupList', name: 'GroupList',
components: { components: {
ZoneSelectize, TagsSelectize
BaseSelectize
}, },
data () { data () {
@ -136,134 +129,114 @@ export default {
], ],
search: '', search: '',
permissions: undefined, permissions: undefined,
normalGroups: undefined, permissionsOptions: undefined,
userGroups: undefined primaryGroups: undefined,
userGroups: undefined,
usersOptions: undefined,
activeUserGroups: undefined
} }
}, },
computed: { computed: {
filteredGroups () { filteredGroups () {
const groups = this.normalGroups const groups = this.primaryGroups
if (!groups) return if (!groups) return
const search = this.search.toLowerCase() const search = this.search.toLowerCase()
const filtered = {} const filtered = {}
for (const name in groups) { for (const groupName in groups) {
if (name.toLowerCase().includes(search)) { if (groupName.toLowerCase().includes(search)) {
filtered[name] = groups[name] filtered[groupName] = groups[groupName]
} }
} }
return isEmptyValue(filtered) ? null : filtered return isEmptyValue(filtered) ? null : filtered
},
userGroupsNames () {
const groups = this.userGroups
if (!groups) return
return Object.keys(groups).filter(name => {
return groups[name].permissions !== null
})
},
availableMembers () {
const groups = this.userGroups
if (!groups) return
return Object.keys(groups).filter(name => {
return groups[name].permissions === null
})
} }
}, },
methods: { methods: {
onQueriesResponse (users, allGroups, permissions) { onQueriesResponse (users, allGroups, permsDict) {
// Do not use computed properties to get values from the store here to avoid auto // Do not use computed properties to get values from the store here to avoid auto
// updates while modifying values. // updates while modifying values.
const normalGroups = {} const permissions = Object.entries(permsDict).map(([id, value]) => ({ id, ...value }))
const userGroups = {}
const userNames = users ? Object.keys(users) : [] const userNames = users ? Object.keys(users) : []
const primaryGroups = {}
const userGroups = {}
for (const groupName in allGroups) { for (const groupName in allGroups) {
// copy the group to unlink it from the store // copy the group to unlink it from the store
const group = { ...allGroups[groupName] } const group = { ...allGroups[groupName] }
group.availablePermissions = Object.keys(permissions).filter(perm => { group.permissions = group.permissions.map((perm) => {
// Remove 'email', 'xmpp' and protected permissions in visitors's permission choice list return permsDict[perm].label
if (groupName === 'visitors' && (['mail.main', 'xmpp.main'].includes(perm) || permissions[perm].protected)) {
return false
}
return !group.permissions.includes(perm)
}) })
if (userNames.includes(groupName)) { if (userNames.includes(groupName)) {
if (group.permissions.length === 0) {
// This forbid the user to appear in the displayed user list
group.permissions = null
}
userGroups[groupName] = group userGroups[groupName] = group
continue continue
} }
if (['visitors', 'all_users'].includes(groupName)) { group.isSpecial = ['visitors', 'all_users'].includes(groupName)
group.isSpecial = true
} else { if (groupName === 'visitors') {
group.availableMembers = userNames.filter(name => { // Forbid to add or remove a protected permission on group `visitors`
return !group.members.includes(name) group.disabledItems = permissions.filter(({ id }) => {
}) return ['mail.main', 'xmpp.main'].includes(id) || permsDict[id].protected
}).map(({ id }) => permsDict[id].label)
} }
normalGroups[groupName] = group
primaryGroups[groupName] = group
} }
this.permissions = permissions const activeUserGroups = Object.entries(userGroups).filter(([_, group]) => {
this.normalGroups = normalGroups return group.permissions.length > 0
this.userGroups = isEmptyValue(userGroups) ? null : userGroups }).map(([name]) => name)
},
onPermissionChanged ({ item, index, name, groupType, action }) { Object.assign(this, {
// const uri = 'users/permissions/' + item permissions,
// const data = { [action]: name } permissionsOptions: permissions.map(perm => perm.label),
const from = action === 'add' ? 'availablePermissions' : 'permissions' primaryGroups,
const to = action === 'add' ? 'permissions' : 'availablePermissions' userGroups: isEmptyValue(userGroups) ? null : userGroups,
api.put( usersOptions: userNames,
`users/permissions/${item}/${action}/${name}`, activeUserGroups
{},
{ key: 'permissions.' + action, perm: item.replace('.main', ''), name }
).then(() => {
this[groupType + 'Groups'][name][from].splice(index, 1)
this[groupType + 'Groups'][name][to].push(item)
}) })
}, },
onUserChanged ({ item, index, name, action }) { onPermissionChanged ({ option, groupName, action, applyMethod }) {
const from = action === 'add' ? 'availableMembers' : 'members' const permId = this.permissions.find(perm => perm.label === option).id
const to = action === 'add' ? 'members' : 'availableMembers'
api.put( api.put(
`users/groups/${name}/${action}/${item}`, `users/permissions/${permId}/${action}/${groupName}`,
{}, {},
{ key: 'groups.' + action, user: item, name } { key: 'permissions.' + action, perm: option, name: groupName }
).then(() => { ).then(() => {
this.normalGroups[name][from].splice(index, 1) applyMethod(option)
this.normalGroups[name][to].push(item)
}) })
}, },
onSpecificUserAdded ({ item }) { onUserChanged ({ option, groupName, action, applyMethod }) {
this.userGroups[item].permissions = [] api.put(
`users/groups/${groupName}/${action}/${option}`,
{},
{ key: 'groups.' + action, user: option, name: groupName }
).then(() => {
applyMethod(option)
})
}, },
// FIXME Find a way to pass a filter to a component onSpecificUserAdded ({ option: userName, action, applyMethod }) {
formatPermission (name) { if (action === 'add') {
return this.permissions[name].label this.userGroups[userName].permissions = []
applyMethod(userName)
}
}, },
removable (name) { async deleteGroup (groupName) {
return this.permissions[name].protected === false const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name: groupName }))
},
async deleteGroup (name) {
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name }))
if (!confirmed) return if (!confirmed) return
api.delete( api.delete(
{ uri: 'users/groups', param: name, storeKey: 'groups' }, {}, { key: 'groups.delete', name } { uri: 'users/groups', param: groupName, storeKey: 'groups' },
{},
{ key: 'groups.delete', name: groupName }
).then(() => { ).then(() => {
Vue.delete(this.normalGroups, name) Vue.delete(this.primaryGroups, groupName)
}) })
} }
} }