+
+
+ {{ $t('common.TOTAL').toLowerCase() }}:
+
+
+ {{ pagination.total }}
+ {{ $t('workouts.WORKOUT', pagination.total) }}
+
+
@@ -117,9 +133,11 @@
import { LocationQuery, useRoute, useRouter } from 'vue-router'
import FilterSelects from '@/components/Common/FilterSelects.vue'
+ import Pagination from '@/components/Common/Pagination.vue'
import StaticMap from '@/components/Common/StaticMap.vue'
import NoWorkouts from '@/components/Workouts/NoWorkouts.vue'
import { WORKOUTS_STORE } from '@/store/constants'
+ import { IPagination } from '@/types/api'
import { ITranslatedSport } from '@/types/sports'
import { IUserProfile } from '@/types/user'
import { IWorkout, TWorkoutsPayload } from '@/types/workouts'
@@ -133,6 +151,7 @@
components: {
FilterSelects,
NoWorkouts,
+ Pagination,
StaticMap,
},
props: {
@@ -158,6 +177,9 @@
const workouts: ComputedRef
= computed(
() => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS]
)
+ const pagination: ComputedRef = computed(
+ () => store.getters[WORKOUTS_STORE.GETTERS.WORKOUTS_PAGINATION]
+ )
let query: TWorkoutsPayload = getWorkoutsQuery(route.query)
onBeforeMount(() => {
@@ -205,6 +227,7 @@
return {
query,
orderByList,
+ pagination,
sortList,
workouts,
capitalize,
@@ -225,11 +248,36 @@
width: 100%;
.box {
+ padding: $default-padding $default-padding * 2;
@media screen and (max-width: $small-limit) {
&.empty-table {
display: none;
}
}
+
+ .total {
+ display: flex;
+ gap: $default-padding * 0.5;
+ .total-label {
+ font-weight: bold;
+ }
+ }
+
+ .top-pagination {
+ display: none;
+
+ @media screen and (max-width: $small-limit) {
+ display: flex;
+ }
+ }
+ ::v-deep(.pagination-center) {
+ @media screen and (max-width: $small-limit) {
+ ul {
+ margin-top: 0;
+ }
+ }
+ }
+
.workouts-table {
.sport-col {
padding-right: 0;
diff --git a/fittrackee_client/src/locales/en/common.json b/fittrackee_client/src/locales/en/common.json
index ac721713..275108ac 100644
--- a/fittrackee_client/src/locales/en/common.json
+++ b/fittrackee_client/src/locales/en/common.json
@@ -15,5 +15,6 @@
"PER_PAGE": {
"LABEL": "par page"
}
- }
+ },
+ "TOTAL": "Total"
}
\ No newline at end of file
diff --git a/fittrackee_client/src/locales/en/statistics.json b/fittrackee_client/src/locales/en/statistics.json
index 8a9cc83c..ecd2cbfe 100644
--- a/fittrackee_client/src/locales/en/statistics.json
+++ b/fittrackee_client/src/locales/en/statistics.json
@@ -1,6 +1,5 @@
{
"STATISTICS": "Statistics",
- "TOTAL": "Total",
"TIME_FRAMES": {
"week": "week",
"month": "month",
diff --git a/fittrackee_client/src/locales/fr/common.json b/fittrackee_client/src/locales/fr/common.json
index 15c80b7f..1e2e9483 100644
--- a/fittrackee_client/src/locales/fr/common.json
+++ b/fittrackee_client/src/locales/fr/common.json
@@ -15,5 +15,6 @@
"PER_PAGE": {
"LABEL": "par page"
}
- }
+ },
+ "TOTAL ": "Total"
}
\ No newline at end of file
diff --git a/fittrackee_client/src/locales/fr/statistics.json b/fittrackee_client/src/locales/fr/statistics.json
index b8230cfd..12097bed 100644
--- a/fittrackee_client/src/locales/fr/statistics.json
+++ b/fittrackee_client/src/locales/fr/statistics.json
@@ -1,6 +1,5 @@
{
"STATISTICS": "Statistiques",
- "TOTAL": "Total",
"TIME_FRAMES": {
"week": "semaine",
"month": "mois",
diff --git a/fittrackee_client/src/store/modules/workouts/actions.ts b/fittrackee_client/src/store/modules/workouts/actions.ts
index 49374480..6a505f47 100644
--- a/fittrackee_client/src/store/modules/workouts/actions.ts
+++ b/fittrackee_client/src/store/modules/workouts/actions.ts
@@ -30,6 +30,12 @@ const getWorkouts = (
.then((res) => {
if (res.data.status === 'success') {
context.commit(WORKOUTS_STORE.MUTATIONS[target], res.data.data.workouts)
+ if (target === WorkoutsMutations['SET_USER_WORKOUTS']) {
+ context.commit(
+ WORKOUTS_STORE.MUTATIONS.SET_WORKOUTS_PAGINATION,
+ res.data.pagination
+ )
+ }
} else {
handleError(context, null)
}
diff --git a/fittrackee_client/src/store/modules/workouts/enums.ts b/fittrackee_client/src/store/modules/workouts/enums.ts
index 2852cadb..d6c0345a 100644
--- a/fittrackee_client/src/store/modules/workouts/enums.ts
+++ b/fittrackee_client/src/store/modules/workouts/enums.ts
@@ -15,6 +15,7 @@ export enum WorkoutsGetters {
TIMELINE_WORKOUTS = 'TIMELINE_WORKOUTS',
USER_WORKOUTS = 'USER_WORKOUTS',
WORKOUT_DATA = 'WORKOUT_DATA',
+ WORKOUTS_PAGINATION = 'WORKOUTS_PAGINATION',
}
export enum WorkoutsMutations {
@@ -29,4 +30,5 @@ export enum WorkoutsMutations {
SET_WORKOUT_GPX = 'SET_WORKOUT_GPX',
SET_WORKOUT_CHART_DATA = 'SET_WORKOUT_CHART_DATA',
SET_WORKOUT_LOADING = 'SET_WORKOUT_LOADING',
+ SET_WORKOUTS_PAGINATION = 'SET_WORKOUTS_PAGINATION',
}
diff --git a/fittrackee_client/src/store/modules/workouts/getters.ts b/fittrackee_client/src/store/modules/workouts/getters.ts
index f8024517..ac4f6cd6 100644
--- a/fittrackee_client/src/store/modules/workouts/getters.ts
+++ b/fittrackee_client/src/store/modules/workouts/getters.ts
@@ -21,4 +21,7 @@ export const getters: GetterTree &
[WORKOUTS_STORE.GETTERS.WORKOUT_DATA]: (state: IWorkoutsState) => {
return state.workoutData
},
+ [WORKOUTS_STORE.GETTERS.WORKOUTS_PAGINATION]: (state: IWorkoutsState) => {
+ return state.pagination
+ },
}
diff --git a/fittrackee_client/src/store/modules/workouts/mutations.ts b/fittrackee_client/src/store/modules/workouts/mutations.ts
index 85575482..fe4fe51f 100644
--- a/fittrackee_client/src/store/modules/workouts/mutations.ts
+++ b/fittrackee_client/src/store/modules/workouts/mutations.ts
@@ -5,6 +5,7 @@ import {
IWorkoutsState,
TWorkoutsMutations,
} from '@/store/modules/workouts/types'
+import { IPagination } from '@/types/api'
import { IWorkout, IWorkoutApiChartData } from '@/types/workouts'
export const mutations: MutationTree & TWorkoutsMutations = {
@@ -32,6 +33,12 @@ export const mutations: MutationTree & TWorkoutsMutations = {
) {
state.user_workouts = workouts
},
+ [WORKOUTS_STORE.MUTATIONS.SET_WORKOUTS_PAGINATION](
+ state: IWorkoutsState,
+ pagination: IPagination
+ ) {
+ state.pagination = pagination
+ },
[WORKOUTS_STORE.MUTATIONS.SET_WORKOUT](
state: IWorkoutsState,
workout: IWorkout
diff --git a/fittrackee_client/src/store/modules/workouts/state.ts b/fittrackee_client/src/store/modules/workouts/state.ts
index e2d65a0a..dd43dd97 100644
--- a/fittrackee_client/src/store/modules/workouts/state.ts
+++ b/fittrackee_client/src/store/modules/workouts/state.ts
@@ -1,9 +1,11 @@
import { IWorkoutsState } from '@/store/modules/workouts/types'
+import { IPagination } from '@/types/api'
import { IWorkout } from '@/types/workouts'
export const workoutsState: IWorkoutsState = {
calendar_workouts: [],
timeline_workouts: [],
+ pagination: {},
user_workouts: [],
workoutData: {
gpx: '',
diff --git a/fittrackee_client/src/store/modules/workouts/types.ts b/fittrackee_client/src/store/modules/workouts/types.ts
index a9ec290e..6b20cb5d 100644
--- a/fittrackee_client/src/store/modules/workouts/types.ts
+++ b/fittrackee_client/src/store/modules/workouts/types.ts
@@ -7,6 +7,7 @@ import {
import { WORKOUTS_STORE } from '@/store/constants'
import { IRootState } from '@/store/modules/root/types'
+import { IPagination } from '@/types/api'
import {
IWorkout,
IWorkoutApiChartData,
@@ -21,6 +22,7 @@ export interface IWorkoutsState {
calendar_workouts: IWorkout[]
timeline_workouts: IWorkout[]
workoutData: IWorkoutData
+ pagination: IPagination
}
export interface IWorkoutsActions {
@@ -67,6 +69,9 @@ export interface IWorkoutsGetters {
[WORKOUTS_STORE.GETTERS.TIMELINE_WORKOUTS](state: IWorkoutsState): IWorkout[]
[WORKOUTS_STORE.GETTERS.USER_WORKOUTS](state: IWorkoutsState): IWorkout[]
[WORKOUTS_STORE.GETTERS.WORKOUT_DATA](state: IWorkoutsState): IWorkoutData
+ [WORKOUTS_STORE.GETTERS.WORKOUTS_PAGINATION](
+ state: IWorkoutsState
+ ): IPagination
}
export type TWorkoutsMutations = {
@@ -96,6 +101,10 @@ export type TWorkoutsMutations = {
state: S,
loading: boolean
): void
+ [WORKOUTS_STORE.MUTATIONS.SET_WORKOUTS_PAGINATION](
+ state: S,
+ pagination: IPagination
+ ): void
[WORKOUTS_STORE.MUTATIONS.EMPTY_CALENDAR_WORKOUTS](state: S): void
[WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUTS](state: S): void
[WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT](state: S): void
diff --git a/fittrackee_client/src/utils/api.ts b/fittrackee_client/src/utils/api.ts
index 9b175193..9e24a377 100644
--- a/fittrackee_client/src/utils/api.ts
+++ b/fittrackee_client/src/utils/api.ts
@@ -62,3 +62,55 @@ export const workoutsPayloadKeys = [
'duration_to',
'sport_id',
]
+
+const getRange = (stop: number, start = 1): number[] => {
+ return Array.from({ length: stop - start + 1 }, (_, i) => start + i)
+}
+
+export const rangePagination = (
+ pages: number,
+ currentPage: number
+): (string | number)[] => {
+ if (pages < 0) {
+ return []
+ }
+
+ if (pages < 9) {
+ return getRange(pages)
+ }
+
+ let pagination: (string | number)[] = [1, 2]
+ if (currentPage < 4) {
+ pagination = pagination.concat([3, 4, 5])
+ } else if (currentPage < 6) {
+ pagination = pagination.concat(getRange(currentPage + 2, 3))
+ } else {
+ pagination = pagination.concat(['...'])
+ if (currentPage < pages - 2) {
+ pagination = pagination.concat(getRange(currentPage + 2, currentPage - 2))
+ }
+ }
+ if (currentPage + 2 <= pages - 2) {
+ pagination = pagination.concat(['...'])
+ pagination = pagination.concat(getRange(pages, pages - 1))
+ } else {
+ if (
+ pagination[pagination.length - 1] !== '...' &&
+ pagination[pagination.length - 1] >= pages - 2 &&
+ pagination[pagination.length - 1] < pages
+ ) {
+ pagination = pagination.concat(
+ getRange(pages, +pagination[pagination.length - 1] + 1)
+ )
+ } else {
+ pagination = pagination.concat(
+ getRange(
+ pages,
+ currentPage < pages - 3 ? currentPage + 3 : currentPage - 5
+ )
+ )
+ }
+ }
+
+ return pagination
+}
diff --git a/fittrackee_client/tests/unit/utils/api.spec.ts b/fittrackee_client/tests/unit/utils/api.spec.ts
index 6ccc909b..de3d7770 100644
--- a/fittrackee_client/tests/unit/utils/api.spec.ts
+++ b/fittrackee_client/tests/unit/utils/api.spec.ts
@@ -7,6 +7,7 @@ import {
getNumberQueryValue,
getStringQueryValue,
getQuery,
+ rangePagination,
} from '@/utils/api'
const orderByList = ['admin', 'created_at', 'username', 'workouts_count']
@@ -236,3 +237,140 @@ describe('getQuery w/ default values and input pagination payload', () => {
)
})
})
+
+describe('rangePagination', () => {
+ const testsParams = [
+ {
+ description: 'returns empty array if pages total equals 0',
+ input: {
+ currentPage: 1,
+ pages: 0,
+ },
+ expectedPagination: [],
+ },
+ {
+ description: 'returns empty array if pages total is a negative value',
+ input: {
+ currentPage: 1,
+ pages: -1,
+ },
+ expectedPagination: [],
+ },
+ {
+ description:
+ 'returns pagination if current page is 1 and pages total equals 1',
+ input: {
+ currentPage: 1,
+ pages: 1,
+ },
+ expectedPagination: [1],
+ },
+ {
+ description:
+ 'returns pagination if current page is 1 and pages total equals 4',
+ input: {
+ currentPage: 1,
+ pages: 4,
+ },
+ expectedPagination: [1, 2, 3, 4],
+ },
+ {
+ description:
+ 'returns pagination if current page is 4 and pages total equals 8',
+ input: {
+ currentPage: 4,
+ pages: 8,
+ },
+ expectedPagination: [1, 2, 3, 4, 5, 6, 7, 8],
+ },
+ {
+ description: 'returns pagination if current page is 1 and total pages 10',
+ input: {
+ currentPage: 1,
+ pages: 10,
+ },
+ expectedPagination: [1, 2, 3, 4, 5, '...', 9, 10],
+ },
+ {
+ description:
+ 'returns pagination if current page is 4 and pages total equals 10',
+ input: {
+ currentPage: 4,
+ pages: 10,
+ },
+ expectedPagination: [1, 2, 3, 4, 5, 6, '...', 9, 10],
+ },
+ {
+ description:
+ 'returns pagination if current page is 7 and pages total equals 10',
+ input: {
+ currentPage: 7,
+ pages: 10,
+ },
+ expectedPagination: [1, 2, '...', 5, 6, 7, 8, 9, 10],
+ },
+ {
+ description:
+ 'returns pagination if current page is 20 and pages total equals 30',
+ input: {
+ currentPage: 20,
+ pages: 30,
+ },
+ expectedPagination: [1, 2, '...', 18, 19, 20, 21, 22, '...', 29, 30],
+ },
+ {
+ description:
+ 'returns pagination if current page is 1 and total pages 100',
+ input: {
+ currentPage: 1,
+ pages: 100,
+ },
+ expectedPagination: [1, 2, 3, 4, 5, '...', 99, 100],
+ },
+ {
+ description:
+ 'returns pagination if current page is 5 and total pages 100',
+ input: {
+ currentPage: 5,
+ pages: 100,
+ },
+ expectedPagination: [1, 2, 3, 4, 5, 6, 7, '...', 99, 100],
+ },
+ {
+ description:
+ 'returns pagination if current page is 50 and total pages 100',
+ input: {
+ currentPage: 50,
+ pages: 100,
+ },
+ expectedPagination: [1, 2, '...', 48, 49, 50, 51, 52, '...', 99, 100],
+ },
+ {
+ description:
+ 'returns pagination if current page is 97 and total pages 100',
+ input: {
+ currentPage: 97,
+ pages: 100,
+ },
+ expectedPagination: [1, 2, '...', 95, 96, 97, 98, 99, 100],
+ },
+ {
+ description:
+ 'returns pagination if current page is 100 and total pages 100',
+ input: {
+ currentPage: 100,
+ pages: 100,
+ },
+ expectedPagination: [1, 2, '...', 95, 96, 97, 98, 99, 100],
+ },
+ ]
+
+ testsParams.map((testParams) => {
+ it(testParams.description, () => {
+ assert.deepEqual(
+ rangePagination(testParams.input.pages, testParams.input.currentPage),
+ testParams.expectedPagination
+ )
+ })
+ })
+})