forked from kaverti/website
313 lines
9.2 KiB
Vue
313 lines
9.2 KiB
Vue
<template>
|
|
<div class='admin_users' ref='scrollElement'>
|
|
<modal-window v-model='showBadgeModal' :loading='loading' style='z-index: 99; '>
|
|
<div slot="header">
|
|
Modify {{user.username}} <b-tooltip v-if='user' class="is-info" label="This page allows you to modify users badges that appear on user profiles, user list, and various other pages. Abusing this feature will leave you demoted.">
|
|
<b-tag class="is-info" rounded><i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
</div>
|
|
<div slot='main' class="card-content">
|
|
<br>
|
|
<div>
|
|
<p>User Profile Badges:</p>
|
|
<b-switch type="is-dark" v-model="user.booster">
|
|
Booster
|
|
</b-switch><br>
|
|
<b-switch type="is-success" v-model="user.system">
|
|
System
|
|
</b-switch><br>
|
|
<b-switch v-model="user.bot">
|
|
Bot
|
|
</b-switch>
|
|
</div>
|
|
</div>
|
|
<div slot='footer'>
|
|
<button class='button is-info' @click='modifyUser(user)'>Save</button>
|
|
</div>
|
|
</modal-window>
|
|
<h1 class='admin_users__header'>Users</h1>
|
|
<div class='category_widget__box'>
|
|
<h2>Search</h2>
|
|
<div class='admin_users__filters'>
|
|
<b-input
|
|
:large='false'
|
|
v-model='search'
|
|
placeholder="Search for a user"
|
|
></b-input>
|
|
|
|
</div>
|
|
</div>
|
|
<scroll-load
|
|
class='category_widget__box'
|
|
@loadNext='fetchData'
|
|
:loading='loading'
|
|
query-selector='.admin_users'
|
|
:padding-bottom='100'
|
|
>
|
|
<table>
|
|
<tr>
|
|
<th>
|
|
<sort-menu v-model='tableSort' column='username' display='Username'></sort-menu>
|
|
</th>
|
|
<th>
|
|
Badges
|
|
</th>
|
|
<th>
|
|
<sort-menu v-model='tableSort' column='createdAt' display='Account created at'></sort-menu>
|
|
</th>
|
|
<th>
|
|
<sort-menu v-model='tableSort' column='postCount' display='Posts count'></sort-menu>
|
|
</th>
|
|
<th>
|
|
<sort-menu v-model='tableSort' column='threadCount' display='Threads count'></sort-menu>
|
|
</th>
|
|
</tr>
|
|
<tr v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.hidden">
|
|
<td class='admin_users__user_column'>
|
|
<avatar-icon :user='user' size='small'></avatar-icon>
|
|
<router-link :to='"/user/" + user.username'>{{user.username}}</router-link>
|
|
</td>
|
|
<td>
|
|
<b-tooltip v-if='user && user.system' class="is-success" label="This user is a system user operated by administrators that mainly run API operations.">
|
|
<b-tag rounded class="is-success"> SYSTEM <i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
|
|
<b-tooltip v-if='user && user.bot' class="is-info" label="This user is a bot account that can run automated API operations.">
|
|
<b-tag rounded class="is-info"> BOT <i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
|
|
<b-tooltip v-if='user && user.admin' class="is-danger" label="User is an official Kaverti administrator.">
|
|
<b-tag class="is-danger" rounded> ADMIN <i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
|
|
<b-tooltip v-if='user && user.hidden' class="is-info" label="User is not discoverable in the user list.">
|
|
<b-tag rounded> HIDDEN <i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
|
|
<b-tooltip v-if='user && user.booster' class="is-light" label="User is boosting the Kaverti Discord server.">
|
|
<b-tag class="is-light" rounded> BOOSTER <i class="fas fa-info-circle"></i></b-tag>
|
|
</b-tooltip>
|
|
|
|
<b-button v-if="!user.admin" class="is-info is-small" rounded @click='toggleBadgeModal(user)'>
|
|
<i class="fas fa-plus"></i>
|
|
</b-button>
|
|
<b-tooltip label="You cannot edit admin users">
|
|
<b-button v-if="user.admin" disabled="disabled" class="is-info is-small" rounded @click='toggleBadgeModal(user)'>
|
|
<i class="fas fa-plus"></i>
|
|
</b-button>
|
|
</b-tooltip>
|
|
</td>
|
|
<td>{{user.createdAt | formatDate}}</td>
|
|
<td>{{user.postCount}}</td>
|
|
<td>{{user.threadCount}}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<transition name='fade' mode='out-in'>
|
|
<loading-message key='loading' v-if='loading'></loading-message>
|
|
<div class='overlay_message' v-if='!loading && !users.length'>
|
|
No users found
|
|
</div>
|
|
</transition>
|
|
</scroll-load>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import SortMenu from '../SortMenu.vue';
|
|
import LoadingMessage from '../LoadingMessage';
|
|
import ScrollLoad from '../ScrollLoad';
|
|
import AvatarIcon from '../AvatarIcon';
|
|
import ModalWindow from '../ModalWindow'
|
|
|
|
import throttle from 'lodash.throttle';
|
|
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
|
|
|
export default {
|
|
name: 'UserList',
|
|
components: {
|
|
SortMenu,
|
|
LoadingMessage,
|
|
ScrollLoad,
|
|
AvatarIcon,
|
|
ModalWindow
|
|
},
|
|
data () {
|
|
return {
|
|
search: '',
|
|
users: [],
|
|
showBadgeModal: false,
|
|
|
|
loading: true,
|
|
offset: 0,
|
|
limit: 15,
|
|
|
|
roleOptions: [
|
|
{ name: 'Admin', value: 'admin' },
|
|
{ name: 'Booster', value: 'booster' },
|
|
{ name: 'System', value: 'system' },
|
|
{ name: 'Bot', value: 'bot' }
|
|
],
|
|
roleSelected: ['admin', 'booster', 'system', 'bot'],
|
|
|
|
tableSort: {
|
|
column: 'username',
|
|
sort: 'desc'
|
|
},
|
|
user: {
|
|
username: '',
|
|
booster: '',
|
|
bot: '',
|
|
system: '',
|
|
admin: ''
|
|
},
|
|
}
|
|
},
|
|
methods: {
|
|
recaptcha() {
|
|
this.$recaptcha('login').then((token) => {
|
|
console.log(token) // Will print the token
|
|
})},
|
|
toggleBadgeModal (user) {
|
|
this.user.username = user.username
|
|
this.user.booster = user.booster
|
|
this.user.bot = user.bot
|
|
this.user.system = user.system
|
|
this.user.admin = user.admin
|
|
this.showBadgeModal = true
|
|
},
|
|
modifyUser (user) {
|
|
this.axios
|
|
.put('/api/v1/admin/user/modify', {
|
|
username: user.username,
|
|
bot: user.bot,
|
|
system: user.system,
|
|
booster: user.booster
|
|
})
|
|
.then(() => {
|
|
this.showBadgeModal = false
|
|
this.resetFetchData()
|
|
})
|
|
.catch(e => {
|
|
AjaxErrorHandler(this.$store)(e, error => {
|
|
this.description.error = error.message
|
|
})
|
|
})
|
|
},
|
|
fetchData () {
|
|
if(this.offset === null) return;
|
|
|
|
let url = `/api/v1/user?
|
|
sort=${this.tableSort.column}
|
|
&order=${this.tableSort.sort}
|
|
&offset=${this.offset}
|
|
`;
|
|
if(this.roleSelected.length === 1) {
|
|
url += '&role=' + this.roleSelected[0];
|
|
}
|
|
if(this.search.length) {
|
|
url += '&search=' + encodeURIComponent(this.search.trim());
|
|
}
|
|
|
|
this.loading = true;
|
|
this.axios
|
|
.get(url)
|
|
.then(res => {
|
|
this.users.push(...res.data);
|
|
this.loading = /*loading =*/ false;
|
|
|
|
//If returned data is less than the limit
|
|
//then there must be no more pages to paginate
|
|
if(res.data.length < this.limit) {
|
|
this.offset = null;
|
|
} else {
|
|
this.offset+= this.limit;
|
|
}
|
|
})
|
|
.catch(e => {
|
|
AjaxErrorHandler(this.$store)(e);
|
|
this.loading = /*loading =*/ false;
|
|
});
|
|
},
|
|
resetFetchData () {
|
|
this.offset = 0;
|
|
this.users = [];
|
|
|
|
this.fetchData();
|
|
}
|
|
},
|
|
mounted () {
|
|
this.fetchData();
|
|
},
|
|
watch: {
|
|
tableSort: 'resetFetchData',
|
|
roleSelected: 'resetFetchData',
|
|
search: throttle(function () {
|
|
this.resetFetchData();
|
|
}, 200)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang='scss' scoped>
|
|
@import '../../assets/scss/variables.scss';
|
|
|
|
.admin_users {
|
|
padding: 1rem 2rem;
|
|
|
|
@at-root #{&}__header {
|
|
margin: 0.5rem 0 1rem 0;
|
|
}
|
|
|
|
@at-root #{&}__filters {
|
|
margin-bottom: 0.5rem;
|
|
|
|
.select_filter {
|
|
margin-right: 0.5rem;
|
|
}
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
|
|
th {
|
|
border-bottom: 0.125rem solid $color__gray--darker;
|
|
padding: 0.5rem 0.75rem;
|
|
text-align: left;
|
|
}
|
|
|
|
tr {
|
|
cursor: default;
|
|
|
|
&:first-child {
|
|
background-color: #fff;
|
|
}
|
|
&:nth-child(odd) {
|
|
background-color: lighten($color__gray--primary, 20%);
|
|
}
|
|
&:nth-child(even) {
|
|
background-color: $color__gray--primary;
|
|
}
|
|
}
|
|
|
|
td {
|
|
padding: 0.75rem;
|
|
}
|
|
}
|
|
@at-root #{&}__user_column {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
a {
|
|
margin: 0 0.25rem;
|
|
}
|
|
}
|
|
|
|
.overlay_message {
|
|
padding-top: 2rem;
|
|
padding-bottom: 1rem;
|
|
}
|
|
}
|
|
</style>
|