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:
parent
1865b74272
commit
b431b0bc5e
5 changed files with 91 additions and 43 deletions
|
@ -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 />} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue