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

View file

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

View file

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

View file

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

View file

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