diff --git a/packages/console/src/pages/ApiResources/index.module.scss b/packages/console/src/pages/ApiResources/index.module.scss index 1122ae523..43664e39b 100644 --- a/packages/console/src/pages/ApiResources/index.module.scss +++ b/packages/console/src/pages/ApiResources/index.module.scss @@ -14,6 +14,10 @@ flex: 1; } +.pagination { + min-height: 64px; +} + .apiResourceName { width: 360px; } diff --git a/packages/console/src/pages/ApiResources/index.tsx b/packages/console/src/pages/ApiResources/index.tsx index c5c5e5e28..d11510b45 100644 --- a/packages/console/src/pages/ApiResources/index.tsx +++ b/packages/console/src/pages/ApiResources/index.tsx @@ -1,11 +1,10 @@ import { Resource } from '@logto/schemas'; -import { conditional } from '@silverhand/essentials/lib/utilities/conditional.js'; import classNames from 'classnames'; import React, { useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import Modal from 'react-modal'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import useSWR from 'swr'; import Button from '@/components/Button'; @@ -14,6 +13,7 @@ import CardTitle from '@/components/CardTitle'; import CopyToClipboard from '@/components/CopyToClipboard'; import ImagePlaceholder from '@/components/ImagePlaceholder'; import ItemPreview from '@/components/ItemPreview'; +import Pagination from '@/components/Pagination'; import TableEmpty from '@/components/Table/TableEmpty'; import TableError from '@/components/Table/TableError'; import TableLoading from '@/components/Table/TableLoading'; @@ -26,12 +26,19 @@ import * as styles from './index.module.scss'; const buildDetailsLink = (id: string) => `/api-resources/${id}`; +const pageSize = 20; + const ApiResources = () => { const [isCreateFormOpen, setIsCreateFormOpen] = useState(false); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { data, error, mutate } = useSWR<Resource[], RequestError>('/api/resources'); + const [query, setQuery] = useSearchParams(); + const pageIndex = Number(query.get('page') ?? '1'); + const { data, error, mutate } = useSWR<[Resource[], number], RequestError>( + `/api/resources?page=${pageIndex}&page_size=${pageSize}` + ); const isLoading = !data && !error; const navigate = useNavigate(); + const [apiResources, totalCount] = data ?? []; return ( <Card className={styles.card}> @@ -54,7 +61,6 @@ const ApiResources = () => { setIsCreateFormOpen(false); if (createdApiResource) { - void mutate(conditional(data && [...data, createdApiResource])); toast.success( t('api_resources.api_resource_created', { name: createdApiResource.name }) ); @@ -85,7 +91,7 @@ const ApiResources = () => { /> )} {isLoading && <TableLoading columns={2} />} - {data?.length === 0 && ( + {apiResources?.length === 0 && ( <TableEmpty columns={2}> <Button title="admin_console.api_resources.create" @@ -96,7 +102,7 @@ const ApiResources = () => { /> </TableEmpty> )} - {data?.map(({ id, name, indicator }) => ( + {apiResources?.map(({ id, name, indicator }) => ( <tr key={id} className={tableStyles.clickable} @@ -115,6 +121,17 @@ const ApiResources = () => { </tbody> </table> </div> + <div className={styles.pagination}> + {!!totalCount && ( + <Pagination + pageCount={Math.ceil(totalCount / pageSize)} + pageIndex={pageIndex} + onChange={(page) => { + setQuery({ page: String(page) }); + }} + /> + )} + </div> </Card> ); }; diff --git a/packages/console/src/pages/Applications/index.module.scss b/packages/console/src/pages/Applications/index.module.scss index e1e6c4560..3f0ea711c 100644 --- a/packages/console/src/pages/Applications/index.module.scss +++ b/packages/console/src/pages/Applications/index.module.scss @@ -14,6 +14,10 @@ flex: 1; } +.pagination { + min-height: 64px; +} + .applicationName { width: 360px; } diff --git a/packages/console/src/pages/Applications/index.tsx b/packages/console/src/pages/Applications/index.tsx index 47a0c9938..2c5c2bd34 100644 --- a/packages/console/src/pages/Applications/index.tsx +++ b/packages/console/src/pages/Applications/index.tsx @@ -1,11 +1,10 @@ import { Application } from '@logto/schemas'; -import { conditional } from '@silverhand/essentials/lib/utilities/conditional.js'; import classNames from 'classnames'; import React, { useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import Modal from 'react-modal'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import useSWR from 'swr'; import Button from '@/components/Button'; @@ -14,6 +13,7 @@ import CardTitle from '@/components/CardTitle'; import CopyToClipboard from '@/components/CopyToClipboard'; import ImagePlaceholder from '@/components/ImagePlaceholder'; import ItemPreview from '@/components/ItemPreview'; +import Pagination from '@/components/Pagination'; import TableEmpty from '@/components/Table/TableEmpty'; import TableError from '@/components/Table/TableError'; import TableLoading from '@/components/Table/TableLoading'; @@ -25,12 +25,19 @@ import { applicationTypeI18nKey } from '@/types/applications'; import CreateForm from './components/CreateForm'; import * as styles from './index.module.scss'; +const pageSize = 20; + const Applications = () => { const [isCreateFormOpen, setIsCreateFormOpen] = useState(false); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { data, error, mutate } = useSWR<Application[], RequestError>('/api/applications'); + const [query, setQuery] = useSearchParams(); + const pageIndex = Number(query.get('page') ?? '1'); + const { data, error, mutate } = useSWR<[Application[], number], RequestError>( + `/api/applications?page=${pageIndex}&page_size=${pageSize}` + ); const isLoading = !data && !error; const navigate = useNavigate(); + const [applications, totalCount] = data ?? []; return ( <Card className={styles.card}> @@ -53,8 +60,6 @@ const Applications = () => { setIsCreateFormOpen(false); if (createdApp) { - void mutate(conditional(data && [...data, createdApp])); - toast.success(t('applications.application_created', { name: createdApp.name })); navigate(`/applications/${createdApp.id}`); } @@ -83,7 +88,7 @@ const Applications = () => { /> )} {isLoading && <TableLoading columns={2} />} - {data?.length === 0 && ( + {applications?.length === 0 && ( <TableEmpty columns={2}> <Button title="admin_console.applications.create" @@ -94,7 +99,7 @@ const Applications = () => { /> </TableEmpty> )} - {data?.map(({ id, name, type }) => ( + {applications?.map(({ id, name, type }) => ( <tr key={id} className={tableStyles.clickable} @@ -118,6 +123,17 @@ const Applications = () => { </tbody> </table> </div> + <div className={styles.pagination}> + {!!totalCount && ( + <Pagination + pageCount={Math.ceil(totalCount / pageSize)} + pageIndex={pageIndex} + onChange={(page) => { + setQuery({ page: String(page) }); + }} + /> + )} + </div> </Card> ); }; diff --git a/packages/console/src/pages/Users/index.tsx b/packages/console/src/pages/Users/index.tsx index 3355273dd..da19666f1 100644 --- a/packages/console/src/pages/Users/index.tsx +++ b/packages/console/src/pages/Users/index.tsx @@ -135,7 +135,7 @@ const Users = () => { </table> </div> <div className={styles.pagination}> - {totalCount !== undefined && totalCount > 0 && ( + {!!totalCount && ( <Pagination pageCount={Math.ceil(totalCount / pageSize)} pageIndex={pageIndex}