feat(settings): Split account management into navigation and content

The should ease the maintenance of it due to reduced complexity.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
pull/44092/head
Ferdinand Thiessen 2 months ago
parent 1100e908b7
commit 30d5b02811
No known key found for this signature in database
GPG Key ID: 45FAE7268762B400

@ -36,8 +36,7 @@
<NcLoadingIcon v-if="isInitialLoad && loading.users"
:name="t('settings', 'Loading accounts …')"
:size="64" />
<NcIconSvgWrapper v-else
:svg="usersSvg" />
<NcIconSvgWrapper v-else :path="mdiAccountGroup" :size="64" />
</template>
</NcEmptyContent>
@ -78,16 +77,16 @@
</template>
<script>
import Vue from 'vue'
import { mdiAccountGroup } from '@mdi/js'
import { showError } from '@nextcloud/dialogs'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { Fragment } from 'vue-frag'
import Vue from 'vue'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { showError } from '@nextcloud/dialogs'
import VirtualList from './Users/VirtualList.vue'
import NewUserModal from './Users/NewUserModal.vue'
import UserListFooter from './Users/UserListFooter.vue'
@ -97,9 +96,7 @@ import UserRow from './Users/UserRow.vue'
import { defaultQuota, isObfuscated, unlimitedQuota } from '../utils/userUtils.ts'
import logger from '../logger.ts'
import usersSvg from '../../img/users.svg?raw'
const newUser = {
const newUser = Object.freeze({
id: '',
displayName: '',
password: '',
@ -112,7 +109,7 @@ const newUser = {
code: 'en',
name: t('settings', 'Default language'),
},
}
})
export default {
name: 'UserList',
@ -139,19 +136,26 @@ export default {
},
},
data() {
setup() {
// non reactive properties
return {
mdiAccountGroup,
rowHeight: 55,
UserRow,
}
},
data() {
return {
loading: {
all: false,
groups: false,
users: false,
},
newUser: { ...newUser },
isInitialLoad: true,
rowHeight: 55,
usersSvg,
searchQuery: '',
newUser: Object.assign({}, newUser),
}
},
@ -252,7 +256,7 @@ export default {
watch: {
// watch url change and group select
async selectedGroup(val, old) {
async selectedGroup(val) {
this.isInitialLoad = true
// if selected is the disabled group but it's empty
await this.redirectIfDisabled()

@ -60,9 +60,8 @@
<NcAppSettingsSection id="default-settings"
:name="t('settings', 'Defaults')">
<label for="default-quota-select">{{ t('settings', 'Default quota') }}</label>
<NcSelect v-model="defaultQuota"
input-id="default-quota-select"
:input-label="t('settings', 'Default quota')"
placement="top"
:taggable="true"
:options="quotaOptions"
@ -75,9 +74,11 @@
</template>
<script>
import axios from '@nextcloud/axios'
import { getBuilder } from '@nextcloud/browser-storage'
import { formatFileSize, parseFileSize } from '@nextcloud/files'
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import NcAppSettingsDialog from '@nextcloud/vue/dist/Components/NcAppSettingsDialog.js'
import NcAppSettingsSection from '@nextcloud/vue/dist/Components/NcAppSettingsSection.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
@ -102,6 +103,15 @@ export default {
},
},
setup() {
const localStorage = getBuilder('settings')
.persist(true)
.clearOnLogout(true)
.build()
return { localStorage }
},
data() {
return {
selectedQuota: false,
@ -213,7 +223,7 @@ export default {
methods: {
getLocalstorage(key) {
// force initialization
const localConfig = this.$localStorage.get(key)
const localConfig = JSON.parse(this.localStorage.getItem(key) ?? 'null')
// if localstorage is null, fallback to original values
this.$store.commit('setShowConfig', { key, value: localConfig !== null ? localConfig === 'true' : this.showConfig[key] })
return this.showConfig[key]
@ -221,7 +231,7 @@ export default {
setLocalStorage(key, status) {
this.$store.commit('setShowConfig', { key, value: status })
this.$localStorage.set(key, status)
this.localStorage.setItem(key, JSON.stringify(status))
return status
},
@ -236,12 +246,12 @@ export default {
quota = quota?.id || quota.label
}
// only used for new presets sent through @Tag
const validQuota = OC.Util.computerFileSize(quota)
const validQuota = parseFileSize(quota)
if (validQuota === null) {
return unlimitedQuota
} else {
// unify format output
quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota))
quota = formatFileSize(parseFileSize(quota))
return { id: quota, label: quota }
}
},
@ -271,10 +281,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
label[for="default-quota-select"] {
display: block;
padding: 4px 0;
}
</style>

@ -0,0 +1,52 @@
import type { ComputedRef, Ref } from 'vue'
import type { IGroup } from '../views/user-types'
import { computed } from 'vue'
/**
* Format a group to a menu entry
*
* @param group the group
*/
function formatGroupMenu(group?: IGroup) {
if (typeof group === 'undefined') {
return null
}
const item = {
id: group.id,
title: group.name,
usercount: group.usercount,
count: Math.max(0, group.usercount - group.disabled),
}
return item
}
export const useFormatGroups = (groups: Ref<IGroup[]>|ComputedRef<IGroup[]>) => {
/**
* All non-disabled non-admin groups
*/
const userGroups = computed(() => {
const formatted = groups.value
// filter out disabled and admin
.filter(group => group.id !== 'disabled' && group.id !== 'admin')
// format group
.map(group => formatGroupMenu(group))
// remove invalid
.filter(group => group !== null)
return formatted as NonNullable<ReturnType<typeof formatGroupMenu>>[]
})
/**
* The admin group if found otherwise null
*/
const adminGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === 'admin')))
/**
* The group of disabled users
*/
const disabledGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === 'disabled')))
return { adminGroup, disabledGroup, userGroups }
}

@ -29,12 +29,13 @@ import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import SettingsApp from './views/SettingsApp.vue'
import router from './router/index.ts'
import store from './store/index.js'
import { useStore } from './store/index.js'
import { getRequestToken } from '@nextcloud/auth'
import { PiniaVuePlugin, createPinia } from 'pinia'
Vue.use(VTooltip, { defaultHtml: false })
const store = useStore()
sync(store, router)
// CSP config for webpack dynamic chunk loading
@ -44,10 +45,6 @@ __webpack_nonce__ = btoa(getRequestToken() ?? '')
// bind to window
Vue.prototype.t = t
Vue.prototype.n = n
Vue.prototype.OC = window.OC
Vue.prototype.OCA = window.OCA
// @ts-expect-error This is a private property we use
Vue.prototype.oc_userconfig = window.oc_userconfig
Vue.use(PiniaVuePlugin)
const pinia = createPinia()

@ -45,14 +45,20 @@ const mutations = {
},
}
export default new Store({
modules: {
users,
apps,
settings,
oc,
},
strict: debug,
let store = null
mutations,
})
export const useStore = () => {
if (store === null) {
store = new Store({
modules: {
users,
apps,
settings,
oc,
},
strict: debug,
mutations,
})
}
return store
}

@ -21,192 +21,31 @@
-->
<template>
<Fragment>
<NcAppNavigation :aria-label="t('settings', 'Account management')">
<NcAppNavigationNew button-id="new-user-button"
:text="t('settings','New account')"
@click="showNewUserMenu"
@keyup.enter="showNewUserMenu"
@keyup.space="showNewUserMenu">
<template #icon>
<Plus :size="20" />
</template>
</NcAppNavigationNew>
<NcAppNavigationList data-cy-users-settings-navigation-groups="system">
<NcAppNavigationItem id="everyone"
:exact="true"
:name="t('settings', 'Active accounts')"
:to="{ name: 'users' }">
<template #icon>
<AccountGroup :size="20" />
</template>
<template #counter>
<NcCounterBubble v-if="userCount" :type="!selectedGroupDecoded ? 'highlighted' : undefined">
{{ userCount }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
<NcAppNavigationItem v-if="settings.isAdmin"
id="admin"
:exact="true"
:name="t('settings', 'Admins')"
:to="{ name: 'group', params: { selectedGroup: 'admin' } }">
<template #icon>
<ShieldAccount :size="20" />
</template>
<template v-if="adminGroupMenu.count > 0" #counter>
<NcCounterBubble :type="selectedGroupDecoded === 'admin' ? 'highlighted' : undefined">
{{ adminGroupMenu.count }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
<!-- Hide the disabled if none, if we don't have the data (-1) show it -->
<NcAppNavigationItem v-if="disabledGroupMenu.usercount > 0 || disabledGroupMenu.usercount === -1"
id="disabled"
:exact="true"
:name="t('settings', 'Disabled users')"
:to="{ name: 'group', params: { selectedGroup: 'disabled' } }">
<template #icon>
<AccountOff :size="20" />
</template>
<template v-if="disabledGroupMenu.usercount > 0" #counter>
<NcCounterBubble :type="selectedGroupDecoded === 'disabled' ? 'highlighted' : undefined">
{{ disabledGroupMenu.usercount }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
</NcAppNavigationList>
<NcAppNavigationCaption :name="t('settings', 'Groups')"
:disabled="loadingAddGroup"
:aria-label="loadingAddGroup ? t('settings', 'Creating group …') : t('settings', 'Create group')"
force-menu
is-heading
:open.sync="isAddGroupOpen">
<template #actionsTriggerIcon>
<NcLoadingIcon v-if="loadingAddGroup" />
<Plus v-else :size="20" />
</template>
<template #actions>
<NcActionText>
<template #icon>
<AccountGroup :size="20" />
</template>
{{ t('settings', 'Create group') }}
</NcActionText>
<NcActionInput :label="t('settings', 'Group name')"
data-cy-users-settings-new-group-name
:label-outside="false"
:disabled="loadingAddGroup"
:value.sync="newGroupName"
:error="hasAddGroupError"
:helper-text="hasAddGroupError ? t('settings', 'Please enter a valid group name') : ''"
@submit="createGroup" />
</template>
</NcAppNavigationCaption>
<NcAppNavigationList data-cy-users-settings-navigation-groups="custom">
<GroupListItem v-for="group in groupList"
:id="group.id"
:key="group.id"
:active="selectedGroupDecoded === group.id"
:name="group.title"
:count="group.count" />
</NcAppNavigationList>
<template #footer>
<ul class="app-navigation-entry__settings">
<NcAppNavigationItem :name="t('settings', 'Account management settings')"
@click="isDialogOpen = true">
<template #icon>
<Cog :size="20" />
</template>
</NcAppNavigationItem>
</ul>
</template>
</NcAppNavigation>
<NcAppContent :page-heading="pageHeading">
<UserList :selected-group="selectedGroupDecoded"
:external-actions="externalActions" />
</NcAppContent>
<UserSettingsDialog :open.sync="isDialogOpen" />
</Fragment>
<NcAppContent :page-heading="pageHeading">
<UserList :selected-group="selectedGroupDecoded"
:external-actions="externalActions" />
</NcAppContent>
</template>
<script>
import Vue from 'vue'
import VueLocalStorage from 'vue-localstorage'
import { Fragment } from 'vue-frag'
import { translate as t } from '@nextcloud/l10n'
import { showError } from '@nextcloud/dialogs'
import { defineComponent } from 'vue'
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
import NcAppNavigationCaption from '@nextcloud/vue/dist/Components/NcAppNavigationCaption.js'
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
import NcAppNavigationList from '@nextcloud/vue/dist/Components/NcAppNavigationList.js'
import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js'
import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
import AccountOff from 'vue-material-design-icons/AccountOff.vue'
import Cog from 'vue-material-design-icons/Cog.vue'
import Plus from 'vue-material-design-icons/Plus.vue'
import GroupListItem from '../components/GroupListItem.vue'
import UserList from '../components/UserList.vue'
import UserSettingsDialog from '../components/Users/UserSettingsDialog.vue'
Vue.use(VueLocalStorage)
export default {
export default defineComponent({
name: 'UserManagement',
components: {
AccountGroup,
AccountOff,
Cog,
Fragment,
GroupListItem,
NcActionInput,
NcActionText,
NcAppContent,
NcAppNavigation,
NcAppNavigationCaption,
NcAppNavigationItem,
NcAppNavigationList,
NcAppNavigationNew,
NcCounterBubble,
NcLoadingIcon,
Plus,
UserList,
UserSettingsDialog,
},
props: {
selectedGroup: {
type: String,
default: null,
},
},
data() {
return {
// temporary value used for multiselect change
externalActions: [],
newGroupName: '',
isAddGroupOpen: false,
loadingAddGroup: false,
hasAddGroupError: false,
isDialogOpen: false,
}
},
@ -222,54 +61,13 @@ export default {
return matchHeading[this.selectedGroupDecoded] ?? t('settings', 'Account group: {group}', { group: this.selectedGroupDecoded })
},
showConfig() {
return this.$store.getters.getShowConfig
selectedGroup() {
return this.$route.params.selectedGroup
},
selectedGroupDecoded() {
return this.selectedGroup ? decodeURIComponent(this.selectedGroup) : null
},
users() {
return this.$store.getters.getUsers
},
groups() {
return this.$store.getters.getGroups
},
usersOffset() {
return this.$store.getters.getUsersOffset
},
usersLimit() {
return this.$store.getters.getUsersLimit
},
userCount() {
return this.$store.getters.getUserCount
},
settings() {
return this.$store.getters.getServerData
},
groupList() {
const groups = Array.isArray(this.groups) ? this.groups : []
return groups
// filter out disabled and admin
.filter(group => group.id !== 'disabled' && group.id !== 'admin')
.map(group => this.formatGroupMenu(group))
},
adminGroupMenu() {
return this.formatGroupMenu(this.groups.find(group => group.id === 'admin'))
},
disabledGroupMenu() {
return this.formatGroupMenu(this.groups.find(group => group.id === 'disabled'))
},
},
beforeMount() {
@ -283,26 +81,16 @@ export default {
created() {
// init the OCA.Settings.UserList object
window.OCA = window.OCA ?? {}
window.OCA.Settings = window.OCA.Settings ?? {}
window.OCA.Settings.UserList = window.OCA.Settings.UserList ?? {}
// and add the registerAction method
Object.assign(OCA, {
Settings: {
UserList: {
registerAction: this.registerAction,
},
},
})
window.OCA.Settings.UserList.registerAction = this.registerAction
},
methods: {
t,
showNewUserMenu() {
this.$store.commit('setShowConfig', {
key: 'showNewUserForm',
value: true,
})
},
/**
* Register a new action for the user menu
*
@ -319,60 +107,8 @@ export default {
})
return this.externalActions
},
/**
* Create a new group
*/
async createGroup() {
this.hasAddGroupError = false
const groupId = this.newGroupName.trim()
if (groupId === '') {
this.hasAddGroupError = true
return
}
this.isAddGroupOpen = false
this.loadingAddGroup = true
try {
await this.$store.dispatch('addGroup', groupId)
await this.$router.push({
name: 'group',
params: {
selectedGroup: encodeURIComponent(groupId),
},
})
this.newGroupName = ''
} catch {
showError(t('settings', 'Failed to create group'))
}
this.loadingAddGroup = false
},
/**
* Format a group to a menu entry
*
* @param {object} group the group
* @return {object}
*/
formatGroupMenu(group) {
const item = {}
if (typeof group === 'undefined') {
return {}
}
item.id = group.id
item.title = group.name
item.usercount = group.usercount
// users count for all groups
if (group.usercount - group.disabled > 0) {
item.count = group.usercount - group.disabled
}
return item
},
},
}
})
</script>
<style lang="scss" scoped>
@ -383,10 +119,4 @@ export default {
flex-direction: column;
max-height: 100%;
}
.app-navigation-entry__settings {
height: auto !important;
// Prevent shrinking or growing
flex: 0 0 auto;
}
</style>

@ -1,5 +1,222 @@
<template>
<div>...</div>
<NcAppNavigation :aria-label="t('settings', 'Account management')">
<NcAppNavigationNew button-id="new-user-button"
:text="t('settings','New account')"
@click="showNewUserMenu"
@keyup.enter="showNewUserMenu"
@keyup.space="showNewUserMenu">
<template #icon>
<NcIconSvgWrapper :path="mdiPlus" />
</template>
</NcAppNavigationNew>
<NcAppNavigationList class="account-management__system-list"
data-cy-users-settings-navigation-groups="system">
<NcAppNavigationItem id="everyone"
:exact="true"
:name="t('settings', 'Active accounts')"
:to="{ name: 'users' }">
<template #icon>
<NcIconSvgWrapper :path="mdiAccount" />
</template>
<template #counter>
<NcCounterBubble v-if="userCount" :type="!selectedGroupDecoded ? 'highlighted' : undefined">
{{ userCount }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
<NcAppNavigationItem v-if="isAdmin"
id="admin"
:exact="true"
:name="t('settings', 'Admins')"
:to="{ name: 'group', params: { selectedGroup: 'admin' } }">
<template #icon>
<NcIconSvgWrapper :path="mdiShieldAccount" />
</template>
<template #counter>
<NcCounterBubble v-if="adminGroup && adminGroup.count > 0"
:type="selectedGroupDecoded === 'admin' ? 'highlighted' : undefined">
{{ adminGroup.count }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
<!-- Hide the disabled if none, if we don't have the data (-1) show it -->
<NcAppNavigationItem v-if="disabledGroup && (disabledGroup.usercount > 0 || disabledGroup.usercount === -1)"
id="disabled"
:exact="true"
:name="t('settings', 'Disabled accounts')"
:to="{ name: 'group', params: { selectedGroup: 'disabled' } }">
<template #icon>
<NcIconSvgWrapper :path="mdiAccountOff" />
</template>
<template v-if="disabledGroup.usercount > 0" #counter>
<NcCounterBubble :type="selectedGroupDecoded === 'disabled' ? 'highlighted' : undefined">
{{ disabledGroup.usercount }}
</NcCounterBubble>
</template>
</NcAppNavigationItem>
</NcAppNavigationList>
<NcAppNavigationCaption :name="t('settings', 'Groups')"
:disabled="loadingAddGroup"
:aria-label="loadingAddGroup ? t('settings', 'Creating group…') : t('settings', 'Create group')"
force-menu
is-heading
:open.sync="isAddGroupOpen">
<template #actionsTriggerIcon>
<NcLoadingIcon v-if="loadingAddGroup" />
<NcIconSvgWrapper v-else :path="mdiPlus" />
</template>
<template #actions>
<NcActionText>
<template #icon>
<AccountGroup :size="20" />
</template>
{{ t('settings', 'Create group') }}
</NcActionText>
<NcActionInput :label="t('settings', 'Group name')"
data-cy-users-settings-new-group-name
:label-outside="false"
:disabled="loadingAddGroup"
:value.sync="newGroupName"
:error="hasAddGroupError"
:helper-text="hasAddGroupError ? t('settings', 'Please enter a valid group name') : ''"
@submit="createGroup" />
</template>
</NcAppNavigationCaption>
<NcAppNavigationList class="account-management__group-list" data-cy-users-settings-navigation-groups="custom">
<GroupListItem v-for="group in userGroups"
:id="group.id"
:key="group.id"
:active="selectedGroupDecoded === group.id"
:name="group.title"
:count="group.count" />
</NcAppNavigationList>
<template #footer>
<NcButton class="account-management__settings-toggle"
type="tertiary"
@click="isDialogOpen = true">
<template #icon>
<NcIconSvgWrapper :path="mdiCog" />
</template>
{{ t('settings', 'Account management settings') }}
</NcButton>
<UserSettingsDialog :open.sync="isDialogOpen" />
</template>
</NcAppNavigation>
</template>
<script setup>
<script setup lang="ts">
import { mdiAccount, mdiAccountOff, mdiCog, mdiPlus, mdiShieldAccount } from '@mdi/js'
import { showError } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import { computed, ref } from 'vue'
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
import NcAppNavigationCaption from '@nextcloud/vue/dist/Components/NcAppNavigationCaption.js'
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
import NcAppNavigationList from '@nextcloud/vue/dist/Components/NcAppNavigationList.js'
import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import GroupListItem from '../components/GroupListItem.vue'
import UserSettingsDialog from '../components/Users/UserSettingsDialog.vue'
import { useStore } from '../store'
import { useRoute, useRouter } from 'vue-router/composables'
import { useFormatGroups } from '../composables/useGroupsNavigation'
const route = useRoute()
const router = useRouter()
const store = useStore()
/** State of the 'new-account' dialog */
const isDialogOpen = ref(false)
/** Current active group in the view - this is URL encoded */
const selectedGroup = computed(() => route.params?.selectedGroup)
/** Current active group - URL decoded */
const selectedGroupDecoded = computed(() => selectedGroup.value ? decodeURIComponent(selectedGroup.value) : null)
/** Overall user count */
const userCount = computed(() => store.getters.getUserCount)
/** All available groups */
const groups = computed(() => store.getters.getGroups)
const { adminGroup, disabledGroup, userGroups } = useFormatGroups(groups)
/** True if the current user is an administrator */
const isAdmin = computed(() => store.getters.getServerData.isAdmin)
/** True if the 'add-group' dialog is open - needed to be able to close it when the group is created */
const isAddGroupOpen = ref(false)
/** True if the group creation is in progress to show loading spinner and disable adding another one */
const loadingAddGroup = ref(false)
/** Error state for creating a new group */
const hasAddGroupError = ref(false)
/** Name of the group to create (used in the group creation dialog) */
const newGroupName = ref('')
/**
* Create a new group
*/
async function createGroup() {
hasAddGroupError.value = false
const groupId = newGroupName.value.trim()
if (groupId === '') {
hasAddGroupError.value = true
return
}
isAddGroupOpen.value = false
loadingAddGroup.value = true
try {
await store.dispatch('addGroup', groupId)
await router.push({
name: 'group',
params: {
selectedGroup: encodeURIComponent(groupId),
},
})
newGroupName.value = ''
} catch {
showError(t('settings', 'Failed to create group'))
}
loadingAddGroup.value = false
}
/**
* Open the new-user form dialog
*/
function showNewUserMenu() {
store.commit('setShowConfig', {
key: 'showNewUserForm',
value: true,
})
}
</script>
<style scoped lang="scss">
.account-management{
&__system-list {
height: auto !important;
overflow: visible !important;
}
&__group-list {
height: 100% !important;
}
&__settings-toggle {
margin-bottom: 12px;
}
}
</style>

@ -0,0 +1,14 @@
export interface IGroup {
id: string
name: string
/**
* Overall user count
*/
usercount: number
/**
* Number of disabled users
*/
disabled: number
}

@ -34,7 +34,7 @@ describe('Settings: Show and hide columns', function() {
beforeEach(function() {
// open the settings dialog
cy.get('.app-navigation-entry__settings').contains('Account management settings').click()
cy.contains('button', 'Account management settings').click()
// reset all visibility toggles
cy.get('.modal-container #settings-section_visibility-settings input[type="checkbox"]').uncheck({ force: true })
@ -57,7 +57,7 @@ describe('Settings: Show and hide columns', function() {
})
// open the settings dialog
cy.get('.app-navigation-entry__settings').contains('Account management settings').click()
cy.contains('button', 'Account management settings').click()
cy.contains('.modal-container', 'Account management settings').within(() => {
// enable the language toggle
@ -88,7 +88,7 @@ describe('Settings: Show and hide columns', function() {
})
// open the settings dialog
cy.get('.app-navigation-entry__settings').contains('Account management settings').click()
cy.contains('button', 'Account management settings').click()
cy.contains('.modal-container', 'Account management settings').within(() => {
// disable the last login toggle

Loading…
Cancel
Save