235 lines
6.9 KiB
Vue
235 lines
6.9 KiB
Vue
<template>
|
|
<div id="admin-users" class="admin-card">
|
|
<Card>
|
|
<template #title>{{ capitalize($t('admin.USER', 0)) }}</template>
|
|
<template #content>
|
|
<button class="top-button" @click.prevent="$router.push('/admin')">
|
|
{{ $t('admin.BACK_TO_ADMIN') }}
|
|
</button>
|
|
<FilterSelects
|
|
:sort="sortList"
|
|
:order_by="orderByList"
|
|
:query="query"
|
|
message="admin.USERS.SELECTS.ORDER_BY"
|
|
@updateSelect="reloadUsers"
|
|
/>
|
|
<div class="responsive-table">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th class="left-text">{{ $t('user.USERNAME') }}</th>
|
|
<th class="left-text">{{ $t('user.EMAIL') }}</th>
|
|
<th class="left-text">
|
|
{{ $t('user.PROFILE.REGISTRATION_DATE') }}
|
|
</th>
|
|
<th>
|
|
{{ capitalize($t('workouts.WORKOUT', 0)) }}
|
|
</th>
|
|
<th>{{ $t('user.ADMIN') }}</th>
|
|
<th>{{ $t('admin.ACTION') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="user in users" :key="user.username">
|
|
<td>
|
|
<span class="cell-heading">
|
|
{{ $t('user.PROFILE.PICTURE') }}
|
|
</span>
|
|
<UserPicture :user="user" />
|
|
</td>
|
|
<td>
|
|
<span class="cell-heading">
|
|
{{ $t('user.USERNAME') }}
|
|
</span>
|
|
<router-link :to="`/users/${user.username}`">
|
|
{{ user.username }}
|
|
</router-link>
|
|
</td>
|
|
<td>
|
|
<span class="cell-heading">
|
|
{{ $t('user.EMAIL') }}
|
|
</span>
|
|
{{ user.email }}
|
|
</td>
|
|
<td>
|
|
<span class="cell-heading">
|
|
{{ $t('user.PROFILE.REGISTRATION_DATE') }}
|
|
</span>
|
|
{{
|
|
format(
|
|
getDateWithTZ(user.created_at, authUser.timezone),
|
|
'dd/MM/yyyy HH:mm'
|
|
)
|
|
}}
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="cell-heading">
|
|
{{ capitalize($t('workouts.WORKOUT', 0)) }}
|
|
</span>
|
|
{{ user.nb_workouts }}
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="cell-heading">
|
|
{{ $t('user.ADMIN') }}
|
|
</span>
|
|
<i
|
|
:class="`fa fa${user.admin ? '-check' : ''}-square-o`"
|
|
aria-hidden="true"
|
|
/>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="cell-heading">
|
|
{{ $t('admin.ACTION') }}
|
|
</span>
|
|
<button
|
|
:class="{ danger: user.admin }"
|
|
:disabled="user.username === authUser.username"
|
|
@click="updateUser(user.username, !user.admin)"
|
|
>
|
|
{{
|
|
$t(
|
|
`admin.USERS.TABLE.${
|
|
user.admin ? 'REMOVE' : 'ADD'
|
|
}_ADMIN_RIGHTS`
|
|
)
|
|
}}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<Pagination
|
|
v-if="pagination.page"
|
|
path="/admin/users"
|
|
:pagination="pagination"
|
|
:query="query"
|
|
/>
|
|
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
|
|
<button @click.prevent="$router.push('/admin')">
|
|
{{ $t('admin.BACK_TO_ADMIN') }}
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</Card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { format } from 'date-fns'
|
|
import {
|
|
ComputedRef,
|
|
computed,
|
|
reactive,
|
|
watch,
|
|
capitalize,
|
|
onBeforeMount,
|
|
onUnmounted,
|
|
} from 'vue'
|
|
import { LocationQuery, useRoute, useRouter } from 'vue-router'
|
|
|
|
import FilterSelects from '@/components/Common/FilterSelects.vue'
|
|
import Pagination from '@/components/Common/Pagination.vue'
|
|
import UserPicture from '@/components/User/UserPicture.vue'
|
|
import { AUTH_USER_STORE, ROOT_STORE, USERS_STORE } from '@/store/constants'
|
|
import { IPagination, TPaginationPayload } from '@/types/api'
|
|
import { IAuthUserProfile, IUserProfile } from '@/types/user'
|
|
import { useStore } from '@/use/useStore'
|
|
import { getQuery, sortList } from '@/utils/api'
|
|
import { getDateWithTZ } from '@/utils/dates'
|
|
|
|
const store = useStore()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const orderByList: string[] = [
|
|
'admin',
|
|
'created_at',
|
|
'username',
|
|
'workouts_count',
|
|
]
|
|
const defaultOrderBy = 'created_at'
|
|
let query: TPaginationPayload = reactive(
|
|
getQuery(route.query, orderByList, defaultOrderBy)
|
|
)
|
|
const authUser: ComputedRef<IAuthUserProfile> = computed(
|
|
() => store.getters[AUTH_USER_STORE.GETTERS.AUTH_USER_PROFILE]
|
|
)
|
|
const users: ComputedRef<IUserProfile[]> = computed(
|
|
() => store.getters[USERS_STORE.GETTERS.USERS]
|
|
)
|
|
const pagination: ComputedRef<IPagination> = computed(
|
|
() => store.getters[USERS_STORE.GETTERS.USERS_PAGINATION]
|
|
)
|
|
const errorMessages: ComputedRef<string | string[] | null> = computed(
|
|
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
|
|
)
|
|
|
|
onBeforeMount(() => loadUsers(query))
|
|
|
|
function loadUsers(queryParams: TPaginationPayload) {
|
|
store.dispatch(USERS_STORE.ACTIONS.GET_USERS, queryParams)
|
|
}
|
|
function updateUser(username: string, admin: boolean) {
|
|
store.dispatch(USERS_STORE.ACTIONS.UPDATE_USER, {
|
|
username,
|
|
admin,
|
|
})
|
|
}
|
|
function reloadUsers(queryParam: string, queryValue: string) {
|
|
query[queryParam] = queryValue
|
|
if (queryParam === 'per_page') {
|
|
query.page = 1
|
|
}
|
|
router.push({ path: '/admin/users', query })
|
|
}
|
|
|
|
onUnmounted(() => {
|
|
store.dispatch(USERS_STORE.ACTIONS.EMPTY_USERS)
|
|
})
|
|
|
|
watch(
|
|
() => route.query,
|
|
(newQuery: LocationQuery) => {
|
|
query = getQuery(newQuery, orderByList, defaultOrderBy, { query })
|
|
loadUsers(query)
|
|
}
|
|
)
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '~@/scss/vars.scss';
|
|
#admin-users {
|
|
.top-button {
|
|
display: none;
|
|
}
|
|
table {
|
|
td {
|
|
font-size: 1.1em;
|
|
}
|
|
}
|
|
.left-text {
|
|
text-align: left;
|
|
}
|
|
::v-deep(.user-picture) {
|
|
img {
|
|
height: 30px;
|
|
width: 30px;
|
|
}
|
|
.no-picture {
|
|
font-size: 2em;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: $small-limit) {
|
|
.top-button {
|
|
display: block;
|
|
margin-bottom: $default-margin * 2;
|
|
}
|
|
.pagination-center {
|
|
margin-top: -3 * $default-margin;
|
|
}
|
|
}
|
|
}
|
|
</style>
|