2020-10-29 15:41:40 -07:00
|
|
|
<template>
|
|
|
|
<NotFound v-if="!$admin()"/>
|
2020-11-16 11:43:44 -08:00
|
|
|
<div v-else>
|
2020-10-29 15:41:40 -07:00
|
|
|
<h2>
|
|
|
|
<Icon v="user-cog"/>
|
|
|
|
<T>admin.header</T>
|
|
|
|
</h2>
|
|
|
|
|
2020-11-24 16:11:17 -08:00
|
|
|
<section>
|
|
|
|
<details class="border mb-3">
|
|
|
|
<summary class="bg-light p-3">
|
|
|
|
<Icon v="users"/>
|
|
|
|
Users
|
|
|
|
({{stats.users.overall}}, {{stats.users.admins}} admins)
|
|
|
|
</summary>
|
|
|
|
<div class="border-top">
|
2020-12-22 02:05:33 -08:00
|
|
|
<input class="form-control mt-4" v-model="userFilter" :placeholder="$t('crud.filterLong')"/>
|
|
|
|
<Table :data="visibleUsers" :columns="4">
|
2020-11-24 16:11:17 -08:00
|
|
|
<template v-slot:header>
|
|
|
|
<th class="text-nowrap">
|
|
|
|
<T>admin.user.user</T>
|
|
|
|
</th>
|
|
|
|
<th class="text-nowrap">
|
|
|
|
<T>admin.user.email</T>
|
|
|
|
</th>
|
|
|
|
<th class="text-nowrap">
|
|
|
|
<T>admin.user.roles</T>
|
|
|
|
</th>
|
|
|
|
<th class="text-nowrap">
|
|
|
|
<T>admin.user.profiles</T>
|
|
|
|
</th>
|
|
|
|
</template>
|
2020-10-29 15:41:40 -07:00
|
|
|
|
2020-11-24 16:11:17 -08:00
|
|
|
<template v-slot:row="s">
|
|
|
|
<td>
|
|
|
|
<Avatar :user="s.el" dsize="2rem"/>
|
|
|
|
{{s.el.username}}
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<p>
|
|
|
|
<a :href="`mailto:${s.el.email}`" target="_blank" rel="noopener">
|
|
|
|
{{s.el.email}}
|
|
|
|
</a>
|
|
|
|
</p>
|
|
|
|
<ul v-if="s.el.socialConnections.length" class="list-inline">
|
|
|
|
<li v-for="conn in s.el.socialConnections" class="list-inline-item">
|
|
|
|
<Icon :v="socialProviders[conn].icon || conn" set="b"/>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<a href="#" :class="['badge', s.el.roles === 'admin' ? 'badge-primary' : 'badge-light']"
|
|
|
|
@click.prevent="setRole(s.el.id, s.el.roles === 'admin' ? 'user' : 'admin')">
|
|
|
|
{{s.el.roles}}
|
|
|
|
</a>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<ul class="list-unstyled">
|
|
|
|
<li v-for="locale in s.el.profiles" v-if="locales[locale]">
|
|
|
|
<LocaleLink :link="`/@${s.el.username}`" :locale="locale">
|
|
|
|
{{ locales[locale].name }}
|
|
|
|
</LocaleLink>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</td>
|
|
|
|
</template>
|
|
|
|
</Table>
|
|
|
|
</div>
|
|
|
|
</details>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section v-for="(locale, k) in stats.locales" :key="k">
|
|
|
|
<details class="border mb-3" open>
|
|
|
|
<summary class="bg-light p-3">
|
|
|
|
<LocaleLink :locale="k" link="/">{{locale.name}}</LocaleLink>
|
|
|
|
</summary>
|
|
|
|
<div class="p-3 border-top d-flex justify-content-between flex-column flex-md-row">
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<h4 class="h5">
|
|
|
|
<Icon v="id-card"/>
|
|
|
|
Profiles
|
|
|
|
</h4>
|
|
|
|
{{locale.profiles}}
|
|
|
|
</div>
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<h4 class="h5">
|
|
|
|
<Icon v="tags"/>
|
|
|
|
Pronouns
|
|
|
|
</h4>
|
2020-11-27 11:30:21 -08:00
|
|
|
<ListExpandable :data="locale.pronouns"/>
|
|
|
|
</div>
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<h4 class="h5">
|
|
|
|
<Icon v="flag"/>
|
|
|
|
Flags
|
|
|
|
</h4>
|
|
|
|
<ListExpandable :data="locale.flags"/>
|
2020-11-24 16:11:17 -08:00
|
|
|
</div>
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<h4 class="h5">
|
|
|
|
<Icon v="atom-alt"/>
|
|
|
|
Dictionary
|
|
|
|
</h4>
|
|
|
|
<ul class="list-unstyled">
|
|
|
|
<li>
|
|
|
|
<strong>Approved</strong>: {{locale.nouns.approved}}
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<strong>Awaiting</strong>: {{locale.nouns.awaiting}}
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</details>
|
|
|
|
</section>
|
2020-10-29 15:41:40 -07:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import {head} from "../src/helpers";
|
2020-11-02 12:45:45 -08:00
|
|
|
import {socialProviders} from "../src/data";
|
2020-10-29 15:41:40 -07:00
|
|
|
|
|
|
|
export default {
|
2020-11-02 12:45:45 -08:00
|
|
|
data() {
|
2020-11-27 11:30:21 -08:00
|
|
|
return {
|
|
|
|
socialProviders,
|
2020-12-22 02:05:33 -08:00
|
|
|
userFilter: '',
|
2020-11-27 11:30:21 -08:00
|
|
|
}
|
2020-11-02 12:45:45 -08:00
|
|
|
},
|
2020-10-29 15:41:40 -07:00
|
|
|
async asyncData({ app, store }) {
|
|
|
|
if (!store.state.user || store.state.user.roles !== 'admin') {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-11-24 16:11:17 -08:00
|
|
|
const stats = await app.$axios.$get(`/admin/stats`);
|
|
|
|
|
|
|
|
const users = await app.$axios.$get(`/admin/users`);
|
2020-10-29 15:41:40 -07:00
|
|
|
|
|
|
|
return {
|
2020-11-24 16:11:17 -08:00
|
|
|
stats,
|
2020-11-03 01:03:07 -08:00
|
|
|
users,
|
2020-10-29 15:41:40 -07:00
|
|
|
};
|
|
|
|
},
|
2020-11-03 01:03:07 -08:00
|
|
|
methods: {
|
|
|
|
async setRole(userId, role) {
|
|
|
|
await this.$confirm(this.$t('admin.user.confirmRole', {username: this.users[userId].username, role}));
|
|
|
|
|
|
|
|
const response = await this.$axios.$post(`/user/${userId}/set-roles`, { roles: role });
|
|
|
|
|
|
|
|
this.users[userId].roles = role;
|
|
|
|
}
|
|
|
|
},
|
2020-12-22 02:05:33 -08:00
|
|
|
computed: {
|
|
|
|
visibleUsers() {
|
|
|
|
return Object.values(this.users).filter(u => u.username.toLowerCase().includes(this.userFilter.toLowerCase()));
|
|
|
|
},
|
|
|
|
},
|
2020-10-29 15:41:40 -07:00
|
|
|
head() {
|
|
|
|
return head({
|
|
|
|
title: this.$t('admin.header'),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|