Client - get workouts ordered by distance, duration or average speed

This commit is contained in:
Sam 2021-11-02 15:12:57 +01:00
parent 05b22e6f72
commit e8350abf55
7 changed files with 90 additions and 26 deletions

View File

@ -9,7 +9,7 @@
@change="onSelectUpdate" @change="onSelectUpdate"
> >
<option v-for="order in order_by" :value="order" :key="order"> <option v-for="order in order_by" :value="order" :key="order">
{{ $t(`${message}.${order}`) }} {{ $t(`${message}.${order.toUpperCase()}`) }}
</option> </option>
</select> </select>
</label> </label>

View File

@ -157,9 +157,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, ComputedRef, defineComponent, PropType } from 'vue' import { computed, ComputedRef, defineComponent, PropType, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { LocationQuery, useRoute, useRouter } from 'vue-router'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
import { IUserProfile } from '@/types/user' import { IUserProfile } from '@/types/user'
@ -180,12 +180,13 @@
emits: ['filter'], emits: ['filter'],
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n() const { t } = useI18n()
const route = useRoute()
const router = useRouter() const router = useRouter()
const translatedSports: ComputedRef<ISport[]> = computed(() => const translatedSports: ComputedRef<ISport[]> = computed(() =>
translateSports(props.sports, t) translateSports(props.sports, t)
) )
const params: Record<string, string> = {} let params: LocationQuery = Object.assign({}, route.query)
function handleFilterChange(event: Event & { target: HTMLInputElement }) { function handleFilterChange(event: Event & { target: HTMLInputElement }) {
if (event.target.value === '') { if (event.target.value === '') {
@ -199,6 +200,13 @@
router.push({ path: '/workouts', query: params }) router.push({ path: '/workouts', query: params })
} }
watch(
() => route.query,
(newQuery) => {
params = Object.assign({}, newQuery)
}
)
return { translatedSports, onFilter, handleFilterChange } return { translatedSports, onFilter, handleFilterChange }
}, },
}) })

View File

@ -1,6 +1,13 @@
<template> <template>
<div class="workouts-list"> <div class="workouts-list">
<div class="box" :class="{ 'empty-table': workouts.length === 0 }"> <div class="box" :class="{ 'empty-table': workouts.length === 0 }">
<FilterSelects
:sort="sortList"
:order_by="orderByList"
:query="query"
message="workouts"
@updateSelect="reloadWorkouts"
/>
<div class="workouts-table responsive-table"> <div class="workouts-table responsive-table">
<table> <table>
<thead> <thead>
@ -103,26 +110,28 @@
PropType, PropType,
computed, computed,
defineComponent, defineComponent,
ref,
watch, watch,
capitalize, capitalize,
onBeforeMount, onBeforeMount,
} from 'vue' } from 'vue'
import { LocationQuery, useRoute } from 'vue-router' import { LocationQuery, useRoute, useRouter } from 'vue-router'
import FilterSelects from '@/components/Common/FilterSelects.vue'
import StaticMap from '@/components/Common/StaticMap.vue' import StaticMap from '@/components/Common/StaticMap.vue'
import NoWorkouts from '@/components/Workouts/NoWorkouts.vue' import NoWorkouts from '@/components/Workouts/NoWorkouts.vue'
import { WORKOUTS_STORE } from '@/store/constants' import { WORKOUTS_STORE } from '@/store/constants'
import { ITranslatedSport } from '@/types/sports' import { ITranslatedSport } from '@/types/sports'
import { IUserProfile } from '@/types/user' import { IUserProfile } from '@/types/user'
import { IWorkout } from '@/types/workouts' import { IWorkout, TWorkoutsPayload } from '@/types/workouts'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
import { getQuery, sortList, workoutsPayloadKeys } from '@/utils/api'
import { getDateWithTZ } from '@/utils/dates' import { getDateWithTZ } from '@/utils/dates'
import { defaultOrder } from '@/utils/workouts' import { defaultOrder } from '@/utils/workouts'
export default defineComponent({ export default defineComponent({
name: 'WorkoutsList', name: 'WorkoutsList',
components: { components: {
FilterSelects,
NoWorkouts, NoWorkouts,
StaticMap, StaticMap,
}, },
@ -138,39 +147,70 @@
setup() { setup() {
const store = useStore() const store = useStore()
const route = useRoute() const route = useRoute()
const router = useRouter()
const orderByList: string[] = [
'ave_speed',
'distance',
'duration',
'workout_date',
]
const workouts: ComputedRef<IWorkout[]> = computed( const workouts: ComputedRef<IWorkout[]> = computed(
() => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS] () => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS]
) )
const per_page = 10 let query: TWorkoutsPayload = getWorkoutsQuery(route.query)
const page = ref(1)
onBeforeMount(() => { onBeforeMount(() => {
loadWorkouts(route.query) loadWorkouts(query)
}) })
function loadWorkouts(newQuery: LocationQuery) { function loadWorkouts(payload: TWorkoutsPayload) {
page.value = 1 store.dispatch(WORKOUTS_STORE.ACTIONS.GET_USER_WORKOUTS, payload)
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_USER_WORKOUTS, { }
page: page.value, function reloadWorkouts(queryParam: string, queryValue: string) {
per_page, const newQuery: LocationQuery = Object.assign({}, route.query)
...defaultOrder, newQuery[queryParam] = queryValue
...newQuery, if (queryParam === 'per_page') {
newQuery['page'] = '1'
}
query = getWorkoutsQuery(newQuery)
router.push({ path: '/workouts', query })
}
function getWorkoutsQuery(newQuery: LocationQuery): TWorkoutsPayload {
query = getQuery(newQuery, orderByList, defaultOrder.order_by, {
defaultSort: defaultOrder.order,
query,
}) })
Object.keys(newQuery)
.filter((k) => workoutsPayloadKeys.includes(k))
.map((k) => {
if (typeof newQuery[k] === 'string') {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
query[k] = newQuery[k]
}
})
return query
} }
watch( watch(
() => route.query, () => route.query,
async (newQuery) => { async (newQuery) => {
loadWorkouts(newQuery) query = getWorkoutsQuery(newQuery)
loadWorkouts(query)
} }
) )
return { return {
query,
orderByList,
sortList,
workouts, workouts,
capitalize, capitalize,
format, format,
getDateWithTZ, getDateWithTZ,
reloadWorkouts,
} }
}, },
}) })

View File

@ -36,10 +36,10 @@
}, },
"SELECTS": { "SELECTS": {
"ORDER_BY": { "ORDER_BY": {
"admin": "admin status", "ADMIN": "admin status",
"created_at": "registration date", "CREATED_AT": "registration date",
"username": "username", "USERNAME": "username",
"workouts_count": "workout count" "WORKOUTS_COUNT": "workout count"
} }
} }
} }

View File

@ -36,10 +36,10 @@
}, },
"SELECTS": { "SELECTS": {
"ORDER_BY": { "ORDER_BY": {
"admin": "status administrateur", "ADMIN": "status administrateur",
"created_at": "date d'inscription", "CREATED_AT": "date d'inscription",
"username": "nom d'utilisateur", "USERNAME": "nom d'utilisateur",
"workouts_count": "nombre de séances" "WORKOUTS_COUNT": "nombre de séances"
} }
} }
} }

View File

@ -1,3 +1,5 @@
import { TWorkoutsPayload } from '@/types/workouts'
export interface IPagination { export interface IPagination {
has_next: boolean has_next: boolean
has_prev: boolean has_prev: boolean

View File

@ -48,3 +48,17 @@ export const getQuery = (
return query return query
} }
export const workoutsPayloadKeys = [
'from',
'to',
'ave_speed_from',
'ave_speed_to',
'max_speed_from',
'max_speed_to',
'distance_from',
'distance_to',
'duration_from',
'duration_to',
'sport_id',
]