Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/OP-108/pagination-limit #375

Merged
merged 10 commits into from
Oct 31, 2023
11 changes: 8 additions & 3 deletions pwa/src/apiService/resources/applications.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Send } from "../apiService";
import { AxiosInstance } from "axios";
import { IFiltersContext } from "../../context/filters";

export default class Applications {
private _instance: AxiosInstance;
Expand All @@ -15,9 +14,15 @@ export default class Applications {
return data;
};

public getAll = async (filters: IFiltersContext, currentPage: number): Promise<any> => {
const { data } = await Send(this._instance, "GET", `/applications?page=${currentPage}&limit=10&extend[]=all`);
public getAll = async (currentPage: number, limit: number): Promise<any> => {
const { data } = await Send(this._instance, "GET", `/applications?page=${currentPage}&limit=${limit}&extend[]=all`);

return data;
};

public getCount = async (): Promise<any> => {
const { data } = await Send(this._instance, "GET", `/applications?limit=1`);

return data.total;
};
}
4 changes: 2 additions & 2 deletions pwa/src/apiService/resources/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export default class Organization {
return data;
};

public getAll = async (filters: IFiltersContext, currentPage: number): Promise<any> => {
let url = `/organizations?page=${currentPage}&order[owns]=desc&limit=10&extend[]=all`;
public getAll = async (filters: IFiltersContext, currentPage: number, limit: number): Promise<any> => {
let url = `/organizations?page=${currentPage}&order[owns]=desc&limit=${limit}&extend[]=all`;

if (filters.organizationSearch) {
url += `&_search=${filters.organizationSearch}`;
Expand Down
4 changes: 2 additions & 2 deletions pwa/src/apiService/resources/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default class Search {
this._instance = _instance;
}

public getSearch = async (filters: IFiltersContext, currentPage: number): Promise<any> => {
let endpoint = `/search?page=${currentPage}&order[_self.dateCreated]=desc&limit=10&extend[]=all${filtersToQueryParams(
public getSearch = async (filters: IFiltersContext, currentPage: number, limit: number): Promise<any> => {
let endpoint = `/search?page=${currentPage}&order[_self.dateCreated]=desc&limit=${limit}&extend[]=all${filtersToQueryParams(
filters,
)}`;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.container {
display: flex;
flex-wrap: wrap;
gap: var(--web-app-size-xs);
align-items: center;
list-style-type: none;

padding: 0;
margin: 0;

user-select: none;
}
75 changes: 75 additions & 0 deletions pwa/src/components/paginationLimitSelect/PaginationLimitSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from "react";
import * as styles from "./PaginationLimitSelect.module.css";
import clsx from "clsx";
import { useForm } from "react-hook-form";
import { SelectSingle } from "@conduction/components";
import { IQueryLimitContext, QUERY_LIMIT_DEFAULT, useQueryLimitContext } from "../../context/queryLimit";
import { useTranslation } from "react-i18next";

interface PaginationLimitSelectProps {
queryLimitName: string;
layoutClassName?: string;
}

export const PaginationLimitSelectComponent: React.FC<PaginationLimitSelectProps> = ({
queryLimitName,
layoutClassName,
}) => {
const {
watch,
register,
control,
setValue,
formState: { errors },
} = useForm();
const { queryLimit, setQueryLimit } = useQueryLimitContext();
const { t } = useTranslation();

const watchLimit = watch("limit");

const value = queryLimit[queryLimitName as keyof IQueryLimitContext];

React.useEffect(() => {
if (!watchLimit) return;
if (parseInt(watchLimit.value) === value) return;

const selectedLimit = limitSelectOptions.find((limitOption) => limitOption.value === watchLimit.value);

if (selectedLimit) {
setQueryLimit({ ...queryLimit, [queryLimitName]: parseInt(selectedLimit.value) });
}
}, [watchLimit]);

React.useEffect(() => {
setValue(
"limit",
limitSelectOptions.find((limitOption) => limitOption.value === (value !== undefined && value.toString())),
);
}, []);

return (
<div className={clsx(styles.container, layoutClassName && layoutClassName)}>
<span>{t("Results per page")}:</span>
<SelectSingle
ariaLabel={t("Select result limit")}
{...{ register, errors, control }}
defaultValue={QUERY_LIMIT_DEFAULT}
name="limit"
options={limitSelectOptions}
menuPlacement="auto"
placeholder={t("Limit")}
/>
</div>
);
};

const limitSelectOptions = [
{ label: "6", value: "6" },
{ label: "8", value: "8" },
{ label: "10", value: "10" },
{ label: "16", value: "16" },
{ label: "20", value: "20" },
{ label: "40", value: "40" },
{ label: "60", value: "60" },
{ label: "100", value: "100" },
];
3 changes: 3 additions & 0 deletions pwa/src/context/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ import * as React from "react";
import { defaultGatsbyContext, IGatsbyContext } from "./gatsby";
import { defaultFiltersContext, IFiltersContext } from "./filters";
import { defaultPaginationContext, IPaginationContext } from "./pagination";
import { defaultQueryLimitContext, IQueryLimitContext } from "./queryLimit";

export interface IGlobalContext {
initiated: boolean;
gatsby: IGatsbyContext;
filters: IFiltersContext;
pagination: IPaginationContext;
queryLimit: IQueryLimitContext;
}

export const defaultGlobalContext: IGlobalContext = {
initiated: false,
gatsby: defaultGatsbyContext,
filters: defaultFiltersContext,
pagination: defaultPaginationContext,
queryLimit: defaultQueryLimitContext,
};

export const GlobalContext = React.createContext<
Expand Down
28 changes: 28 additions & 0 deletions pwa/src/context/queryLimit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import { GlobalContext } from "./global";

export const QUERY_LIMIT_DEFAULT = 10;

export interface IQueryLimitContext {
componentsSearchQueryLimit: number;
organizationsQueryLimit: number;
applicationsQueryLimit: number;
}

export const defaultQueryLimitContext: IQueryLimitContext = {
componentsSearchQueryLimit: QUERY_LIMIT_DEFAULT,
organizationsQueryLimit: QUERY_LIMIT_DEFAULT,
applicationsQueryLimit: QUERY_LIMIT_DEFAULT,
};

export const useQueryLimitContext = () => {
const [globalContext, setGlobalContext] = React.useContext(GlobalContext);

const queryLimit: IQueryLimitContext = globalContext.queryLimit;

const setQueryLimit = (query: IQueryLimitContext) => {
setGlobalContext((context) => ({ ...context, queryLimit: { ...globalContext.queryLimit, ...query } }));
};

return { setQueryLimit, queryLimit };
};
27 changes: 21 additions & 6 deletions pwa/src/hooks/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import { IFiltersContext } from "../context/filters";
export const useApplications = (queryClient: QueryClient) => {
const API: APIService | null = React.useContext(APIContext);

const getAll = (filters: IFiltersContext, currentPage: number) =>
useQuery<any, Error>(["applications", filters, currentPage], () => API?.Applications.getAll(filters, currentPage), {
onError: (error) => {
throw new Error(error.message);
const getAll = (filters: IFiltersContext, currentPage: number, limit: number) =>
useQuery<any, Error>(
["applications", filters, currentPage, limit],
() => API?.Applications.getAll(currentPage, limit),
{
onError: (error) => {
throw new Error(error.message);
},
},
});
);

const getOne = (applicationId: string) =>
useQuery<any, Error>(["applications", applicationId], () => API?.Applications.getOne(applicationId), {
Expand All @@ -24,5 +28,16 @@ export const useApplications = (queryClient: QueryClient) => {
enabled: !!applicationId,
});

return { getOne, getAll };
const getCount = () =>
useQuery<any, Error>(["applications_count"], () => API?.Applications.getCount(), {
onError: (error) => {
throw new Error(error.message);
},
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
staleTime: 60 * 10 * 1000, // 10 minutes
});

return { getOne, getAll, getCount };
};
6 changes: 3 additions & 3 deletions pwa/src/hooks/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export const useOrganization = (queryClient: QueryClient) => {
enabled: !!organizationId,
});

const getAll = (filters: IFiltersContext, currentPage: number) =>
const getAll = (filters: IFiltersContext, currentPage: number, limit: number) =>
useQuery<any, Error>(
["organizations", filters, currentPage],
() => API?.Organization.getAll(filters, currentPage),
["organizations", filters, currentPage, limit],
() => API?.Organization.getAll(filters, currentPage, limit),
{
onError: (error) => {
throw new Error(error.message);
Expand Down
6 changes: 3 additions & 3 deletions pwa/src/hooks/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { IFiltersContext } from "../context/filters";
export const useSearch = (_: QueryClient) => {
const API: APIService | null = React.useContext(APIContext);

const getSearch = (filters: IFiltersContext, currentPage: number) =>
const getSearch = (filters: IFiltersContext, currentPage: number, limit: number) =>
useQuery<any, Error>(
["search", filters, currentPage],
() => API?.Search.getSearch(filters, currentPage),
["search", filters, currentPage, limit],
() => API?.Search.getSearch(filters, currentPage, limit),
{
onError: (error) => {
throw new Error(error.message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
}
}

.inlineTextLink {
display: inline-flex;
align-items: baseline !important;
}

/* pagination */

.paginationContainer {
Expand All @@ -59,6 +64,13 @@
flex: 1;
}

.pagination {
display: grid;
justify-content: unset;
justify-items: center;
margin-block-start: var(--web-app-size-lg);
}

@media only screen and (min-width: 992px) {
.paginationContainer > *:not(:first-child) {
margin-inline-start: var(--web-app-size-xl);
Expand Down
49 changes: 34 additions & 15 deletions pwa/src/templates/applicationsTemplate/ApplicationsTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import { ApplicationCard } from "../../components/applicationCard/ApplicationCar
import { QueryClient } from "react-query";
import { useApplications } from "../../hooks/applications";
import Skeleton from "react-loading-skeleton";
import { IconExternalLink } from "@tabler/icons-react";
import { usePaginationContext } from "../../context/pagination";
import { PaginationLimitSelectComponent } from "../../components/paginationLimitSelect/PaginationLimitSelect";
import { useQueryLimitContext } from "../../context/queryLimit";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLink } from "@fortawesome/free-solid-svg-icons";

export const ApplicationsTemplate: React.FC = () => {
const { t } = useTranslation();
const { filters } = useFiltersContext();
const { queryLimit } = useQueryLimitContext();
const { pagination, setPagination } = usePaginationContext();

const queryClient = new QueryClient();
Expand All @@ -23,32 +27,43 @@ export const ApplicationsTemplate: React.FC = () => {
...filters,
},
pagination.applicationCurrentPage,
queryLimit.applicationsQueryLimit,
);

const applicationsCount = _useApplications.getCount();

React.useEffect(() => {
setPagination({ ...pagination, applicationCurrentPage: 1 });
}, [queryLimit.applicationsQueryLimit]);

return (
<Container layoutClassName={styles.container}>
<div className={styles.header}>
<div>
<Heading level={2} className={styles.title}>
{t("Applications")}
{t("Applications")} {applicationsCount.data >= 0 && `(${applicationsCount.data})`}
</Heading>
<Paragraph className={styles.description}>
Totaal oplossing op basis van een set componenten. Het gaat om werkende software die een oplossing biedt
voor een bepaalde{" "}
<span>
<Link target="_new" href="https://www.gemmaonline.nl/index.php/GEMMA_Bedrijfsfuncties">
<Link
className={styles.inlineTextLink}
target="_new"
href="https://www.gemmaonline.nl/index.php/GEMMA_Bedrijfsfuncties"
>
<Icon>
<IconExternalLink />
</Icon>{" "}
bedrijfsfunctie
<FontAwesomeIcon icon={faExternalLink} />
</Icon>
{t("Business function")}
</Link>
</span>
.
</Paragraph>
</div>
</div>

{getApplications.isSuccess && (
{getApplications.isSuccess && getApplications.data.total !== 0 && (
<>
<div className={styles.ComponentsGrid}>
{getApplications.data?.results?.map((application: any) => (
Expand All @@ -63,17 +78,21 @@ export const ApplicationsTemplate: React.FC = () => {
/>
))}
</div>
<Pagination
layoutClassName={styles.paginationContainer}
totalPages={getApplications.data.pages}
currentPage={getApplications.data.page}
setCurrentPage={(page: any) => setPagination({ ...pagination, applicationCurrentPage: page })}
ariaLabels={{ nextPage: t("Next page"), previousPage: t("Previous page"), page: t("Page") }}
/>
<div className={styles.pagination}>
<Pagination
layoutClassName={styles.paginationContainer}
totalPages={getApplications.data.pages}
currentPage={getApplications.data.page}
setCurrentPage={(page: any) => setPagination({ ...pagination, applicationCurrentPage: page })}
ariaLabels={{ nextPage: t("Next page"), previousPage: t("Previous page"), page: t("Page") }}
/>
<PaginationLimitSelectComponent queryLimitName={"applicationsQueryLimit"} />
</div>
</>
)}

{!getApplications.data?.results && !getApplications.isLoading && "Geen resultaten gevonden"}
{!getApplications.data?.results && !getApplications.isLoading && t("No results found")}
{getApplications.isSuccess && getApplications.data.total === 0 && t("No results available")}

{getApplications.isLoading && <Skeleton height="200px" />}
</Container>
Expand Down
7 changes: 7 additions & 0 deletions pwa/src/templates/components/ComponentsTemplate.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
align-items: center;
}

.pagination {
display: grid;
justify-content: unset;
justify-items: center;
margin-block-start: var(--web-app-size-lg);
}

@media only screen and (min-width: 992px) {
.header {
display: flex;
Expand Down
Loading
Loading