Skip to content

Commit

Permalink
#93 Create paginated component for holding pagination logic and UI (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
nevendyulgerov authored Jan 23, 2024
1 parent df28a38 commit b7f5686
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 144 deletions.
94 changes: 21 additions & 73 deletions apps/web/src/components/applications/applications.tsx
Original file line number Diff line number Diff line change
@@ -1,96 +1,44 @@
"use client";

import { Group, Pagination, Select, Stack, Text } from "@mantine/core";
import { useScrollIntoView } from "@mantine/hooks";
import { pathOr } from "ramda";
import { FC, useEffect, useState } from "react";
import { Stack } from "@mantine/core";
import { FC, useCallback, useState } from "react";
import {
ApplicationOrderByInput,
useApplicationsConnectionQuery,
} from "../../graphql";
import {
limitBounds,
usePaginationParams,
} from "../../hooks/usePaginationParams";
import ApplicationsTable from "./applicationsTable";
import ApplicationsTable from "../applications/applicationsTable";
import Paginated from "../paginated";

const Applications: FC = () => {
const [{ limit, page }, updateParams] = usePaginationParams();
const [limit, setLimit] = useState(10);
const [page, setPage] = useState(1);
const after = page === 1 ? undefined : ((page - 1) * limit).toString();
const [query] = useApplicationsConnectionQuery({
variables: { orderBy: ApplicationOrderByInput.IdAsc, limit, after },
});
const totalInputs = query.data?.applicationsConnection.totalCount ?? 1;
const totalPages = Math.ceil(totalInputs / limit);
const [activePage, setActivePage] = useState(
page > totalPages ? totalPages : page,
);
const applications =
query.data?.applicationsConnection.edges.map((edge) => edge.node) ?? [];
const { scrollIntoView } = useScrollIntoView<HTMLDivElement>({
duration: 700,
offset: 150,
cancelable: true,
});

useEffect(() => {
if (!query.fetching && page > totalPages) {
updateParams(totalPages, limit);
}
}, [limit, page, query.fetching, totalPages, updateParams]);

useEffect(() => {
setActivePage((n) => {
return n !== page ? page : n;
});
}, [page]);
const onChangePagination = useCallback((limit: number, page: number) => {
setLimit(limit);
setPage(page);
}, []);

return (
<Stack>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={(pageN) => {
updateParams(pageN, limit);
}}
/>

<ApplicationsTable
applications={applications}
<Paginated
fetching={query.fetching}
totalCount={query.data?.applicationsConnection.totalCount ?? 0}
/>

<Group justify="space-between" align="center">
<Group>
<Text>Show:</Text>
<Select
style={{ width: "5rem" }}
value={limit.toString()}
onChange={(val) => {
const entry = val ?? limit;
const l = pathOr(limit, [entry], limitBounds);
updateParams(page, l);
}}
data={[
limitBounds[10].toString(),
limitBounds[20].toString(),
limitBounds[30].toString(),
]}
/>
<Text>Applications</Text>
</Group>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={(pageN) => {
updateParams(pageN, limit);
scrollIntoView({ alignment: "center" });
}}
totalCount={query.data?.applicationsConnection.totalCount}
onChange={onChangePagination}
>
<ApplicationsTable
applications={applications}
fetching={query.fetching}
totalCount={
query.data?.applicationsConnection.totalCount ?? 0
}
/>
</Group>
</Paginated>
</Stack>
);
};
Expand Down
90 changes: 19 additions & 71 deletions apps/web/src/components/inputs/inputs.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
"use client";

import { Group, Pagination, Select, Stack, Text } from "@mantine/core";
import { useScrollIntoView } from "@mantine/hooks";
import { pathOr } from "ramda";
import { FC, useEffect, useState } from "react";
import { Stack } from "@mantine/core";
import { FC, useCallback, useEffect, useState } from "react";
import { InputOrderByInput, useInputsQuery } from "../../graphql";
import {
limitBounds,
usePaginationParams,
} from "../../hooks/usePaginationParams";
import InputsTable from "./inputsTable";
import InputsTable from "../inputs/inputsTable";
import Paginated from "../paginated";

export type InputsProps = {
orderBy?: InputOrderByInput;
Expand All @@ -20,7 +15,8 @@ const Inputs: FC<InputsProps> = ({
orderBy = InputOrderByInput.TimestampDesc,
applicationId,
}) => {
const [{ limit, page }, updateParams] = usePaginationParams();
const [limit, setLimit] = useState(10);
const [page, setPage] = useState(1);
const after = page === 1 ? undefined : ((page - 1) * limit).toString();
const [{ data, fetching }] = useInputsQuery({
variables: {
Expand All @@ -30,74 +26,26 @@ const Inputs: FC<InputsProps> = ({
after,
},
});
const totalInputs = data?.inputsConnection.totalCount ?? 1;
const totalPages = Math.ceil(totalInputs / limit);
const [activePage, setActivePage] = useState(
page > totalPages ? totalPages : page,
);
const inputs = data?.inputsConnection.edges.map((edge) => edge.node) ?? [];
const { scrollIntoView } = useScrollIntoView<HTMLDivElement>({
duration: 700,
offset: 150,
cancelable: true,
});

if (!fetching && page > totalPages) {
updateParams(totalPages, limit);
}

useEffect(() => {
setActivePage((n) => {
return n !== page ? page : n;
});
}, [page]);
const onChangePagination = useCallback((limit: number, page: number) => {
setLimit(limit);
setPage(page);
}, []);

return (
<Stack>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={(pageN) => {
updateParams(pageN, limit);
}}
/>

<InputsTable
inputs={inputs}
<Paginated
fetching={fetching}
totalCount={data?.inputsConnection.totalCount ?? 0}
/>

<Group justify="space-between" align="center">
<Group>
<Text>Show:</Text>
<Select
style={{ width: "5rem" }}
value={limit.toString()}
onChange={(val) => {
const entry = val ?? limit;
const l = pathOr(limit, [entry], limitBounds);
updateParams(page, l);
}}
data={[
limitBounds[10].toString(),
limitBounds[20].toString(),
limitBounds[30].toString(),
]}
/>
<Text>inputs</Text>
</Group>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={(pageN) => {
updateParams(pageN, limit);
scrollIntoView({ alignment: "center" });
}}
totalCount={data?.inputsConnection.totalCount}
onChange={onChangePagination}
>
<InputsTable
inputs={inputs}
fetching={fetching}
totalCount={data?.inputsConnection.totalCount ?? 0}
/>
</Group>
</Paginated>
</Stack>
);
};
Expand Down
107 changes: 107 additions & 0 deletions apps/web/src/components/paginated.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"use client";

import { Group, Pagination, Select, Stack, Text } from "@mantine/core";
import { useScrollIntoView } from "@mantine/hooks";
import { pathOr } from "ramda";
import { FC, ReactNode, useCallback, useEffect, useState } from "react";
import { limitBounds, usePaginationParams } from "../hooks/usePaginationParams";

const perPageList = Array.from({ length: 3 }).map((_, index) => {
const key = ((index + 1) * 10).toString() as keyof typeof limitBounds;
return limitBounds[key].toString();
});

export type PaginatedProps = {
children: ReactNode;
totalCount?: number;
fetching: boolean;
onChange: (limit: number, page: number) => void;
};

const Paginated: FC<PaginatedProps> = (props) => {
const { children, totalCount, fetching, onChange } = props;
const [{ limit, page }, updateParams] = usePaginationParams();
const totalPages = Math.ceil((totalCount ?? 1) / limit);
const [activePage, setActivePage] = useState(
page > totalPages ? totalPages : page,
);
const { scrollIntoView } = useScrollIntoView<HTMLDivElement>({
duration: 700,
offset: 150,
cancelable: true,
});

const onChangeTopPagination = useCallback(
(pageN: number) => {
updateParams(pageN, limit);
},
[limit, updateParams],
);

const onChangeBottomPagination = useCallback(
(pageN: number) => {
updateParams(pageN, limit);
scrollIntoView({ alignment: "center" });
},
[limit, scrollIntoView, updateParams],
);

const onChangeLimit = useCallback(
(val: string | null) => {
const entry = val ?? limit;
const nextLimit = pathOr(limit, [entry], limitBounds);
updateParams(page, nextLimit);
},
[limit, page, updateParams],
);

useEffect(() => {
if (!fetching && page > totalPages) {
updateParams(totalPages, limit);
}
}, [limit, page, fetching, totalPages, updateParams]);

useEffect(() => {
setActivePage((activePage) =>
activePage !== page ? page : activePage,
);
}, [page]);

useEffect(() => {
onChange(limit, page);
}, [limit, page, onChange]);

return (
<Stack>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={onChangeTopPagination}
/>

{children}

<Group justify="space-between" align="center">
<Group>
<Text>Show:</Text>
<Select
style={{ width: "5rem" }}
value={limit.toString()}
onChange={onChangeLimit}
data={perPageList}
/>
<Text>items</Text>
</Group>
<Pagination
styles={{ root: { alignSelf: "flex-end" } }}
value={activePage}
total={totalPages}
onChange={onChangeBottomPagination}
/>
</Group>
</Stack>
);
};

export default Paginated;
Loading

0 comments on commit b7f5686

Please sign in to comment.