0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(console): support pagination on rbac-related pages (#2934)

This commit is contained in:
Xiao Yijun 2023-01-13 12:37:16 +08:00 committed by GitHub
parent 5211fa1e0c
commit ef795ac592
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 163 additions and 21 deletions

View file

@ -1,6 +1,7 @@
@use '@/scss/underscore' as _;
.permissionTable {
flex: 1;
margin-bottom: _.unit(6);
color: var(--color-text);

View file

@ -9,18 +9,27 @@ import { ApiResourceDetailsTabs } from '@/consts/page-tabs';
import Button from '../Button';
import IconButton from '../IconButton';
import type { Props as PaginationProps } from '../Pagination';
import Search from '../Search';
import Table from '../Table';
import type { Column } from '../Table/types';
import TextLink from '../TextLink';
import * as styles from './index.module.scss';
type SearchProps = {
keyword: string;
searchHandler: (value: string) => void;
clearSearchHandler: () => void;
};
type Props = {
scopes?: ScopeResponse[];
isLoading: boolean;
errorMessage?: string;
createButtonTitle: AdminConsoleKey;
isApiColumnVisible?: boolean;
pagination?: PaginationProps;
search: SearchProps;
createHandler: () => void;
deleteHandler: (ScopeResponse: ScopeResponse) => void;
retryHandler: () => void;
@ -32,6 +41,8 @@ const PermissionsTable = ({
errorMessage,
createButtonTitle,
isApiColumnVisible = false,
pagination,
search: { keyword, searchHandler, clearSearchHandler },
createHandler,
deleteHandler,
retryHandler,
@ -97,7 +108,12 @@ const PermissionsTable = ({
columns={columns}
filter={
<div className={styles.filter}>
<Search />
<Search
defaultValue={keyword}
isClearable={Boolean(keyword)}
onSearch={searchHandler}
onClearSearch={clearSearchHandler}
/>
<Button
title={createButtonTitle}
type="primary"
@ -110,6 +126,7 @@ const PermissionsTable = ({
</div>
}
isLoading={isLoading}
pagination={pagination}
placeholder={{
content: (
<Button

View file

@ -1,4 +1,5 @@
import type { Scope, ScopeResponse } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -9,6 +10,8 @@ import ConfirmModal from '@/components/ConfirmModal';
import PermissionsTable from '@/components/PermissionsTable';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useTableSearchParams, { formatKeyword } from '@/hooks/use-table-search-params';
import { buildUrl } from '@/utilities/url';
import type { ApiResourceDetailsOutletContext } from '../types';
import CreatePermissionModal from './components/CreatePermissionModal';
@ -21,12 +24,21 @@ const ApiResourcePermissions = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
data: scopes,
error,
mutate,
} = useSWR<ScopeResponse[], RequestError>(resourceId && `/api/resources/${resourceId}/scopes`);
pagination: { pageIndex, pageSize, setPageIndex },
search: { keyword, setKeyword },
} = useTableSearchParams();
const isLoading = !scopes && !error;
const { data, error, mutate } = useSWR<[ScopeResponse[], number], RequestError>(
resourceId &&
buildUrl(`/api/resources/${resourceId}/scopes`, {
page: String(pageIndex),
page_size: String(pageSize),
...conditional(keyword && { search: formatKeyword(keyword) }),
})
);
const isLoading = !data && !error;
const [scopes, totalCount] = data ?? [];
const api = useApi();
@ -62,6 +74,23 @@ const ApiResourcePermissions = () => {
deleteHandler={setScopeToBeDeleted}
errorMessage={error?.body?.message ?? error?.message}
retryHandler={async () => mutate(undefined, true)}
pagination={{
pageIndex,
pageSize,
totalCount,
onChange: setPageIndex,
}}
search={{
keyword,
searchHandler: (value) => {
setKeyword(value);
setPageIndex(1);
},
clearSearchHandler: () => {
setKeyword('');
setPageIndex(1);
},
}}
/>
{isCreateFormOpen && (
<CreatePermissionModal

View file

@ -1,4 +1,5 @@
import type { Scope, ScopeResponse } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -9,6 +10,8 @@ import ConfirmModal from '@/components/ConfirmModal';
import PermissionsTable from '@/components/PermissionsTable';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useTableSearchParams, { formatKeyword } from '@/hooks/use-table-search-params';
import { buildUrl } from '@/utilities/url';
import type { RoleDetailsOutletContext } from '../types';
import AssignPermissionsModal from './components/AssignPermissionsModal';
@ -21,12 +24,22 @@ const RolePermissions = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
data: scopes,
error,
mutate,
} = useSWR<ScopeResponse[], RequestError>(roleId && `/api/roles/${roleId}/scopes`);
pagination: { pageIndex, pageSize, setPageIndex },
search: { keyword, setKeyword },
} = useTableSearchParams();
const isLoading = !scopes && !error;
const { data, error, mutate } = useSWR<[ScopeResponse[], number], RequestError>(
roleId &&
buildUrl(`/api/roles/${roleId}/scopes`, {
page: String(pageIndex),
page_size: String(pageSize),
...conditional(keyword && { search: formatKeyword(keyword) }),
})
);
const isLoading = !data && !error;
const [scopes, totalCount] = data ?? [];
const [isAssignPermissionsModalOpen, setIsAssignPermissionsModalOpen] = useState(false);
const [scopeToBeDeleted, setScopeToBeDeleted] = useState<Scope>();
@ -65,6 +78,23 @@ const RolePermissions = () => {
deleteHandler={setScopeToBeDeleted}
errorMessage={error?.body?.message ?? error?.message}
retryHandler={async () => mutate(undefined, true)}
pagination={{
pageIndex,
pageSize,
totalCount,
onChange: setPageIndex,
}}
search={{
keyword,
searchHandler: (value) => {
setKeyword(value);
setPageIndex(1);
},
clearSearchHandler: () => {
setKeyword('');
setPageIndex(1);
},
}}
/>
{scopeToBeDeleted && (
<ConfirmModal

View file

@ -1,6 +1,7 @@
@use '@/scss/underscore' as _;
.usersTable {
flex: 1;
margin-bottom: _.unit(6);
color: var(--color-text);

View file

@ -1,4 +1,5 @@
import type { User } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -18,6 +19,8 @@ import Table from '@/components/Table';
import UserAvatar from '@/components/UserAvatar';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useTableSearchParams, { formatKeyword } from '@/hooks/use-table-search-params';
import { buildUrl } from '@/utilities/url';
import type { RoleDetailsOutletContext } from '../types';
import AssignUsersModal from './components/AssignUsersModal';
@ -31,12 +34,22 @@ const RoleUsers = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
data: users,
error,
mutate,
} = useSWR<User[], RequestError>(roleId && `/api/roles/${roleId}/users`);
pagination: { pageIndex, pageSize, setPageIndex },
search: { keyword, setKeyword },
} = useTableSearchParams();
const isLoading = !users && !error;
const { data, error, mutate } = useSWR<[User[], number], RequestError>(
roleId &&
buildUrl(`/api/roles/${roleId}/users`, {
page: String(pageIndex),
page_size: String(pageSize),
...conditional(keyword && { search: formatKeyword(keyword) }),
})
);
const isLoading = !data && !error;
const [users, totalCount] = data ?? [];
const [isAssignModalOpen, setIsAssignModalOpen] = useState(false);
const [userToBeDeleted, setUserToBeDeleted] = useState<User>();
@ -84,7 +97,7 @@ const RoleUsers = () => {
},
{
title: t('role_details.users.app_column'),
dataIndex: 'name',
dataIndex: 'app',
colSpan: 5,
render: ({ applicationId }) =>
applicationId ? <ApplicationName applicationId={applicationId} /> : '-',
@ -112,7 +125,18 @@ const RoleUsers = () => {
]}
filter={
<div className={styles.filter}>
<Search />
<Search
defaultValue={keyword}
isClearable={Boolean(keyword)}
onSearch={(value) => {
setKeyword(value);
setPageIndex(1);
}}
onClearSearch={() => {
setKeyword('');
setPageIndex(1);
}}
/>
<Button
title="role_details.users.assign_button"
type="primary"
@ -124,6 +148,12 @@ const RoleUsers = () => {
/>
</div>
}
pagination={{
pageIndex,
pageSize,
totalCount,
onChange: setPageIndex,
}}
placeholder={{
content: (
<Button

View file

@ -1,6 +1,7 @@
@use '@/scss/underscore' as _;
.rolesTable {
flex: 1;
margin-bottom: _.unit(6);
color: var(--color-text);

View file

@ -1,4 +1,5 @@
import type { Role } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -15,6 +16,8 @@ import Table from '@/components/Table';
import TextLink from '@/components/TextLink';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useTableSearchParams, { formatKeyword } from '@/hooks/use-table-search-params';
import { buildUrl } from '@/utilities/url';
import type { UserDetailsOutletContext } from '../types';
import AssignRolesModal from './components/AssignRolesModal';
@ -26,9 +29,22 @@ const UserRoles = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { data: roles, error, mutate } = useSWR<Role[], RequestError>(`/api/users/${userId}/roles`);
const {
pagination: { pageIndex, pageSize, setPageIndex },
search: { keyword, setKeyword },
} = useTableSearchParams();
const isLoading = !roles && !error;
const { data, error, mutate } = useSWR<[Role[], number], RequestError>(
buildUrl(`/api/users/${userId}/roles`, {
page: String(pageIndex),
page_size: String(pageSize),
...conditional(keyword && { search: formatKeyword(keyword) }),
})
);
const isLoading = !data && !error;
const [roles, totalCount] = data ?? [];
const [isAssignRolesModalOpen, setIsAssignRolesModalOpen] = useState(false);
const [roleToBeDeleted, setRoleToBeDeleted] = useState<Role>();
@ -93,7 +109,18 @@ const UserRoles = () => {
]}
filter={
<div className={styles.filter}>
<Search />
<Search
defaultValue={keyword}
isClearable={Boolean(keyword)}
onSearch={(value) => {
setKeyword(value);
setPageIndex(1);
}}
onClearSearch={() => {
setKeyword('');
setPageIndex(1);
}}
/>
<Button
title="user_details.roles.assign_button"
type="primary"
@ -105,6 +132,12 @@ const UserRoles = () => {
/>
</div>
}
pagination={{
pageIndex,
pageSize,
totalCount,
onChange: setPageIndex,
}}
placeholder={{
content: (
<Button