Skip to content

Commit

Permalink
Merge pull request #45716 from nextcloud/feat/recent-accounts-section
Browse files Browse the repository at this point in the history
feat: Add Recently active accounts section
  • Loading branch information
Pytal authored Jul 11, 2024
2 parents 19c8c63 + 281d837 commit 64701f8
Show file tree
Hide file tree
Showing 89 changed files with 197 additions and 137 deletions.
10 changes: 8 additions & 2 deletions apps/settings/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,15 @@ public function usersList(): TemplateResponse {
$userCount -= $disabledUsers;
}

$recentUsersGroup = [
'id' => '__nc_internal_recent',
'name' => $this->l10n->t('Recently active'),
'usercount' => $userCount,
];

$disabledUsersGroup = [
'id' => 'disabled',
'name' => 'Disabled accounts',
'name' => $this->l10n->t('Disabled accounts'),
'usercount' => $disabledUsers
];

Expand All @@ -192,7 +198,7 @@ public function usersList(): TemplateResponse {
/* FINAL DATA */
$serverData = [];
// groups
$serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
$serverData['groups'] = array_merge_recursive($adminGroup, [$recentUsersGroup, $disabledUsersGroup], $groups);
// Various data
$serverData['isAdmin'] = $isAdmin;
$serverData['sortGroups'] = $forceSortGroupByName
Expand Down
10 changes: 8 additions & 2 deletions apps/settings/src/components/UserList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ export default {
},
groups() {
// data provided php side + remove the disabled group
// data provided php side + remove the recent and disabled groups
return this.$store.getters.getGroups
.filter(group => group.id !== 'disabled')
.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
.sort((a, b) => a.name.localeCompare(b.name))
},
Expand Down Expand Up @@ -298,6 +298,12 @@ export default {
limit: this.disabledUsersLimit,
search: this.searchQuery,
})
} else if (this.selectedGroup === '__nc_internal_recent') {
await this.$store.dispatch('getRecentUsers', {
offset: this.usersOffset,
limit: this.usersLimit,
search: this.searchQuery,
})
} else {
await this.$store.dispatch('getUsers', {
offset: this.usersOffset,
Expand Down
4 changes: 2 additions & 2 deletions apps/settings/src/components/Users/NewUserDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ export default {
},
groups() {
// data provided php side + remove the disabled group
// data provided php side + remove the recent and disabled groups
return this.$store.getters.getGroups
.filter(group => group.id !== 'disabled')
.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
.sort((a, b) => a.name.localeCompare(b.name))
},
Expand Down
9 changes: 7 additions & 2 deletions apps/settings/src/composables/useGroupsNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const useFormatGroups = (groups: Ref<IGroup[]>|ComputedRef<IGroup[]>) =>
const userGroups = computed(() => {
const formatted = groups.value
// filter out disabled and admin
.filter(group => group.id !== 'disabled' && group.id !== 'admin')
.filter(group => group.id !== 'disabled' && group.id !== '__nc_internal_recent' && group.id !== 'admin')
// format group
.map(group => formatGroupMenu(group))
// remove invalid
Expand All @@ -52,5 +52,10 @@ export const useFormatGroups = (groups: Ref<IGroup[]>|ComputedRef<IGroup[]>) =>
*/
const disabledGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === 'disabled')))

return { adminGroup, disabledGroup, userGroups }
/**
* The group of recent users
*/
const recentGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === '__nc_internal_recent')))

return { adminGroup, recentGroup, disabledGroup, userGroups }
}
32 changes: 30 additions & 2 deletions apps/settings/src/store/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,21 @@ const mutations = {
return
}

const recentGroup = state.groups.find(group => group.id === '__nc_internal_recent')
const disabledGroup = state.groups.find(group => group.id === 'disabled')
switch (actionType) {
case 'enable':
case 'disable':
disabledGroup.usercount += user.enabled ? -1 : 1 // update Disabled Users count
recentGroup.usercount += user.enabled ? 1 : -1
state.userCount += user.enabled ? 1 : -1 // update Active Users count
user.groups.forEach(userGroup => {
const group = state.groups.find(groupSearch => groupSearch.id === userGroup)
group.disabled += user.enabled ? -1 : 1 // update group disabled count
})
break
case 'create':
recentGroup.usercount++
state.userCount++ // increment Active Users count

user.groups.forEach(userGroup => {
Expand All @@ -168,6 +171,7 @@ const mutations = {
break
case 'remove':
if (user.enabled) {
recentGroup.usercount--
state.userCount-- // decrement Active Users count
user.groups.forEach(userGroup => {
const group = state.groups.find(groupSearch => groupSearch.id === userGroup)
Expand Down Expand Up @@ -241,8 +245,8 @@ const getters = {
return state.groups
},
getSubadminGroups(state) {
// Can't be subadmin of admin or disabled
return state.groups.filter(group => group.id !== 'admin' && group.id !== 'disabled')
// Can't be subadmin of admin, recent, or disabled
return state.groups.filter(group => group.id !== 'admin' && group.id !== '__nc_internal_recent' && group.id !== 'disabled')
},
getSortedGroups(state) {
const groups = [...state.groups]
Expand Down Expand Up @@ -383,6 +387,30 @@ const actions = {
})
},

/**
* Get recent users with full details
*
* @param {object} context store context
* @param {object} options destructuring object
* @param {number} options.offset List offset to request
* @param {number} options.limit List number to return from offset
* @param {string} options.search Search query
* @return {Promise<number>}
*/
async getRecentUsers(context, { offset, limit, search }) {
const url = generateOcsUrl('cloud/users/recent?offset={offset}&limit={limit}&search={search}', { offset, limit, search })
try {
const response = await api.get(url)
const usersCount = Object.keys(response.data.ocs.data.users).length
if (usersCount > 0) {
context.commit('appendUsers', response.data.ocs.data.users)
}
return usersCount
} catch (error) {
context.commit('API_FAILURE', error)
}
},

/**
* Get disabled users with full details
*
Expand Down
2 changes: 1 addition & 1 deletion apps/settings/src/views/UserManagement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default defineComponent({
computed: {
pageHeading() {
if (this.selectedGroupDecoded === null) {
return t('settings', 'Active accounts')
return t('settings', 'All accounts')
}
const matchHeading = {
admin: t('settings', 'Admins'),
Expand Down
21 changes: 18 additions & 3 deletions apps/settings/src/views/UserManagementNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
data-cy-users-settings-navigation-groups="system">
<NcAppNavigationItem id="everyone"
:exact="true"
:name="t('settings', 'Active accounts')"
:name="t('settings', 'All accounts')"
:to="{ name: 'users' }">
<template #icon>
<NcIconSvgWrapper :path="mdiAccount" />
Expand Down Expand Up @@ -46,6 +46,21 @@
</template>
</NcAppNavigationItem>

<NcAppNavigationItem id="recent"
:exact="true"
:name="t('settings', 'Recently active')"
:to="{ name: 'group', params: { selectedGroup: '__nc_internal_recent' } }">
<template #icon>
<NcIconSvgWrapper :path="mdiHistory" />
</template>
<template #counter>
<NcCounterBubble v-if="recentGroup?.usercount > 0"
:type="selectedGroupDecoded === '__nc_internal_recent' ? 'highlighted' : undefined">
{{ recentGroup.usercount }}
</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"
Expand Down Expand Up @@ -115,7 +130,7 @@
</template>
<script setup lang="ts">
import { mdiAccount, mdiAccountOff, mdiCog, mdiPlus, mdiShieldAccount } from '@mdi/js'
import { mdiAccount, mdiAccountOff, mdiCog, mdiPlus, mdiShieldAccount, mdiHistory } from '@mdi/js'
import { showError } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import { computed, ref } from 'vue'
Expand Down Expand Up @@ -154,7 +169,7 @@ const selectedGroupDecoded = computed(() => selectedGroup.value ? decodeURICompo
const userCount = computed(() => store.getters.getUserCount)
/** All available groups */
const groups = computed(() => store.getters.getSortedGroups)
const { adminGroup, disabledGroup, userGroups } = useFormatGroups(groups)
const { adminGroup, recentGroup, disabledGroup, userGroups } = useFormatGroups(groups)
/** True if the current user is an administrator */
const isAdmin = computed(() => store.getters.getServerData.isAdmin)
Expand Down
Loading

0 comments on commit 64701f8

Please sign in to comment.