Skip to content

Commit

Permalink
fix(ui): correctly paginate request list
Browse files Browse the repository at this point in the history
  • Loading branch information
sct committed Mar 29, 2021
1 parent a9461f7 commit 67fbb40
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 68 deletions.
36 changes: 4 additions & 32 deletions src/components/RequestList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
import { useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages';
import Button from '../Common/Button';
Expand Down Expand Up @@ -42,6 +43,7 @@ const RequestList: React.FC = () => {

const page = router.query.page ? Number(router.query.page) : 1;
const pageIndex = page - 1;
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });

const { data, error, revalidate } = useSWR<RequestResultsResponse>(
`/api/v1/request?take=${currentPageSize}&skip=${
Expand Down Expand Up @@ -284,43 +286,13 @@ const RequestList: React.FC = () => {
<div className="flex justify-center flex-auto space-x-2 sm:justify-end sm:flex-1">
<Button
disabled={!hasPrevPage}
onClick={() =>
router
.push(
{
pathname: `${router.pathname}?page=${page - 1}`,
query: router.query.userId
? { userId: router.query.userId }
: {},
},
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
}
onClick={() => updateQueryParams('page', (page - 1).toString())}
>
{intl.formatMessage(globalMessages.previous)}
</Button>
<Button
disabled={!hasNextPage}
onClick={() =>
router
.push(
{
pathname: `${router.pathname}?page=${page + 1}`,
query: router.query.userId
? { userId: router.query.userId }
: {},
},
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
}
onClick={() => updateQueryParams('page', (page + 1).toString())}
>
{intl.formatMessage(globalMessages.next)}
</Button>
Expand Down
22 changes: 4 additions & 18 deletions src/components/Settings/SettingsLogs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LogMessage,
LogsResultsResponse,
} from '../../../../server/interfaces/api/settingsInterfaces';
import { useUpdateQueryParams } from '../../../hooks/useUpdateQueryParams';
import globalMessages from '../../../i18n/globalMessages';
import Error from '../../../pages/_error';
import Badge from '../../Common/Badge';
Expand Down Expand Up @@ -53,6 +54,7 @@ const SettingsLogs: React.FC = () => {

const page = router.query.page ? Number(router.query.page) : 1;
const pageIndex = page - 1;
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });

const toggleLogs = () => {
setRefreshInterval(refreshInterval === 5000 ? 0 : 5000);
Expand Down Expand Up @@ -456,31 +458,15 @@ const SettingsLogs: React.FC = () => {
<Button
disabled={!hasPrevPage}
onClick={() =>
router
.push(
`${router.pathname}?page=${page - 1}`,
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
updateQueryParams('page', (page - 1).toString())
}
>
{intl.formatMessage(globalMessages.previous)}
</Button>
<Button
disabled={!hasNextPage}
onClick={() =>
router
.push(
`${router.pathname}?page=${page + 1}`,
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
updateQueryParams('page', (page + 1).toString())
}
>
{intl.formatMessage(globalMessages.next)}
Expand Down
22 changes: 4 additions & 18 deletions src/components/UserList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as Yup from 'yup';
import type { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
import { hasPermission } from '../../../server/lib/permissions';
import AddUserIcon from '../../assets/useradd.svg';
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
import { Permission, User, UserType, useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages';
import Alert from '../Common/Alert';
Expand Down Expand Up @@ -79,6 +80,7 @@ const UserList: React.FC = () => {

const page = router.query.page ? Number(router.query.page) : 1;
const pageIndex = page - 1;
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });

const { data, error, revalidate } = useSWR<UserResultsResponse>(
`/api/v1/user?take=${currentPageSize}&skip=${
Expand Down Expand Up @@ -675,31 +677,15 @@ const UserList: React.FC = () => {
<Button
disabled={!hasPrevPage}
onClick={() =>
router
.push(
`${router.pathname}?page=${page - 1}`,
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
updateQueryParams('page', (page - 1).toString())
}
>
{intl.formatMessage(globalMessages.previous)}
</Button>
<Button
disabled={!hasNextPage}
onClick={() =>
router
.push(
`${router.pathname}?page=${page + 1}`,
undefined,
{
shallow: true,
}
)
.then(() => window.scrollTo(0, 0))
updateQueryParams('page', (page + 1).toString())
}
>
{intl.formatMessage(globalMessages.next)}
Expand Down
133 changes: 133 additions & 0 deletions src/hooks/useUpdateQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { NextRouter, useRouter } from 'next/router';
import { ParsedUrlQuery } from 'querystring';
import { useCallback } from 'react';

type UseQueryParamReturnedFunction = (
query: ParsedUrlQuery,
routerAction?: 'push' | 'replace'
) => void;

interface MergedQueryString {
pathname: string;
path: string;
}

/**
* Returns a filtered object containing only key/value pairs that don't exist in the current
* router path.
*
* @param router Nextjs router instance
* @param filters Object containing key value pairs for filter items that should be cleaned
*/
export const filterQueryString = (
router: NextRouter,
filters: ParsedUrlQuery
): ParsedUrlQuery => {
const cleanedFilters: ParsedUrlQuery = {};

Object.keys(filters).forEach((key) => {
if (!router.pathname.match(new RegExp(`${key}`))) {
cleanedFilters[key] = filters[key];
}
});

return cleanedFilters;
};

/**
* Takes a query paramter object and returns a new pathname and path
* with the new values appended.
*
* - If the value already exists, it is updated.
* - If a key with the value of null is passed, it will be removed from
* the current query paramters
*
* ## Example usage:
*
* If the current URL is `/foo?bar=test` and you want to add a new query parameter, you
* can do the following:
*
* ```
* const newRoute = mergeQueryString(router, { newParam: 'value' });
* ```
* NewRoute will become
*
* ```
* {
* pathName: '/foo?bar=test&newParam=value',
* path: '/foo?bar=test&newParam=value'
* }
* ```
*
* @param router Nextjs router instance
* @param query Key/value pair object containing query paramters
*/
export const mergeQueryString = (
router: NextRouter,
query: ParsedUrlQuery
): MergedQueryString => {
const cleanedQuery = filterQueryString(router, router.query);

const mergedQuery = Object.assign({}, cleanedQuery, query);

const queryArray: string[] = [];

Object.keys(mergedQuery).map((key) => {
if (mergedQuery[key]) {
queryArray.push(`${key}=${mergedQuery[key]}`);
}
});

const pathWithoutQuery = router.asPath.match(/(.*)\?.*/);
const asPath = pathWithoutQuery ? pathWithoutQuery[1] : router.asPath;

const pathname = `${router.pathname}${
queryArray.length > 0 ? `?${queryArray.join('&')}` : ''
}`;
const path = `${asPath}${
queryArray.length > 0 ? `?${queryArray.join('&')}` : ''
}`;

return { pathname, path };
};

/**
* useQueryParams hook is used just to provide a callback with a nextjs
* router instance attached to it. The returned method can be called with
* an object of key/value pairs to route the user with the new query paramters
*/
export const useQueryParams = (): UseQueryParamReturnedFunction => {
const router = useRouter();

return useCallback(
(query: ParsedUrlQuery, routerAction: 'push' | 'replace' = 'push') => {
const newRoute = mergeQueryString(router, query);

if (newRoute.path !== router.asPath) {
if (routerAction === 'replace') {
router.replace(newRoute.pathname, newRoute.path);
} else {
router.push(newRoute.pathname, newRoute.path);
}
}
},
[router]
);
};

export const useUpdateQueryParams = (
filter: ParsedUrlQuery
): ((key: string, value?: string) => void) => {
const updateQueryParams = useQueryParams();

return useCallback(
(key: string, value?: string) => {
const query = {
...filter,
[key]: value,
};
updateQueryParams(query, 'replace');
},
[filter, updateQueryParams]
);
};

0 comments on commit 67fbb40

Please sign in to comment.