Client - filter on username in administration

This commit is contained in:
Sam 2022-03-13 10:52:10 +01:00
parent 08d412bddf
commit f241504b8f
7 changed files with 137 additions and 5 deletions

View File

@ -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;

View File

@ -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

View 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>

View File

@ -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?",

View File

@ -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é ?",

View File

@ -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 {

View File

@ -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
} }