diff --git a/fittrackee_client/src/components/Administration/AdminUsers.vue b/fittrackee_client/src/components/Administration/AdminUsers.vue index bf4a56d6..022f2f07 100644 --- a/fittrackee_client/src/components/Administration/AdminUsers.vue +++ b/fittrackee_client/src/components/Administration/AdminUsers.vue @@ -7,8 +7,8 @@ {{ $t('admin.BACK_TO_ADMIN') }} @@ -135,6 +135,7 @@ import { IPagination, TPaginationPayload } from '@/types/api' import { IUserProfile } from '@/types/user' import { useStore } from '@/use/useStore' + import { getQuery, sortList } from '@/utils/api' import { getDateWithTZ } from '@/utils/dates' export default defineComponent({ @@ -149,14 +150,16 @@ const route = useRoute() const router = useRouter() - const sort: string[] = ['asc', 'desc'] - const order_by: string[] = [ + const orderByList: string[] = [ 'admin', 'created_at', 'username', 'workouts_count', ] - let query: TPaginationPayload = reactive(getQuery(route.query)) + const defaultOrderBy = 'created_at' + let query: TPaginationPayload = reactive( + getQuery(route.query, orderByList, defaultOrderBy) + ) const authUser: ComputedRef = computed( () => store.getters[USER_STORE.GETTERS.AUTH_USER_PROFILE] @@ -174,32 +177,6 @@ function loadUsers(queryParams: TPaginationPayload) { store.dispatch(USERS_STORE.ACTIONS.GET_USERS, queryParams) } - function getPage(page: string | (string | null)[] | null): number { - return page && typeof page === 'string' && +page > 0 ? +page : 1 - } - function getPerPage(perPage: string | (string | null)[] | null): number { - return perPage && typeof perPage === 'string' && +perPage > 0 - ? +perPage - : 10 - } - function getOrder(order: string | (string | null)[] | null): string { - return order && typeof order === 'string' && sort.includes(order) - ? order - : 'asc' - } - function getOrderBy(order: string | (string | null)[] | null): string { - return order && typeof order === 'string' && order_by.includes(order) - ? order - : 'created_at' - } - function getQuery(query: LocationQuery): TPaginationPayload { - return { - page: getPage(query.page), - per_page: getPerPage(query.per_page), - order: getOrder(query.order), - order_by: getOrderBy(query.order_by), - } - } function updateUser(username: string, admin: boolean) { store.dispatch(USERS_STORE.ACTIONS.UPDATE_USER, { username, @@ -219,10 +196,7 @@ watch( () => route.query, (newQuery: LocationQuery) => { - query.page = getPage(newQuery.page) - query.per_page = getPerPage(newQuery.per_page) - query.order = getOrder(newQuery.order) - query.order_by = getOrderBy(newQuery.order_by) + query = getQuery(newQuery, orderByList, defaultOrderBy, { query }) loadUsers(query) } ) @@ -234,10 +208,10 @@ return { authUser, errorMessages, + orderByList, pagination, - order_by, query, - sort, + sortList, users, capitalize, format, diff --git a/fittrackee_client/src/types/api.ts b/fittrackee_client/src/types/api.ts index 9d78c5e6..853377a8 100644 --- a/fittrackee_client/src/types/api.ts +++ b/fittrackee_client/src/types/api.ts @@ -13,3 +13,8 @@ export type TPaginationPayload = { per_page: number page: number } + +export interface IQueryOptions { + defaultSort?: string + query?: TPaginationPayload +} diff --git a/fittrackee_client/src/utils/api.ts b/fittrackee_client/src/utils/api.ts new file mode 100644 index 00000000..b72a6ba3 --- /dev/null +++ b/fittrackee_client/src/utils/api.ts @@ -0,0 +1,50 @@ +import { LocationQuery } from 'vue-router' + +import { IQueryOptions, TPaginationPayload } from '@/types/api' + +export const sortList: string[] = ['asc', 'desc'] +export const defaultPage = 1 +export const defaultPerPage = 10 + +export const getNumberQueryValue = ( + queryValue: string | (string | null)[] | null, + defaultValue: number +): number => { + return queryValue && typeof queryValue === 'string' && +queryValue > 0 + ? +queryValue + : defaultValue +} + +export const getStringQueryValue = ( + queryValue: string | (string | null)[] | null, + availableValues: string[], + defaultValue: string +): string => { + return queryValue && + typeof queryValue === 'string' && + availableValues.includes(queryValue) + ? queryValue + : defaultValue +} + +export const getQuery = ( + locationQuery: LocationQuery, + orderByList: string[], + defaultOrderBy: string, + options?: IQueryOptions +): TPaginationPayload => { + const queryOptions = options || {} + const defaultSort = queryOptions.defaultSort || 'asc' + const query = queryOptions.query || {} + + query.page = getNumberQueryValue(locationQuery.page, defaultPage) + query.per_page = getNumberQueryValue(locationQuery.per_page, defaultPerPage) + query.order = getStringQueryValue(locationQuery.order, sortList, defaultSort) + query.order_by = getStringQueryValue( + locationQuery.order_by, + orderByList, + defaultOrderBy + ) + + return query +} diff --git a/fittrackee_client/tests/unit/utils/api.spec.ts b/fittrackee_client/tests/unit/utils/api.spec.ts new file mode 100644 index 00000000..6ccc909b --- /dev/null +++ b/fittrackee_client/tests/unit/utils/api.spec.ts @@ -0,0 +1,238 @@ +import { assert } from 'chai' + +import { + defaultPerPage, + defaultPage, + sortList, + getNumberQueryValue, + getStringQueryValue, + getQuery, +} from '@/utils/api' + +const orderByList = ['admin', 'created_at', 'username', 'workouts_count'] +const defaultSort = 'desc' +const defaultOrderBy = 'created_at' + +const generateLocationQuery = ( + query: Record +): Record => { + return query +} + +describe('getNumberQueryValue', () => { + const testsParams = [ + { + description: 'returns 2 if input value is 2', + inputValue: '2', + inputDefaultValue: 2, + expectedValue: 2, + }, + { + description: 'returns default value if input value is null', + inputValue: null, + inputDefaultValue: 1, + expectedValue: 1, + }, + { + description: 'returns default value if input value is negative value', + inputValue: '-1', + inputDefaultValue: 1, + expectedValue: 1, + }, + { + description: 'returns default value if input value is not a number', + inputValue: 'a', + inputDefaultValue: 1, + expectedValue: 1, + }, + ] + + testsParams.map((testParams) => { + it(testParams.description, () => { + assert.equal( + getNumberQueryValue( + testParams.inputValue, + testParams.inputDefaultValue + ), + testParams.expectedValue + ) + }) + }) +}) + +describe('getStringQueryValue', () => { + const testsParams = [ + { + description: 'returns input value if input value is in list', + inputValue: 'asc', + inputDefaultValue: 'asc', + expectedValue: 'asc', + }, + { + description: 'returns default value if input value is null', + inputValue: null, + inputDefaultValue: 'asc', + expectedValue: 'asc', + }, + { + description: 'returns default value if input value is not in list', + inputValue: '1', + inputDefaultValue: 'asc', + expectedValue: 'asc', + }, + ] + + testsParams.map((testParams) => { + it(testParams.description, () => { + assert.equal( + getStringQueryValue( + testParams.inputValue, + sortList, + testParams.inputDefaultValue + ), + testParams.expectedValue + ) + }) + }) +}) + +describe('getQuery', () => { + const testsParams = [ + { + description: 'returns default query if location query is an empty object', + inputLocationQuery: {}, + expectedQuery: { + page: defaultPage, + per_page: defaultPerPage, + order: defaultSort, + order_by: defaultOrderBy, + }, + }, + { + description: 'returns query with input page', + inputLocationQuery: generateLocationQuery({ page: '2' }), + expectedQuery: { + page: 2, + per_page: defaultPerPage, + order: defaultSort, + order_by: defaultOrderBy, + }, + }, + { + description: 'returns query with input per_page', + inputLocationQuery: generateLocationQuery({ per_page: '20' }), + expectedQuery: { + page: defaultPage, + per_page: 20, + order: defaultSort, + order_by: defaultOrderBy, + }, + }, + { + description: 'returns query with input order', + inputLocationQuery: generateLocationQuery({ order: 'asc' }), + expectedQuery: { + page: defaultPage, + per_page: defaultPerPage, + order: 'asc', + order_by: defaultOrderBy, + }, + }, + { + description: 'returns query with input order_by', + inputLocationQuery: generateLocationQuery({ order_by: 'username' }), + expectedQuery: { + page: defaultPage, + per_page: defaultPerPage, + order: defaultSort, + order_by: 'username', + }, + }, + { + description: + 'returns default query with input location query values are invalid', + inputLocationQuery: generateLocationQuery({ + page: '0', + per_page: '0', + order: 'random', + order_by: 'name', + }), + expectedQuery: { + page: defaultPage, + per_page: defaultPerPage, + order: defaultSort, + order_by: defaultOrderBy, + }, + }, + ] + + testsParams.map((testParams) => { + it(testParams.description, () => { + assert.deepEqual( + getQuery(testParams.inputLocationQuery, orderByList, defaultOrderBy, { + defaultSort, + }), + testParams.expectedQuery + ) + }) + }) +}) + +describe('getQuery w/ default values', () => { + it('returns default query if location query is an empty object', () => { + assert.deepEqual(getQuery({}, orderByList, defaultOrderBy), { + page: 1, + per_page: 10, + order: 'asc', + order_by: defaultOrderBy, + }) + }) +}) + +describe('getQuery w/ default values and input pagination payload', () => { + const inputQuery = { + page: 2, + per_page: 20, + order: 'desc', + order_by: 'username', + } + + it('returns query updated with default values', () => { + assert.deepEqual( + getQuery({}, orderByList, defaultOrderBy, { query: inputQuery }), + { + page: 1, + per_page: 10, + order: 'asc', + order_by: defaultOrderBy, + } + ) + }) + + it('returns query updated with input values', () => { + assert.deepEqual( + getQuery({}, orderByList, defaultOrderBy, { + defaultSort: 'desc', + query: inputQuery, + }), + { + page: 1, + per_page: 10, + order: 'desc', + order_by: defaultOrderBy, + } + ) + }) + + it('returns query updated', () => { + assert.deepEqual( + getQuery( + { page: '3', per_page: '10', order: 'asc', order_by: 'workouts_count' }, + orderByList, + defaultOrderBy, + { query: inputQuery } + ), + { page: 3, per_page: 10, order: 'asc', order_by: 'workouts_count' } + ) + }) +})