add base card and controls for GroupList view

This commit is contained in:
Axolotle 2020-08-01 14:16:32 +02:00
parent f4fe02f798
commit 90fc7d0092
4 changed files with 182 additions and 4 deletions

View file

@ -19,11 +19,11 @@
<b-input-group>
<b-input-group-prepend is-text>
<label class="sr-only" for="search-user">{{ $t('user.search') }}</label>
<label class="sr-only" for="selectize">{{ ariaLabel }}</label>
<icon :iname="searchIcon" v-if="searchIcon" />
</b-input-group-prepend>
<b-form-input
:class="visible ? null : 'collapsed'"
id="selectize" :class="visible ? null : 'collapsed'"
aria-controls="collapse" :aria-expanded="visible ? 'true' : 'false'"
@focus="onInputFocus" @blur="onInputBlur" @keydown="onInputKeydown"
v-model="search" ref="input"
@ -56,7 +56,8 @@ export default {
searchIcon: { type: String, default: 'search' },
itemIcon: { type: String, default: null },
itemRoute: { type: String, default: null },
itemVariant: { type: String, default: 'secondary' }
itemVariant: { type: String, default: 'secondary' },
ariaLabel: { type: String, required: true }
},
data: () => ({

View file

@ -337,6 +337,9 @@
"version": "Version",
"warnings": "%s warnings",
"warning_first_user": "You probably need to <a href='#/users/create' class='alert-link'>create a user</a> first.",
"words": {
"collapse": "Collapse"
},
"wrong_password": "Wrong password",
"yes": "Yes",
"certificate_alert_not_valid": "CRITICAL: Current certificate is not valid! HTTPS won't work at all!",

View file

@ -65,6 +65,7 @@ body {
.card-header h2 {
margin: 0;
font-size: 1.75rem;
}

View file

@ -1,13 +1,186 @@
<template lang="html">
<div class="group-list">
<div class="actions">
<b-input-group>
<b-input-group-prepend is-text>
<label class="sr-only" for="search-group">Search group</label>
<icon iname="search" />
</b-input-group-prepend>
<b-form-input id="search-group" v-model="search" placeholder="Search group" />
</b-input-group>
<div class="buttons">
<b-button variant="success" :to="{name: 'group-create'}">
<icon iname="plus" /> {{ $t('group_new') }}
</b-button>
</div>
</div>
<!-- PRIMARY GROUPS CARDS -->
<template v-if="groups">
<b-card
v-for="(group, name, index) in primaryGroups" :key="index"
no-body
>
<b-card-header class="d-flex align-items-center">
<h2>
<icon iname="group" />{{ group.isSpecial ? $t('group_' + name) : `${$t('group')} "${name}"` }}
</h2>
<div class="ml-auto">
<b-button v-b-toggle="'collapse-' + index" size="sm" variant="outline-secondary">
<icon iname="chevron-right" class="rotate" /><span class="sr-only">{{ $t('words.collapse') }}</span>
</b-button>
<b-button
v-if="!group.isSpecial" v-b-modal.delete-modal
variant="danger" class="ml-2" size="sm"
>
<icon :title="$t('delete')" iname="trash-o" /> <span class="sr-only">{{ $t('delete') }}</span>
</b-button>
</div>
</b-card-header>
<b-collapse :id="'collapse-' + index" visible>
<b-card-body>
<b-row>
<b-col md="3" lg="2">
<strong>{{ $t('users') }}</strong>
</b-col>
<b-col>
<template v-if="group.isSpecial">
<p><icon iname="info-circle" /> {{ $t('group_explain_' + name) }}</p>
<p v-if="name === 'visitors'">
<em>{{ $t('group_explain_visitors_needed_for_external_client') }}</em>
</p>
</template>
<template v-else>
<selectize-zone
:choices="group.membersInv" :selected="group.members"
item-icon="user" search-icon="user-plus" item-route="user-info"
:aria-label="$t('group_add_member')"
/>
</template>
</b-col>
</b-row>
<hr>
<b-row>
<b-col md="3" lg="2">
<strong>{{ $t('permissions') }}</strong>
</b-col>
<b-col>
<selectize-zone
:choices="group.permissionsInv" :selected="group.permissions"
item-icon="key-modern" item-variant="info"
:aria-label="$t('group_add_permission')"
/>
</b-col>
</b-row>
</b-card-body>
</b-collapse>
</b-card>
</template>
</div>
</template>
<script>
import SelectizeZone from '@/components/SelectizeZone'
// TODO add global search with type (search by: group, user, permission)
export default {
name: 'GroupList'
name: 'GroupList',
data: () => ({
search: ''
}),
computed: {
users () {
const users = this.$store.state.data.users
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
const search = this.search.toLowerCase()
const primaryGroups = {}
for (const [groupName, group] of Object.entries(groups)) {
if (group.isPrimary && groupName.toLowerCase().includes(search)) {
primaryGroups[groupName] = group
}
}
return primaryGroups
}
},
created () {
this.$store.dispatch('FETCH_ALL', [
{ uri: 'users' },
{ uri: 'users/groups?full&include_primary_groups', storeKey: 'groups' },
{ uri: 'users/permissions?short', storeKey: 'permissions' }
])
},
components: {
SelectizeZone
}
}
</script>
<style lang="scss" scoped>
.card + .card {
margin-top: 2rem;
}
.card-header {
h2 .icon {
font-size: 1.5rem;
width: 1.5rem;
margin-right: 1.25rem;
}
}
// collapse icon
.not-collapsed .icon {
transform: rotate(-90deg);
}
.collapsed .icon {
transform: rotate(90deg);
position: relative;
top: 2px;
}
.row > div:first-child {
margin-bottom: 1rem;
}
</style>