Client - filter on username in administration
This commit is contained in:
parent
08d412bddf
commit
f241504b8f
@ -6,6 +6,7 @@
|
|||||||
<button class="top-button" @click.prevent="$router.push('/admin')">
|
<button class="top-button" @click.prevent="$router.push('/admin')">
|
||||||
{{ $t('admin.BACK_TO_ADMIN') }}
|
{{ $t('admin.BACK_TO_ADMIN') }}
|
||||||
</button>
|
</button>
|
||||||
|
<UsersNameFilter @filterOnUsername="searchUsers" />
|
||||||
<FilterSelects
|
<FilterSelects
|
||||||
:sort="sortList"
|
:sort="sortList"
|
||||||
:order_by="orderByList"
|
:order_by="orderByList"
|
||||||
@ -13,7 +14,10 @@
|
|||||||
message="admin.USERS.SELECTS.ORDER_BY"
|
message="admin.USERS.SELECTS.ORDER_BY"
|
||||||
@updateSelect="reloadUsers"
|
@updateSelect="reloadUsers"
|
||||||
/>
|
/>
|
||||||
<div class="responsive-table">
|
<div class="no-users" v-if="users.length === 0">
|
||||||
|
{{ $t('user.NO_USERS_FOUND') }}
|
||||||
|
</div>
|
||||||
|
<div class="responsive-table" v-else>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -119,6 +123,7 @@
|
|||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import {
|
import {
|
||||||
ComputedRef,
|
ComputedRef,
|
||||||
|
Ref,
|
||||||
computed,
|
computed,
|
||||||
reactive,
|
reactive,
|
||||||
watch,
|
watch,
|
||||||
@ -131,6 +136,7 @@
|
|||||||
import FilterSelects from '@/components/Common/FilterSelects.vue'
|
import FilterSelects from '@/components/Common/FilterSelects.vue'
|
||||||
import Pagination from '@/components/Common/Pagination.vue'
|
import Pagination from '@/components/Common/Pagination.vue'
|
||||||
import UserPicture from '@/components/User/UserPicture.vue'
|
import UserPicture from '@/components/User/UserPicture.vue'
|
||||||
|
import UsersNameFilter from '@/components/Users/UsersNameFilter.vue'
|
||||||
import { AUTH_USER_STORE, ROOT_STORE, USERS_STORE } from '@/store/constants'
|
import { AUTH_USER_STORE, ROOT_STORE, USERS_STORE } from '@/store/constants'
|
||||||
import { IPagination, TPaginationPayload } from '@/types/api'
|
import { IPagination, TPaginationPayload } from '@/types/api'
|
||||||
import { IAuthUserProfile, IUserProfile } from '@/types/user'
|
import { IAuthUserProfile, IUserProfile } from '@/types/user'
|
||||||
@ -170,6 +176,10 @@
|
|||||||
function loadUsers(queryParams: TPaginationPayload) {
|
function loadUsers(queryParams: TPaginationPayload) {
|
||||||
store.dispatch(USERS_STORE.ACTIONS.GET_USERS, queryParams)
|
store.dispatch(USERS_STORE.ACTIONS.GET_USERS, queryParams)
|
||||||
}
|
}
|
||||||
|
function searchUsers(username: Ref<string>) {
|
||||||
|
reloadUsers('q', username.value)
|
||||||
|
}
|
||||||
|
|
||||||
function updateUser(username: string, admin: boolean) {
|
function updateUser(username: string, admin: boolean) {
|
||||||
store.dispatch(USERS_STORE.ACTIONS.UPDATE_USER, {
|
store.dispatch(USERS_STORE.ACTIONS.UPDATE_USER, {
|
||||||
username,
|
username,
|
||||||
@ -203,6 +213,14 @@
|
|||||||
.top-button {
|
.top-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-users {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: $default-padding * 2 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
td {
|
td {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
@ -45,20 +45,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { toRefs } from 'vue'
|
import { toRefs } from 'vue'
|
||||||
|
|
||||||
import { IPagination } from '@/types/api'
|
import { IPagination, TPaginationPayload } from '@/types/api'
|
||||||
import { TWorkoutsPayload } from '@/types/workouts'
|
import { TWorkoutsPayload } from '@/types/workouts'
|
||||||
import { rangePagination } from '@/utils/api'
|
import { rangePagination } from '@/utils/api'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pagination: IPagination
|
pagination: IPagination
|
||||||
path: string
|
path: string
|
||||||
query: TWorkoutsPayload
|
query: TWorkoutsPayload | TPaginationPayload
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const { pagination, path, query } = toRefs(props)
|
const { pagination, path, query } = toRefs(props)
|
||||||
|
|
||||||
function getQuery(page: number, cursor?: number): TWorkoutsPayload {
|
function getQuery(
|
||||||
|
page: number,
|
||||||
|
cursor?: number
|
||||||
|
): TWorkoutsPayload | TPaginationPayload {
|
||||||
const newQuery = Object.assign({}, query.value)
|
const newQuery = Object.assign({}, query.value)
|
||||||
newQuery.page = cursor ? page + cursor : page
|
newQuery.page = cursor ? page + cursor : page
|
||||||
return newQuery
|
return newQuery
|
||||||
|
101
fittrackee_client/src/components/Users/UsersNameFilter.vue
Normal file
101
fittrackee_client/src/components/Users/UsersNameFilter.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<div class="users-filters">
|
||||||
|
<div class="search-username">
|
||||||
|
<input
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
v-model.trim="username"
|
||||||
|
@keyup.enter="searchUsers"
|
||||||
|
:placeholder="$t('user.FILTER_ON_USERNAME')"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-if="username !== ''"
|
||||||
|
class="fa fa-times"
|
||||||
|
aria-hidden="true"
|
||||||
|
@click="resetFilter"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="fa fa-search"
|
||||||
|
:class="{ 'fa-disabled': username === '' }"
|
||||||
|
aria-hidden="true"
|
||||||
|
@click="searchUsers"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const username = ref(route.query.q ? route.query.q : '')
|
||||||
|
|
||||||
|
const emit = defineEmits(['filterOnUsername'])
|
||||||
|
function searchUsers() {
|
||||||
|
if (username.value !== '') {
|
||||||
|
emit('filterOnUsername', username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function resetFilter() {
|
||||||
|
username.value = ''
|
||||||
|
emit('filterOnUsername', username.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~@/scss/vars.scss';
|
||||||
|
|
||||||
|
.users-filters {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: $default-padding 0;
|
||||||
|
gap: $default-padding;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
.fa-disabled {
|
||||||
|
color: var(--disabled-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-username {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $default-padding;
|
||||||
|
|
||||||
|
border: solid 1px var(--card-border-color);
|
||||||
|
border-radius: $border-radius;
|
||||||
|
color: var(--info-color);
|
||||||
|
width: 45%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
height: 12px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.fa-times {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $small-limit) {
|
||||||
|
.users-filters {
|
||||||
|
.search-username {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: $x-small-limit) {
|
||||||
|
.users-filters {
|
||||||
|
.search-username {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -6,6 +6,7 @@
|
|||||||
"EMAIL": "Email",
|
"EMAIL": "Email",
|
||||||
"EMAIL_INFO": "Enter a valid email address.",
|
"EMAIL_INFO": "Enter a valid email address.",
|
||||||
"ENTER_PASSWORD": "Enter a password",
|
"ENTER_PASSWORD": "Enter a password",
|
||||||
|
"FILTER_ON_USERNAME": "Filter on username",
|
||||||
"HIDE_PASSWORD": "hide password",
|
"HIDE_PASSWORD": "hide password",
|
||||||
"INVALID_TOKEN": "Invalid token, please request a new password reset.",
|
"INVALID_TOKEN": "Invalid token, please request a new password reset.",
|
||||||
"LANGUAGE": "Language",
|
"LANGUAGE": "Language",
|
||||||
@ -13,6 +14,7 @@
|
|||||||
"LOGIN": "Login",
|
"LOGIN": "Login",
|
||||||
"LOGOUT": "Logout",
|
"LOGOUT": "Logout",
|
||||||
"NEW_PASSWORD": "New password",
|
"NEW_PASSWORD": "New password",
|
||||||
|
"NO_USERS_FOUND": "No users found.",
|
||||||
"PASSWORD": "Password",
|
"PASSWORD": "Password",
|
||||||
"PASSWORD_INFO": "At least 8 characters required.",
|
"PASSWORD_INFO": "At least 8 characters required.",
|
||||||
"PASSWORD_FORGOTTEN": "Forgot password?",
|
"PASSWORD_FORGOTTEN": "Forgot password?",
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"EMAIL": "Email",
|
"EMAIL": "Email",
|
||||||
"EMAIL_INFO": "Saisir une adresse email valide.",
|
"EMAIL_INFO": "Saisir une adresse email valide.",
|
||||||
"ENTER_PASSWORD": "Saisir un mot de passe",
|
"ENTER_PASSWORD": "Saisir un mot de passe",
|
||||||
|
"FILTER_ON_USERNAME": "Filtrer sur le nom d'utilisateur",
|
||||||
"HIDE_PASSWORD": "masquer le mot de passe",
|
"HIDE_PASSWORD": "masquer le mot de passe",
|
||||||
"INVALID_TOKEN": "Jeton invalide, veuillez demander une nouvelle réinitialisation de mot de passe.",
|
"INVALID_TOKEN": "Jeton invalide, veuillez demander une nouvelle réinitialisation de mot de passe.",
|
||||||
"LANGUAGE": "Langue",
|
"LANGUAGE": "Langue",
|
||||||
@ -13,6 +14,7 @@
|
|||||||
"LOGIN": "Se connecter",
|
"LOGIN": "Se connecter",
|
||||||
"LOGOUT": "Se déconnecter",
|
"LOGOUT": "Se déconnecter",
|
||||||
"NEW_PASSWORD": "Nouveau mot de passe",
|
"NEW_PASSWORD": "Nouveau mot de passe",
|
||||||
|
"NO_USERS_FOUND": "Aucun utilisateur trouvé.",
|
||||||
"PASSWORD": "Mot de passe",
|
"PASSWORD": "Mot de passe",
|
||||||
"PASSWORD_INFO": "8 caractères minimum.",
|
"PASSWORD_INFO": "8 caractères minimum.",
|
||||||
"PASSWORD_FORGOTTEN": "Mot de passe oublié ?",
|
"PASSWORD_FORGOTTEN": "Mot de passe oublié ?",
|
||||||
|
@ -7,11 +7,12 @@ export interface IPagination {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type TPaginationPayload = {
|
export type TPaginationPayload = {
|
||||||
[key: string]: string | number
|
[key: string]: string | number | undefined
|
||||||
order: string
|
order: string
|
||||||
order_by: string
|
order_by: string
|
||||||
per_page: number
|
per_page: number
|
||||||
page: number
|
page: number
|
||||||
|
q?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQueryOptions {
|
export interface IQueryOptions {
|
||||||
|
@ -45,6 +45,11 @@ export const getQuery = (
|
|||||||
orderByList,
|
orderByList,
|
||||||
defaultOrderBy
|
defaultOrderBy
|
||||||
)
|
)
|
||||||
|
if (typeof locationQuery.q === 'string') {
|
||||||
|
query.q = locationQuery.q
|
||||||
|
} else {
|
||||||
|
delete query.q
|
||||||
|
}
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user