#238 [admin] optimise the list of users, pagination and all
This commit is contained in:
parent
846ddfeea0
commit
b9a9524f1d
|
@ -48,8 +48,7 @@
|
||||||
|
|
||||||
<template v-slot:row="s">
|
<template v-slot:row="s">
|
||||||
<td>
|
<td>
|
||||||
<Avatar :user="s.el" dsize="2rem"/>
|
<a :href="'https://pronouns.page/@ + s.el.username'">@{{s.el.username}}</a>
|
||||||
{{s.el.username}}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
|
@ -57,11 +56,13 @@
|
||||||
{{s.el.email}}
|
{{s.el.email}}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
<!--
|
||||||
<ul v-if="s.el.socialConnections.length" class="list-inline">
|
<ul v-if="s.el.socialConnections.length" class="list-inline">
|
||||||
<li v-for="conn in s.el.socialConnections" class="list-inline-item">
|
<li v-for="conn in s.el.socialConnections" class="list-inline-item">
|
||||||
<Icon :v="socialProviders[conn].icon || conn" set="b"/>
|
<Icon :v="socialProviders[conn].icon || conn" set="b"/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
-->
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Roles :user="s.el"/>
|
<Roles :user="s.el"/>
|
||||||
|
@ -201,9 +202,12 @@
|
||||||
return {
|
return {
|
||||||
socialProviders,
|
socialProviders,
|
||||||
userFilter: '',
|
userFilter: '',
|
||||||
|
userFilterDelayed: '',
|
||||||
|
userFilterDelayHandle: undefined,
|
||||||
localeFilter: true,
|
localeFilter: true,
|
||||||
adminsFilter: false,
|
adminsFilter: false,
|
||||||
users: undefined,
|
users: undefined,
|
||||||
|
visibleUsers: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async asyncData({ app, store }) {
|
async asyncData({ app, store }) {
|
||||||
|
@ -225,7 +229,10 @@
|
||||||
methods: {
|
methods: {
|
||||||
async loadUsers() {
|
async loadUsers() {
|
||||||
if (this.users === undefined) {
|
if (this.users === undefined) {
|
||||||
this.users = await this.$axios.$get(`/admin/users`);
|
this.users = (await this.$axios.$get(`/admin/users`)).map(u => {
|
||||||
|
u.profiles = u.profiles ? u.profiles.split(',') : [];
|
||||||
|
return u;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async handleReport(id) {
|
async handleReport(id) {
|
||||||
|
@ -236,18 +243,18 @@
|
||||||
return r;
|
return r;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
calcVisibleUsers() {
|
||||||
computed: {
|
|
||||||
visibleUsers() {
|
|
||||||
if (this.users === undefined) {
|
if (this.users === undefined) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return Object.values(this.users).filter(u =>
|
return this.users.filter(u =>
|
||||||
u.username.toLowerCase().includes(this.userFilter.toLowerCase())
|
u.username.toLowerCase().includes(this.userFilterDelayed.toLowerCase())
|
||||||
&& (!this.adminsFilter || u.roles !== '')
|
&& (!this.adminsFilter || u.roles !== '')
|
||||||
&& (!this.localeFilter || u.profiles.includes(this.config.locale))
|
&& (!this.localeFilter || u.profiles.includes(this.config.locale))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
profilesByLocale() {
|
profilesByLocale() {
|
||||||
const r = {};
|
const r = {};
|
||||||
for (let locale of Object.values(this.stats.locales).sort((a, b) => b.profiles - a.profiles)) {
|
for (let locale of Object.values(this.stats.locales).sort((a, b) => b.profiles - a.profiles)) {
|
||||||
|
@ -256,6 +263,29 @@
|
||||||
return r;
|
return r;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
userFilter() {
|
||||||
|
if (this.userFilterDelayHandle !== undefined) {
|
||||||
|
clearInterval(this.userFilterDelayHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userFilterDelayHandle = setTimeout(() => {
|
||||||
|
this.userFilterDelayed = this.userFilter;
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
userFilterDelayed() {
|
||||||
|
this.visibleUsers = this.calcVisibleUsers();
|
||||||
|
},
|
||||||
|
localeFilter() {
|
||||||
|
this.visibleUsers = this.calcVisibleUsers();
|
||||||
|
},
|
||||||
|
adminsFilter() {
|
||||||
|
this.visibleUsers = this.calcVisibleUsers();
|
||||||
|
},
|
||||||
|
users() {
|
||||||
|
this.visibleUsers = this.calcVisibleUsers();
|
||||||
|
},
|
||||||
|
},
|
||||||
head() {
|
head() {
|
||||||
return head({
|
return head({
|
||||||
title: this.$t('admin.header'),
|
title: this.$t('admin.header'),
|
||||||
|
|
|
@ -74,38 +74,14 @@ router.get('/admin/users', handleErrorAsync(async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = await req.db.all(SQL`
|
const users = await req.db.all(SQL`
|
||||||
SELECT u.id, u.username, u.email, u.roles, u.avatarSource, p.locale
|
SELECT u.id, u.username, u.email, u.roles, u.avatarSource, group_concat(p.locale) AS profiles
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN profiles p ON p.userId = u.id
|
LEFT JOIN profiles p ON p.userId = u.id
|
||||||
|
GROUP BY u.id
|
||||||
ORDER BY u.id DESC
|
ORDER BY u.id DESC
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const authenticators = await req.db.all(SQL`
|
return res.json(users);
|
||||||
SELECT userId, type FROM authenticators
|
|
||||||
WHERE type IN (`.append(Object.keys(socialLoginConfig).map(k => `'${k}'`).join(',')).append(SQL`)
|
|
||||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
|
||||||
`));
|
|
||||||
|
|
||||||
const groupedUsers = {};
|
|
||||||
for (let user of users) {
|
|
||||||
if (groupedUsers[user.id] === undefined) {
|
|
||||||
groupedUsers[user.id] = {
|
|
||||||
...user,
|
|
||||||
locale: undefined,
|
|
||||||
profiles: user.locale ? [user.locale] : [],
|
|
||||||
avatar: await avatar(req.db, user),
|
|
||||||
socialConnections: [],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
groupedUsers[user.id].profiles.push(user.locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let auth of authenticators) {
|
|
||||||
groupedUsers[auth.userId].socialConnections.push(auth.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json(groupedUsers);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
router.get('/admin/stats', handleErrorAsync(async (req, res) => {
|
router.get('/admin/stats', handleErrorAsync(async (req, res) => {
|
||||||
|
|
Reference in New Issue