From e1ec05a30d18c838cede3577bdcf183c9680b5d2 Mon Sep 17 00:00:00 2001 From: Job Rapati Date: Fri, 22 Sep 2023 14:34:04 +0200 Subject: [PATCH] #5 - add pagination --- .../Servers/CurseForge/ModpackController.php | 10 ++- .../api/server/modpacks/getModpacks.ts | 22 +++-- .../components/elements/Pagination.tsx | 80 +++++++++++++++++++ .../server/modpacks/ModpackContainer.tsx | 57 +++++++++---- 4 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 resources/scripts/components/elements/Pagination.tsx diff --git a/app/Http/Controllers/Client/Servers/CurseForge/ModpackController.php b/app/Http/Controllers/Client/Servers/CurseForge/ModpackController.php index 7990c6b..064b07e 100644 --- a/app/Http/Controllers/Client/Servers/CurseForge/ModpackController.php +++ b/app/Http/Controllers/Client/Servers/CurseForge/ModpackController.php @@ -4,6 +4,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers\CurseForge; use GuzzleHttp\Client; +use Illuminate\Http\Request; use Nette\NotImplementedException; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; @@ -31,8 +32,13 @@ class ModpackController extends ClientApiController { $this->modpack_class_id = config('curseforge.minecraft_modpack_class_id'); } - public function index() { - $result = $this->http_client->get("mods/search?gameid=$this->minecraft_game_id&classid=$this->modpack_class_id&sortField=2"); + public function index(Request $request) { + $index = 0; + if($request->has('pageindex')) { + $index = $request->get('pageindex'); + } + + $result = $this->http_client->get("mods/search?gameid=$this->minecraft_game_id&classid=$this->modpack_class_id&sortField=2&sortorder=desc&index=$index"); if($result->getStatusCode() !== 200) { throw new DisplayException('Failed to fetch modpacks from CurseForge.'); diff --git a/resources/scripts/api/server/modpacks/getModpacks.ts b/resources/scripts/api/server/modpacks/getModpacks.ts index 59e9fda..dba853f 100644 --- a/resources/scripts/api/server/modpacks/getModpacks.ts +++ b/resources/scripts/api/server/modpacks/getModpacks.ts @@ -1,14 +1,26 @@ -import http from '@/api/http' +import http, { PaginationDataSet } from '@/api/http' import { Modpack } from './Modpack'; -export default (uuid: string): Promise => { +export default (uuid: string, pageIndex: number = 0): Promise => { return new Promise((resolve, reject) => { - http.get(`/api/client/servers/${uuid}/modpacks`) - .then((response) => - resolve((response.data.data || []).map((item: any) => rawDataToModpackData(item)))) + http.get(`/api/client/servers/${uuid}/modpacks?pageindex=${pageIndex}`) + .then((response) => { + + console.log(rawDataToModpackPaginationData(response.data.pagination), response.data.pagination); + resolve([(response.data.data || []).map((item: any) => rawDataToModpackData(item)), rawDataToModpackPaginationData(response.data.pagination)]) + }) .catch(reject); }); } + +export const rawDataToModpackPaginationData = (data: any): PaginationDataSet => ({ + total: data.totalCount, + count: data.resultCount, + perPage: data.pageSize, + currentPage: Math.ceil((data.index + data.pageSize) / data.pageSize) == 0 ? 1 : Math.ceil((data.index + data.pageSize) / data.pageSize), + totalPages: Math.ceil(data.totalCount / data.pageSize) + 1 +}); + export const rawDataToModpackData = (data: any): Modpack => ({ id: data.id, gameId: data.gameId, diff --git a/resources/scripts/components/elements/Pagination.tsx b/resources/scripts/components/elements/Pagination.tsx new file mode 100644 index 0000000..ca7de9a --- /dev/null +++ b/resources/scripts/components/elements/Pagination.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { PaginatedResult } from '@/api/http'; +import tw from 'twin.macro'; +import styled from 'styled-components/macro'; +import Button from '@/components/elements/Button'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faAngleDoubleLeft, faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons'; + +interface RenderFuncProps { + items: T[]; + isLastPage: boolean; + isFirstPage: boolean; +} + +interface Props { + data: PaginatedResult; + showGoToLast?: boolean; + showGoToFirst?: boolean; + onPageSelect: (page: number) => void; + children: (props: RenderFuncProps) => React.ReactNode; + paginationButtonsClassNames?: string; +} + +const Block = styled(Button)` + ${tw`p-0 w-10 h-10`} + + &:not(:last-of-type) { + ${tw`mr-2`}; + } +`; + +function Pagination({ data: { items, pagination }, onPageSelect, children, paginationButtonsClassNames}: Props) { + const isFirstPage = pagination.currentPage === 1; + const isLastPage = pagination.currentPage >= pagination.totalPages; + + const pages = []; + + // Start two spaces before the current page. If that puts us before the starting page default + // to the first page as the starting point. + const start = Math.max(pagination.currentPage - 2, 1); + const end = Math.min(pagination.totalPages, pagination.currentPage + 5); + + for (let i = start; i <= end; i++) { + pages.push(i); + } + + return ( + <> + {children({ items, isFirstPage, isLastPage })} +
+ {pages.length > 1 && ( +
+ {pages[0] > 1 && !isFirstPage && ( + onPageSelect(1)}> + + + )} + {pages.map((i) => ( + onPageSelect(i)} + > + {i} + + ))} + {pages[4] < pagination.totalPages && !isLastPage && ( + onPageSelect(pagination.totalPages)}> + + + )} +
+ )} +
+ + ); +} + +export default Pagination; diff --git a/resources/scripts/components/server/modpacks/ModpackContainer.tsx b/resources/scripts/components/server/modpacks/ModpackContainer.tsx index 17f14ad..2e1f5a2 100644 --- a/resources/scripts/components/server/modpacks/ModpackContainer.tsx +++ b/resources/scripts/components/server/modpacks/ModpackContainer.tsx @@ -12,40 +12,65 @@ import databases from '@/state/server/databases'; import Spinner from '@/components/elements/Spinner'; import Fade from '@/components/elements/Fade'; import { Modpack } from '@/api/server/modpacks/Modpack'; +import Pagination from '@/components/elements/Pagination'; +import { PaginatedResult } from '@/api/http'; export default() => { const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); const [loading, setLoading] = useState(true); + const [page, setPage] = useState(1); - const [modpacks, setModpacks] = useState([]); + const [modpacks, setModpacks] = useState>(); + + const changePage = (newPage: number) => { + setPage(newPage); + setLoading(true); + + let pageIndex = 0; + if(modpacks?.pagination.perPage != undefined) { + pageIndex = (modpacks.pagination.perPage * newPage) - modpacks.pagination.perPage; + console.log(pageIndex, modpacks.pagination.perPage, newPage); + } + + getModpacks(uuid, pageIndex) + .then((modpacksResult) => { + setModpacks({items: modpacksResult[0], pagination: modpacksResult[1]}); + setLoading(false); + }) + } useEffect(() => { - setLoading(!modpacks.length); + setLoading(!modpacks?.items.length); getModpacks(uuid) - .then((modpacks) => setModpacks(modpacks)) + .then((modpacksResult) => { + setModpacks({items: modpacksResult[0], pagination: modpacksResult[1]}); + }); }, []); return ( - {!modpacks.length && loading ? ( + {(modpacks == undefined || !modpacks?.items.length) && loading ? ( ) : (
- {modpacks.length > 0 ? ( - modpacks.map((modpack, index) => ( - - )) - ) : ( -

- Couldn't fetch modpacks. -

- )} + } onPageSelect={changePage} paginationButtonsClassNames='col-span-3'> + {({ items }) => ( + items.length > 0 ? ( + modpacks?.items.map((modpack, index) => ( + + )) + ) : ( +

+ Couldn't fetch modpacks. +

+ ))} +
)}