mirror of
https://github.com/logto-io/logto.git
synced 2025-04-14 23:11:31 -05:00
refactor(console): reorg resource details page (#5634)
This commit is contained in:
parent
11c974cfdd
commit
cb4ef9fd0e
26 changed files with 68 additions and 83 deletions
|
@ -126,7 +126,7 @@ function PermissionsTable({
|
|||
render: ({ resource }) => (
|
||||
<TextLink
|
||||
className={styles.link}
|
||||
to={`/api-resources/${resource.id}/${ApiResourceDetailsTabs.Settings}`}
|
||||
to={`/api-resources/${resource.id}/${ApiResourceDetailsTabs.General}`}
|
||||
>
|
||||
{resource.name}
|
||||
</TextLink>
|
||||
|
|
|
@ -7,8 +7,8 @@ export enum ApplicationDetailsTabs {
|
|||
}
|
||||
|
||||
export enum ApiResourceDetailsTabs {
|
||||
Settings = 'settings',
|
||||
Permissions = 'permissions',
|
||||
General = 'general',
|
||||
}
|
||||
|
||||
export enum ConnectorsTabs {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { useContext } from 'react';
|
|||
import { Navigate, Route, Routes, useOutletContext } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
ApiResourceDetailsTabs,
|
||||
ApplicationDetailsTabs,
|
||||
ConnectorsTabs,
|
||||
EnterpriseSsoDetailsTabs,
|
||||
|
@ -17,8 +16,6 @@ import { TenantsContext } from '@/contexts/TenantsProvider';
|
|||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
import useCurrentTenantScopes from '@/hooks/use-current-tenant-scopes';
|
||||
import ApiResourceDetails from '@/pages/ApiResourceDetails';
|
||||
import ApiResourcePermissions from '@/pages/ApiResourceDetails/ApiResourcePermissions';
|
||||
import ApiResourceSettings from '@/pages/ApiResourceDetails/ApiResourceSettings';
|
||||
import ApiResources from '@/pages/ApiResources';
|
||||
import ApplicationDetails from '@/pages/ApplicationDetails';
|
||||
import Applications from '@/pages/Applications';
|
||||
|
@ -112,14 +109,7 @@ function ConsoleContent() {
|
|||
<Route index element={<ApiResources />} />
|
||||
<Route path="create" element={<ApiResources />} />
|
||||
<Route path=":id/guide/:guideId" element={<ApiResourceDetails />} />
|
||||
<Route path=":id" element={<ApiResourceDetails />}>
|
||||
<Route index element={<Navigate replace to={ApiResourceDetailsTabs.Settings} />} />
|
||||
<Route path={ApiResourceDetailsTabs.Settings} element={<ApiResourceSettings />} />
|
||||
<Route
|
||||
path={ApiResourceDetailsTabs.Permissions}
|
||||
element={<ApiResourcePermissions />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path=":id/*" element={<ApiResourceDetails />} />
|
||||
</Route>
|
||||
<Route path="sign-in-experience">
|
||||
<Route index element={<Navigate replace to={SignInExperienceTab.Branding} />} />
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import type { ScopeResponse } from '@logto/schemas';
|
||||
import { isManagementApi, type Resource, type ScopeResponse } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import PermissionsTable from '@/components/PermissionsTable';
|
||||
|
@ -13,17 +12,17 @@ import useApi from '@/hooks/use-api';
|
|||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import { buildUrl, formatSearchKeyword } from '@/utils/url';
|
||||
|
||||
import type { ApiResourceDetailsOutletContext } from '../types';
|
||||
|
||||
import CreatePermissionModal from './components/CreatePermissionModal';
|
||||
|
||||
const pageSize = defaultPageSize;
|
||||
|
||||
function ApiResourcePermissions() {
|
||||
const {
|
||||
resource: { id: resourceId },
|
||||
isLogtoManagementApiResource,
|
||||
} = useOutletContext<ApiResourceDetailsOutletContext>();
|
||||
type Props = {
|
||||
resource: Resource;
|
||||
};
|
||||
|
||||
function ApiResourcePermissions({ resource }: Props) {
|
||||
const { id: resourceId, indicator } = resource;
|
||||
const isLogtoManagementApiResource = isManagementApi(indicator);
|
||||
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import type { Resource } from '@logto/schemas';
|
||||
import { isManagementApi, type Resource } from '@logto/schemas';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
|
||||
import DetailsForm from '@/components/DetailsForm';
|
||||
import FormCard from '@/components/FormCard';
|
||||
|
@ -15,11 +14,14 @@ import useApi from '@/hooks/use-api';
|
|||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
import type { ApiResourceDetailsOutletContext } from '../types';
|
||||
type Props = {
|
||||
resource: Resource;
|
||||
isDeleting: boolean;
|
||||
onResourceUpdated: (updatedData: Resource) => void;
|
||||
};
|
||||
|
||||
function ApiResourceSettings() {
|
||||
const { resource, isDeleting, isLogtoManagementApiResource, onResourceUpdated } =
|
||||
useOutletContext<ApiResourceDetailsOutletContext>();
|
||||
function ApiResourceSettings({ resource, isDeleting, onResourceUpdated }: Props) {
|
||||
const isLogtoManagementApiResource = isManagementApi(resource.indicator);
|
||||
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { getDocumentationUrl } = useDocumentationUrl();
|
||||
|
|
|
@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Outlet, useLocation, useParams } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ApiResourceDark from '@/assets/icons/api-resource-dark.svg';
|
||||
|
@ -28,11 +28,12 @@ import useDocumentationUrl from '@/hooks/use-documentation-url';
|
|||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
|
||||
import ApiResourcePermissions from './ApiResourcePermissions';
|
||||
import ApiResourceSettings from './ApiResourceSettings';
|
||||
import GuideDrawer from './components/GuideDrawer';
|
||||
import GuideModal from './components/GuideModal';
|
||||
import ManagementApiNotice from './components/ManagementApiNotice';
|
||||
import * as styles from './index.module.scss';
|
||||
import { type ApiResourceDetailsOutletContext } from './types';
|
||||
|
||||
const icons = {
|
||||
[Theme.Light]: { ApiIcon: ApiResource, ManagementApiIcon: ManagementApiResource },
|
||||
|
@ -170,25 +171,32 @@ function ApiResourceDetails() {
|
|||
</DeleteConfirmModal>
|
||||
)}
|
||||
<TabNav>
|
||||
<TabNavItem href={`/api-resources/${data.id}/${ApiResourceDetailsTabs.Settings}`}>
|
||||
{t('api_resource_details.settings_tab')}
|
||||
</TabNavItem>
|
||||
<TabNavItem href={`/api-resources/${data.id}/${ApiResourceDetailsTabs.Permissions}`}>
|
||||
{t('api_resource_details.permissions_tab')}
|
||||
</TabNavItem>
|
||||
<TabNavItem href={`/api-resources/${data.id}/${ApiResourceDetailsTabs.General}`}>
|
||||
{t('api_resource_details.general_tab')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<Outlet
|
||||
context={
|
||||
{
|
||||
resource: data,
|
||||
isDeleting,
|
||||
isLogtoManagementApiResource,
|
||||
onResourceUpdated: (resource: Resource) => {
|
||||
void mutate(resource);
|
||||
},
|
||||
} satisfies ApiResourceDetailsOutletContext
|
||||
}
|
||||
/>
|
||||
<Routes>
|
||||
<Route index element={<Navigate replace to={ApiResourceDetailsTabs.Permissions} />} />
|
||||
<Route
|
||||
path={ApiResourceDetailsTabs.Permissions}
|
||||
element={<ApiResourcePermissions resource={data} />}
|
||||
/>
|
||||
<Route
|
||||
path={ApiResourceDetailsTabs.General}
|
||||
element={
|
||||
<ApiResourceSettings
|
||||
resource={data}
|
||||
isDeleting={isDeleting}
|
||||
onResourceUpdated={(updatedData: Resource) => {
|
||||
void mutate(updatedData);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</>
|
||||
)}
|
||||
</DetailsPage>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import type { Resource } from '@logto/schemas';
|
||||
|
||||
export type ApiResourceDetailsOutletContext = {
|
||||
resource: Resource;
|
||||
isDeleting: boolean;
|
||||
isLogtoManagementApiResource: boolean;
|
||||
onResourceUpdated: (resource: Resource) => void;
|
||||
};
|
|
@ -32,7 +32,7 @@ const pageSize = defaultPageSize;
|
|||
const apiResourcesPathname = '/api-resources';
|
||||
const createApiResourcePathname = `${apiResourcesPathname}/create`;
|
||||
const buildDetailsPathname = (id: string) =>
|
||||
`${apiResourcesPathname}/${id}/${ApiResourceDetailsTabs.Settings}`;
|
||||
`${apiResourcesPathname}/${id}/${ApiResourceDetailsTabs.Permissions}`;
|
||||
|
||||
const icons = {
|
||||
[Theme.Light]: { ApiIcon: ApiResource, ManagementApiIcon: ManagementApiResource },
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
expectConfirmModalAndAct,
|
||||
expectModalWithTitle,
|
||||
expectToClickModalAction,
|
||||
expectToClickNavTab,
|
||||
goToAdminConsole,
|
||||
waitForToast,
|
||||
} from '#src/ui-helpers/index.js';
|
||||
|
@ -91,9 +92,7 @@ describe('M2M RBAC', () => {
|
|||
});
|
||||
|
||||
it('create permission for api resource', async () => {
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
text: 'Permissions',
|
||||
});
|
||||
await expectToClickNavTab(page, 'Permissions');
|
||||
|
||||
await expect(page).toClick('div[class$=filter] button[class$=createButton] span', {
|
||||
text: 'Create permission',
|
||||
|
@ -190,9 +189,7 @@ describe('M2M RBAC', () => {
|
|||
});
|
||||
|
||||
it('delete a permission from a role on the role details page', async () => {
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
text: 'Permissions',
|
||||
});
|
||||
await expectToClickNavTab(page, 'Permissions');
|
||||
|
||||
await expectToSelectPermissionAction(page, {
|
||||
permissionName,
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
expectModalWithTitle,
|
||||
expectToClickDetailsPageOption,
|
||||
expectToClickModalAction,
|
||||
expectToClickNavTab,
|
||||
goToAdminConsole,
|
||||
waitForToast,
|
||||
} from '#src/ui-helpers/index.js';
|
||||
|
@ -80,7 +81,7 @@ describe('User RBAC', () => {
|
|||
});
|
||||
|
||||
it('create api permissions', async () => {
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
await expect(page).toClick('nav div[class$=item] div[class*=link] a', {
|
||||
text: 'Permissions',
|
||||
});
|
||||
|
||||
|
@ -194,9 +195,7 @@ describe('User RBAC', () => {
|
|||
});
|
||||
|
||||
it('delete a permission from a role on the role details page', async () => {
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
text: 'Permissions',
|
||||
});
|
||||
await expectToClickNavTab(page, 'Permissions');
|
||||
|
||||
await expectToSelectPermissionAction(page, {
|
||||
permissionName,
|
||||
|
@ -390,9 +389,7 @@ describe('User RBAC', () => {
|
|||
});
|
||||
|
||||
// Navigate to permissions tab
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
text: 'Permissions',
|
||||
});
|
||||
await expectToClickNavTab(page, 'Permissions');
|
||||
|
||||
await expectToSelectPermissionAction(page, {
|
||||
permissionName,
|
||||
|
|
|
@ -115,7 +115,7 @@ export const expectConfirmModalAndAct = async (
|
|||
};
|
||||
|
||||
export const expectToClickNavTab = async (page: Page, tab: string) => {
|
||||
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
await expect(page).toClick('nav div[class$=item] div[class*=link] a', {
|
||||
text: tab,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API Ressourcendetails',
|
||||
back_to_api_resources: 'Zurück zu API Ressourcen',
|
||||
settings_tab: 'Einstellungen',
|
||||
general_tab: 'Allgemein',
|
||||
permissions_tab: 'Berechtigungen',
|
||||
settings: 'Einstellungen',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API resource details',
|
||||
back_to_api_resources: 'Back to API resources',
|
||||
settings_tab: 'Settings',
|
||||
general_tab: 'General',
|
||||
permissions_tab: 'Permissions',
|
||||
settings: 'Settings',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Detalles del recurso de la API',
|
||||
back_to_api_resources: 'Volver a los recursos de la API',
|
||||
settings_tab: 'Configuración',
|
||||
general_tab: 'General',
|
||||
permissions_tab: 'Permisos',
|
||||
settings: 'Configuración',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Détails de la ressource API',
|
||||
back_to_api_resources: 'Retour aux ressources API',
|
||||
settings_tab: 'Paramètres',
|
||||
general_tab: 'Général',
|
||||
permissions_tab: 'Autorisations',
|
||||
settings: 'Paramètres',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Dettagli delle risorse API',
|
||||
back_to_api_resources: 'Torna alle risorse API',
|
||||
settings_tab: 'Impostazioni',
|
||||
general_tab: 'Generale',
|
||||
permissions_tab: 'Autorizzazioni',
|
||||
settings: 'Impostazioni',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'APIリソースの詳細 ',
|
||||
back_to_api_resources: 'APIリソースに戻る',
|
||||
settings_tab: '設定',
|
||||
general_tab: '一般',
|
||||
permissions_tab: '権限',
|
||||
settings: '設定',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API 리소스 세부 정보',
|
||||
back_to_api_resources: 'API 리소스로 돌아가기',
|
||||
settings_tab: '설정',
|
||||
general_tab: '일반',
|
||||
permissions_tab: '권한',
|
||||
settings: '설정',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Szczegóły zasobów API',
|
||||
back_to_api_resources: 'Powróć do zasobów API',
|
||||
settings_tab: 'Ustawienia',
|
||||
general_tab: 'Ogólne',
|
||||
permissions_tab: 'Uprawnienia',
|
||||
settings: 'Ustawienia',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Detalhes do Recurso da API',
|
||||
back_to_api_resources: 'Voltar para os recursos da API',
|
||||
settings_tab: 'Configurações',
|
||||
general_tab: 'Geral',
|
||||
permissions_tab: 'Permissões',
|
||||
settings: 'Configurações',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Detalhes do recurso da API',
|
||||
back_to_api_resources: 'Voltar aos recursos da API',
|
||||
settings_tab: 'Configurações',
|
||||
general_tab: 'Geral',
|
||||
permissions_tab: 'Permissões',
|
||||
settings: 'Configurações',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'Детали ресурса API',
|
||||
back_to_api_resources: 'Вернуться к Ресурсам API',
|
||||
settings_tab: 'Настройки',
|
||||
general_tab: 'Общее',
|
||||
permissions_tab: 'Разрешения',
|
||||
settings: 'Настройки',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API Kaynak detayları',
|
||||
back_to_api_resources: 'API Kaynaklarına geri dön',
|
||||
settings_tab: 'Ayarlar',
|
||||
general_tab: 'Genel',
|
||||
permissions_tab: 'İzinler',
|
||||
settings: 'Ayarlar',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API 资源详情',
|
||||
back_to_api_resources: '返回 API 资源',
|
||||
settings_tab: '设置',
|
||||
general_tab: '常规',
|
||||
permissions_tab: '权限',
|
||||
settings: '设置',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API 資源詳情',
|
||||
back_to_api_resources: '返回 API 資源',
|
||||
settings_tab: '設置',
|
||||
general_tab: '常規',
|
||||
permissions_tab: '權限',
|
||||
settings: '設置',
|
||||
settings_description:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const api_resource_details = {
|
||||
page_title: 'API 資源詳情',
|
||||
back_to_api_resources: '返回 API 資源',
|
||||
settings_tab: '設定',
|
||||
general_tab: '常規',
|
||||
permissions_tab: '權限',
|
||||
settings: '設定',
|
||||
settings_description:
|
||||
|
|
Loading…
Add table
Reference in a new issue