switch to static data in UserList, modifying but not the data store

This commit is contained in:
Axolotle 2020-08-01 22:57:43 +02:00
parent 3955344fa6
commit f14cc2d702
3 changed files with 174 additions and 54 deletions

View file

@ -32,7 +32,7 @@
import BaseSelectize from '@/components/BaseSelectize' import BaseSelectize from '@/components/BaseSelectize'
export default { export default {
name: 'SelectizeZone', name: 'ZoneSelectize',
props: { props: {
itemIcon: { type: String, default: null }, itemIcon: { type: String, default: null },

View file

@ -77,7 +77,7 @@ body {
// custom css // custom css
.actions { .actions {
margin-bottom: 1.5rem; margin-bottom: 2rem;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View file

@ -16,9 +16,9 @@
</div> </div>
<!-- PRIMARY GROUPS CARDS --> <!-- PRIMARY GROUPS CARDS -->
<template v-if="groups"> <template v-if="primaryGroups">
<b-card <b-card
v-for="(group, name, index) in primaryGroups" :key="index" v-for="(group, name, index) in filteredPrimaryGroups" :key="name"
no-body no-body
> >
<b-card-header class="d-flex align-items-center"> <b-card-header class="d-flex align-items-center">
@ -55,10 +55,11 @@
</p> </p>
</template> </template>
<template v-else> <template v-else>
<selectize-zone <zone-selectize
:choices="group.membersInv" :selected="group.members" :choices="group.availableMembers" :selected="group.members"
item-icon="user" search-icon="user-plus" item-route="user-info" item-icon="user" search-icon="user-plus" item-route="user-info"
:aria-label="$t('group_add_member')" :aria-label="$t('group_add_member')"
@change="onUserChanged({ ...$event, name })"
/> />
</template> </template>
</b-col> </b-col>
@ -69,10 +70,13 @@
<strong>{{ $t('permissions') }}</strong> <strong>{{ $t('permissions') }}</strong>
</b-col> </b-col>
<b-col> <b-col>
<selectize-zone <zone-selectize
:choices="group.permissionsInv" :selected="group.permissions"
item-icon="key-modern" item-variant="info" item-icon="key-modern" item-variant="info"
:choices="group.availablePermissions"
:selected="group.permissions"
:aria-label="$t('group_add_permission')" :aria-label="$t('group_add_permission')"
:format="formatPermission"
@change="onPermissionChanged({ ...$event, name, groupType: 'primary' })"
/> />
</b-col> </b-col>
</b-row> </b-row>
@ -80,67 +84,141 @@
</b-collapse> </b-collapse>
</b-card> </b-card>
</template> </template>
<!-- GROUP SPECIFIC CARD -->
<b-card no-body v-if="userGroups">
<b-card-header class="d-flex align-items-center">
<h2>
<icon iname="group" /> {{ $t('group_specific_permissions') }}
</h2>
<div class="ml-auto">
<b-button v-b-toggle.collapse-specific size="sm" variant="outline-secondary">
<icon iname="chevron-right" class="rotate" /><span class="sr-only">{{ $t('words.collapse') }}</span>
</b-button>
</div>
</b-card-header>
<b-collapse id="collapse-specific" visible>
<b-card-body>
<div v-for="name in userGroupsNames" :key="name">
<b-row>
<b-col md="3" lg="2">
<icon iname="user" /> <strong>{{ name }}</strong>
</b-col>
<b-col>
<zone-selectize
item-icon="key-modern" item-variant="info"
:choices="userGroups[name].availablePermissions"
:selected="userGroups[name].permissions"
:aria-label="$t('group_add_permission')"
:format="formatPermission"
@change="onPermissionChanged({ ...$event, name, groupType: 'user' })"
/>
</b-col>
</b-row>
<hr>
</div>
<base-selectize
search-icon="user-plus"
:aria-label="$t('group_add_member')"
:choices="availableMembers"
:selected="userGroupsNames"
@selected="onSpecificUserAdded"
/>
</b-card-body>
</b-collapse>
</b-card>
</div> </div>
</template> </template>
<script> <script>
import SelectizeZone from '@/components/SelectizeZone' import ZoneSelectize from '@/components/ZoneSelectize'
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)
export default { export default {
name: 'GroupList', name: 'GroupList',
data: () => ({ data: () => ({
search: '' search: '',
primaryGroups: undefined,
userGroups: undefined
}), }),
computed: { computed: {
users () { filteredPrimaryGroups () {
const users = this.$store.state.data.users const groups = this.primaryGroups
return users ? Object.values(users) : users
},
groups () {
const users = this.$store.state.data.users
const groups = this.$store.state.data.groups
const perms = this.$store.state.data.permissions
if (!users || !groups || !perms) return
const userNames = Object.keys(users)
for (const groupName in groups) {
groups[groupName].isPrimary = !userNames.includes(groupName)
groups[groupName].permissionsInv = perms.filter(perm => {
// Remove 'email' and 'xmpp' in visitors's permission choice list
if (groupName === 'visitors' && ['mail.main', 'xmpp.main'].includes(perm)) {
return false
}
return !groups[groupName].permissions.includes(perm)
})
groups[groupName].membersInv = userNames.filter(name => {
return !groups[groupName].members.includes(name)
})
}
groups.visitors.isSpecial = true
groups.all_users.isSpecial = true
return groups
},
permissions () {
return this.$store.state.data.permissions
},
primaryGroups () {
const groups = this.groups
if (!groups) return if (!groups) return
const search = this.search.toLowerCase() const search = this.search.toLowerCase()
const primaryGroups = {} const filtered = {}
for (const [groupName, group] of Object.entries(groups)) { for (const name in groups) {
if (group.isPrimary && groupName.toLowerCase().includes(search)) { if (name.toLowerCase().includes(search)) {
primaryGroups[groupName] = group filtered[name] = groups[name]
} }
} }
return primaryGroups return 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: {
onPermissionChanged ({ item, index, name, groupType, action }) {
const uri = 'users/permissions/' + item
const data = { [action]: name }
const from = action === 'add' ? 'availablePermissions' : 'permissions'
const to = action === 'add' ? 'permissions' : 'availablePermissions'
this.$store.dispatch('PUT', { uri, data }).then(() => {
this[groupType + 'Groups'][name][from].splice(index, 1)
this[groupType + 'Groups'][name][to].push(item)
})
},
onUserChanged ({ item, index, name, action }) {
const uri = 'users/groups/' + name
const data = { [action]: item }
const from = action === 'add' ? 'availableMembers' : 'members'
const to = action === 'add' ? 'members' : 'availableMembers'
this.$store.dispatch('PUT', { uri, data }).then(() => {
this.primaryGroups[name][from].splice(index, 1)
this.primaryGroups[name][to].push(item)
})
},
onSpecificUserAdded ({ item }) {
this.userGroups[item].permissions = []
},
// FIXME Find a way to pass a filter to a component
formatPermission: text => {
let result = text.replace('.main', '')
if (result.includes('.')) {
result = result.replace('.', ' (') + ')'
}
if (result === 'mail') return 'E-mail'
else if (result === 'xmpp') return 'XMPP'
else {
return result.replace(/\w\S*/g, txt => {
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase()
})
}
} }
}, },
@ -149,11 +227,53 @@ export default {
{ uri: 'users' }, { uri: 'users' },
{ uri: 'users/groups?full&include_primary_groups', storeKey: 'groups' }, { uri: 'users/groups?full&include_primary_groups', storeKey: 'groups' },
{ uri: 'users/permissions?short', storeKey: 'permissions' } { uri: 'users/permissions?short', storeKey: 'permissions' }
]) ]).then(([users, groups, permissions]) => {
// Do not use computed properties to get values from the store here to avoid auto
// updates while modifying values.
// pre-format the stored data for rendering
const primaryGroups = {}
const userGroups = {}
const userNames = Object.keys(users)
for (const groupName in groups) {
// copy the group to unlink it from the store
const group = { ...groups[groupName] }
group.availablePermissions = permissions.filter(perm => {
// Remove 'email' and 'xmpp' in visitors's permission choice list
if (groupName === 'visitors' && ['mail.main', 'xmpp.main'].includes(perm)) {
return false
}
return !group.permissions.includes(perm)
})
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
continue
}
if (['visitors', 'all_users'].includes(groupName)) {
group.isSpecial = true
} else {
group.availableMembers = userNames.filter(name => {
return !group.members.includes(name)
})
}
primaryGroups[groupName] = group
}
this.primaryGroups = primaryGroups
this.userGroups = userGroups
})
}, },
components: { components: {
SelectizeZone ZoneSelectize,
BaseSelectize
} }
} }
</script> </script>