0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

refactor(console): add routes for create modals (#2690)

This commit is contained in:
Xiao Yijun 2022-12-21 13:16:48 +08:00 committed by GitHub
parent 1865b74272
commit b431b0bc5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 43 deletions

View file

@ -53,13 +53,11 @@ const Main = () => {
<Route path="applications"> <Route path="applications">
<Route index element={<Applications />} /> <Route index element={<Applications />} />
<Route path="create" element={<Applications />} /> <Route path="create" element={<Applications />} />
<Route path=":id"> <Route path=":id" element={<ApplicationDetails />} />
<Route index element={<Navigate replace to="settings" />} />
<Route path="settings" element={<ApplicationDetails />} />
</Route>
</Route> </Route>
<Route path="api-resources"> <Route path="api-resources">
<Route index element={<ApiResources />} /> <Route index element={<ApiResources />} />
<Route path="create" element={<ApiResources />} />
<Route path=":id" element={<ApiResourceDetails />} /> <Route path=":id" element={<ApiResourceDetails />} />
</Route> </Route>
<Route path="connectors"> <Route path="connectors">
@ -69,6 +67,7 @@ const Main = () => {
</Route> </Route>
<Route path="users"> <Route path="users">
<Route index element={<Users />} /> <Route index element={<Users />} />
<Route path="create" element={<Users />} />
<Route path=":userId" element={<UserDetails />} /> <Route path=":userId" element={<UserDetails />} />
<Route path=":userId/logs" element={<UserDetails />} /> <Route path=":userId/logs" element={<UserDetails />} />
<Route path=":userId/logs/:logId" element={<AuditLogDetails />} /> <Route path=":userId/logs/:logId" element={<AuditLogDetails />} />

View file

@ -1,11 +1,10 @@
import type { Resource } from '@logto/schemas'; import type { Resource } from '@logto/schemas';
import { AppearanceMode } from '@logto/schemas'; import { AppearanceMode } from '@logto/schemas';
import classNames from 'classnames'; import classNames from 'classnames';
import { useState } from 'react';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Modal from 'react-modal'; import Modal from 'react-modal';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import useSWR from 'swr'; import useSWR from 'swr';
import ApiResourceDark from '@/assets/images/api-resource-dark.svg'; import ApiResourceDark from '@/assets/images/api-resource-dark.svg';
@ -28,14 +27,18 @@ import * as tableStyles from '@/scss/table.module.scss';
import CreateForm from './components/CreateForm'; import CreateForm from './components/CreateForm';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
const buildDetailsLink = (id: string) => `/api-resources/${id}`; const apiResourcesPathname = '/api-resources';
const createApiResourcePathname = `${apiResourcesPathname}/create`;
const buildDetailsPathname = (id: string) => `${apiResourcesPathname}/${id}`;
const pageSize = 20; const pageSize = 20;
const ApiResources = () => { const ApiResources = () => {
const [isCreateFormOpen, setIsCreateFormOpen] = useState(false); const { pathname } = useLocation();
const isCreateNew = pathname.endsWith('/create');
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [query, setQuery] = useSearchParams(); const [query, setQuery] = useSearchParams();
const search = query.toString();
const pageIndex = Number(query.get('page') ?? '1'); const pageIndex = Number(query.get('page') ?? '1');
const { data, error, mutate } = useSWR<[Resource[], number], RequestError>( const { data, error, mutate } = useSWR<[Resource[], number], RequestError>(
`/api/resources?page=${pageIndex}&page_size=${pageSize}` `/api/resources?page=${pageIndex}&page_size=${pageSize}`
@ -55,28 +58,38 @@ const ApiResources = () => {
size="large" size="large"
icon={<Plus />} icon={<Plus />}
onClick={() => { onClick={() => {
setIsCreateFormOpen(true); navigate({
pathname: createApiResourcePathname,
search,
});
}} }}
/> />
<Modal <Modal
shouldCloseOnEsc shouldCloseOnEsc
isOpen={isCreateFormOpen} isOpen={isCreateNew}
className={modalStyles.content} className={modalStyles.content}
overlayClassName={modalStyles.overlay} overlayClassName={modalStyles.overlay}
onRequestClose={() => { onRequestClose={() => {
setIsCreateFormOpen(false); navigate({
pathname: apiResourcesPathname,
search,
});
}} }}
> >
<CreateForm <CreateForm
onClose={(createdApiResource) => { onClose={(createdApiResource) => {
setIsCreateFormOpen(false);
if (createdApiResource) { if (createdApiResource) {
toast.success( toast.success(
t('api_resources.api_resource_created', { name: createdApiResource.name }) t('api_resources.api_resource_created', { name: createdApiResource.name })
); );
navigate(buildDetailsLink(createdApiResource.id)); navigate(buildDetailsPathname(createdApiResource.id), { replace: true });
return;
} }
navigate({
pathname: apiResourcesPathname,
search,
});
}} }}
/> />
</Modal> </Modal>
@ -109,7 +122,10 @@ const ApiResources = () => {
title="api_resources.create" title="api_resources.create"
type="outline" type="outline"
onClick={() => { onClick={() => {
setIsCreateFormOpen(true); navigate({
pathname: createApiResourcePathname,
search,
});
}} }}
/> />
</TableEmpty> </TableEmpty>
@ -123,14 +139,14 @@ const ApiResources = () => {
key={id} key={id}
className={tableStyles.clickable} className={tableStyles.clickable}
onClick={() => { onClick={() => {
navigate(buildDetailsLink(id)); navigate(buildDetailsPathname(id));
}} }}
> >
<td> <td>
<ItemPreview <ItemPreview
title={name} title={name}
icon={<ResourceIcon className={styles.icon} />} icon={<ResourceIcon className={styles.icon} />}
to={buildDetailsLink(id)} to={buildDetailsPathname(id)}
/> />
</td> </td>
<td> <td>

View file

@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import useSWR from 'swr'; import useSWR from 'swr';
import Back from '@/assets/images/back.svg'; import Back from '@/assets/images/back.svg';
@ -41,7 +41,6 @@ const mapToUriOriginFormatArrays = (value?: string[]) =>
const ApplicationDetails = () => { const ApplicationDetails = () => {
const { id } = useParams(); const { id } = useParams();
const { pathname } = useLocation();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { data, error, mutate } = useSWR<Application, RequestError>( const { data, error, mutate } = useSWR<Application, RequestError>(
id && `/api/applications/${id}` id && `/api/applications/${id}`
@ -197,9 +196,7 @@ const ApplicationDetails = () => {
</div> </div>
</Card> </Card>
<TabNav> <TabNav>
<TabNavItem href={`/applications/${data.id}/settings`}> <TabNavItem href={`/applications/${data.id}`}>{t('general.settings_nav')}</TabNavItem>
{t('general.settings_nav')}
</TabNavItem>
</TabNav> </TabNav>
<FormProvider {...formMethods}> <FormProvider {...formMethods}>
<DetailsForm <DetailsForm

View file

@ -27,12 +27,17 @@ import * as styles from './index.module.scss';
const pageSize = 20; const pageSize = 20;
const applicationsPathname = '/applications';
const createApplicationPathname = `${applicationsPathname}/create`;
const buildDetailsPathname = (id: string) => `${applicationsPathname}/${id}`;
const Applications = () => { const Applications = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const { pathname } = useLocation();
const isCreateNew = location.pathname.endsWith('/create'); const isCreateNew = pathname === createApplicationPathname;
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [query, setQuery] = useSearchParams(); const [query, setQuery] = useSearchParams();
const search = query.toString();
const pageIndex = Number(query.get('page') ?? '1'); const pageIndex = Number(query.get('page') ?? '1');
const { data, error, mutate } = useSWR<[Application[], number], RequestError>( const { data, error, mutate } = useSWR<[Application[], number], RequestError>(
`/api/applications?page=${pageIndex}&page_size=${pageSize}` `/api/applications?page=${pageIndex}&page_size=${pageSize}`
@ -50,7 +55,10 @@ const Applications = () => {
type="primary" type="primary"
size="large" size="large"
onClick={() => { onClick={() => {
navigate('/applications/create'); navigate({
pathname: createApplicationPathname,
search,
});
}} }}
/> />
<Modal <Modal
@ -59,18 +67,24 @@ const Applications = () => {
className={modalStyles.content} className={modalStyles.content}
overlayClassName={modalStyles.overlay} overlayClassName={modalStyles.overlay}
onRequestClose={() => { onRequestClose={() => {
navigate('/applications'); navigate({
pathname: applicationsPathname,
search,
});
}} }}
> >
<CreateForm <CreateForm
onClose={(createdApp) => { onClose={(createdApp) => {
if (createdApp) { if (createdApp) {
toast.success(t('applications.application_created', { name: createdApp.name })); toast.success(t('applications.application_created', { name: createdApp.name }));
navigate(`/applications/${createdApp.id}`); navigate(buildDetailsPathname(createdApp.id), { replace: true });
return; return;
} }
navigate('/applications'); navigate({
pathname: applicationsPathname,
search,
});
}} }}
/> />
</Modal> </Modal>
@ -105,7 +119,10 @@ const Applications = () => {
title="applications.create" title="applications.create"
type="outline" type="outline"
onClick={() => { onClick={() => {
navigate('/applications/create'); navigate({
pathname: createApplicationPathname,
search,
});
}} }}
/> />
</TableEmpty> </TableEmpty>
@ -115,7 +132,7 @@ const Applications = () => {
key={id} key={id}
className={tableStyles.clickable} className={tableStyles.clickable}
onClick={() => { onClick={() => {
navigate(`/applications/${id}`); navigate(buildDetailsPathname(id));
}} }}
> >
<td> <td>
@ -123,7 +140,7 @@ const Applications = () => {
title={name} title={name}
subtitle={t(`${applicationTypeI18nKey[type]}.title`)} subtitle={t(`${applicationTypeI18nKey[type]}.title`)}
icon={<ApplicationIcon className={styles.icon} type={type} />} icon={<ApplicationIcon className={styles.icon} type={type} />}
to={`/applications/${id}`} to={buildDetailsPathname(id)}
/> />
</td> </td>
<td> <td>

View file

@ -1,10 +1,9 @@
import type { User } from '@logto/schemas'; import type { User } from '@logto/schemas';
import { conditional, conditionalString } from '@silverhand/essentials'; import { conditional, conditionalString } from '@silverhand/essentials';
import classNames from 'classnames'; import classNames from 'classnames';
import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Modal from 'react-modal'; import Modal from 'react-modal';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import useSWR from 'swr'; import useSWR from 'swr';
import Plus from '@/assets/images/plus.svg'; import Plus from '@/assets/images/plus.svg';
@ -32,10 +31,16 @@ const pageSize = 20;
const userTableColumn = 3; const userTableColumn = 3;
const usersPathname = '/users';
const createUserPathname = `${usersPathname}/create`;
const buildDetailsPathname = (id: string) => `${usersPathname}/id`;
const Users = () => { const Users = () => {
const [isCreateFormOpen, setIsCreateFormOpen] = useState(false); const { pathname } = useLocation();
const isCreateNew = pathname === createUserPathname;
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [query, setQuery] = useSearchParams(); const [query, setQuery] = useSearchParams();
const search = query.toString();
const pageIndex = Number(query.get('page') ?? '1'); const pageIndex = Number(query.get('page') ?? '1');
const keyword = query.get('search') ?? ''; const keyword = query.get('search') ?? '';
const { data, error, mutate } = useSWR<[User[], number], RequestError>( const { data, error, mutate } = useSWR<[User[], number], RequestError>(
@ -57,26 +62,37 @@ const Users = () => {
type="primary" type="primary"
icon={<Plus />} icon={<Plus />}
onClick={() => { onClick={() => {
setIsCreateFormOpen(true); navigate({
pathname: createUserPathname,
search,
});
}} }}
/> />
<Modal <Modal
shouldCloseOnEsc shouldCloseOnEsc
isOpen={isCreateFormOpen} isOpen={isCreateNew}
className={modalStyles.content} className={modalStyles.content}
overlayClassName={modalStyles.overlay} overlayClassName={modalStyles.overlay}
onRequestClose={() => { onRequestClose={() => {
setIsCreateFormOpen(false); navigate({
pathname: usersPathname,
search,
});
}} }}
> >
<CreateForm <CreateForm
onClose={(createdUser, password) => { onClose={(createdUser, password) => {
setIsCreateFormOpen(false);
if (createdUser && password) { if (createdUser && password) {
sessionStorage.setItem(generatedPasswordStorageKey, password); sessionStorage.setItem(generatedPasswordStorageKey, password);
navigate(`/users/${createdUser.id}`); navigate(buildDetailsPathname(createdUser.id), { replace: true });
return;
} }
navigate({
pathname: usersPathname,
search,
});
}} }}
/> />
</Modal> </Modal>
@ -124,7 +140,10 @@ const Users = () => {
title="users.create" title="users.create"
type="outline" type="outline"
onClick={() => { onClick={() => {
setIsCreateFormOpen(true); navigate({
pathname: createUserPathname,
search,
});
}} }}
/> />
</TableEmpty> </TableEmpty>
@ -134,7 +153,7 @@ const Users = () => {
key={id} key={id}
className={tableStyles.clickable} className={tableStyles.clickable}
onClick={() => { onClick={() => {
navigate(`/users/${id}`); navigate(buildDetailsPathname(id));
}} }}
> >
<td> <td>
@ -142,7 +161,7 @@ const Users = () => {
title={name ?? t('users.unnamed')} title={name ?? t('users.unnamed')}
subtitle={id} subtitle={id}
icon={<UserAvatar className={styles.avatar} url={avatar} />} icon={<UserAvatar className={styles.avatar} url={avatar} />}
to={`/users/${id}`} to={buildDetailsPathname(id)}
size="compact" size="compact"
/> />
</td> </td>