#5 - add pagination

This commit is contained in:
Job Rapati 2023-09-22 14:34:04 +02:00
parent 6746f0f78b
commit e1ec05a30d
4 changed files with 146 additions and 23 deletions

View File

@ -4,6 +4,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers\CurseForge;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Nette\NotImplementedException; use Nette\NotImplementedException;
use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; 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'); $this->modpack_class_id = config('curseforge.minecraft_modpack_class_id');
} }
public function index() { public function index(Request $request) {
$result = $this->http_client->get("mods/search?gameid=$this->minecraft_game_id&classid=$this->modpack_class_id&sortField=2"); $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) { if($result->getStatusCode() !== 200) {
throw new DisplayException('Failed to fetch modpacks from CurseForge.'); throw new DisplayException('Failed to fetch modpacks from CurseForge.');

View File

@ -1,14 +1,26 @@
import http from '@/api/http' import http, { PaginationDataSet } from '@/api/http'
import { Modpack } from './Modpack'; import { Modpack } from './Modpack';
export default (uuid: string): Promise<any> => { export default (uuid: string, pageIndex: number = 0): Promise<any> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${uuid}/modpacks`) http.get(`/api/client/servers/${uuid}/modpacks?pageindex=${pageIndex}`)
.then((response) => .then((response) => {
resolve((response.data.data || []).map((item: any) => rawDataToModpackData(item))))
console.log(rawDataToModpackPaginationData(response.data.pagination), response.data.pagination);
resolve([(response.data.data || []).map((item: any) => rawDataToModpackData(item)), rawDataToModpackPaginationData(response.data.pagination)])
})
.catch(reject); .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 => ({ export const rawDataToModpackData = (data: any): Modpack => ({
id: data.id, id: data.id,
gameId: data.gameId, gameId: data.gameId,

View File

@ -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<T> {
items: T[];
isLastPage: boolean;
isFirstPage: boolean;
}
interface Props<T> {
data: PaginatedResult<T>;
showGoToLast?: boolean;
showGoToFirst?: boolean;
onPageSelect: (page: number) => void;
children: (props: RenderFuncProps<T>) => React.ReactNode;
paginationButtonsClassNames?: string;
}
const Block = styled(Button)`
${tw`p-0 w-10 h-10`}
&:not(:last-of-type) {
${tw`mr-2`};
}
`;
function Pagination<T>({ data: { items, pagination }, onPageSelect, children, paginationButtonsClassNames}: Props<T>) {
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 })}
<div className={paginationButtonsClassNames}>
{pages.length > 1 && (
<div css={tw`mt-4 flex justify-center`}>
{pages[0] > 1 && !isFirstPage && (
<Block isSecondary color={'primary'} onClick={() => onPageSelect(1)}>
<FontAwesomeIcon icon={faAngleDoubleLeft} />
</Block>
)}
{pages.map((i) => (
<Block
isSecondary={pagination.currentPage !== i}
color={'primary'}
key={`block_page_${i}`}
onClick={() => onPageSelect(i)}
>
{i}
</Block>
))}
{pages[4] < pagination.totalPages && !isLastPage && (
<Block isSecondary color={'primary'} onClick={() => onPageSelect(pagination.totalPages)}>
<FontAwesomeIcon icon={faAngleDoubleRight} />
</Block>
)}
</div>
)}
</div>
</>
);
}
export default Pagination;

View File

@ -12,30 +12,54 @@ import databases from '@/state/server/databases';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
import Fade from '@/components/elements/Fade'; import Fade from '@/components/elements/Fade';
import { Modpack } from '@/api/server/modpacks/Modpack'; import { Modpack } from '@/api/server/modpacks/Modpack';
import Pagination from '@/components/elements/Pagination';
import { PaginatedResult } from '@/api/http';
export default() => { export default() => {
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [page, setPage] = useState(1);
const [modpacks, setModpacks] = useState<Modpack[]>([]); const [modpacks, setModpacks] = useState<PaginatedResult<Modpack>>();
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(() => { useEffect(() => {
setLoading(!modpacks.length); setLoading(!modpacks?.items.length);
getModpacks(uuid) getModpacks(uuid)
.then((modpacks) => setModpacks(modpacks)) .then((modpacksResult) => {
setModpacks({items: modpacksResult[0], pagination: modpacksResult[1]});
});
}, []); }, []);
return ( return (
<ServerContentBlock title="Modpacks"> <ServerContentBlock title="Modpacks">
<FlashMessageRender byKey={'modpacks'} css={tw`mb-4`} /> <FlashMessageRender byKey={'modpacks'} css={tw`mb-4`} />
{!modpacks.length && loading ? ( {(modpacks == undefined || !modpacks?.items.length) && loading ? (
<Spinner size={'large'} centered /> <Spinner size={'large'} centered />
) : ( ) : (
<Fade timeout={150}> <Fade timeout={150}>
<div className='grid grid-cols-3 gap-4'> <div className='grid grid-cols-3 gap-4'>
{modpacks.length > 0 ? ( <Pagination data={modpacks as PaginatedResult<Modpack>} onPageSelect={changePage} paginationButtonsClassNames='col-span-3'>
modpacks.map((modpack, index) => ( {({ items }) => (
items.length > 0 ? (
modpacks?.items.map((modpack, index) => (
<ModpackItem <ModpackItem
key={modpack.id} key={modpack.id}
modpack={modpack} modpack={modpack}
@ -45,7 +69,8 @@ export default() => {
<p css={tw`text-center text-sm text-neutral-300`}> <p css={tw`text-center text-sm text-neutral-300`}>
Couldn't fetch modpacks. Couldn't fetch modpacks.
</p> </p>
)} ))}
</Pagination>
</div> </div>
</Fade> </Fade>
)} )}