mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console): settings tabs in application details page (#4357)
This commit is contained in:
parent
6e03b83658
commit
66c75cb0af
21 changed files with 79 additions and 40 deletions
|
@ -1,3 +1,8 @@
|
|||
export enum ApplicationDetailsTabs {
|
||||
Settings = 'settings',
|
||||
AdvancedSettings = 'advanced-settings',
|
||||
}
|
||||
|
||||
export enum ApiResourceDetailsTabs {
|
||||
Settings = 'settings',
|
||||
Permissions = 'permissions',
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
RoleDetailsTabs,
|
||||
WebhookDetailsTabs,
|
||||
TenantSettingsTabs,
|
||||
ApplicationDetailsTabs,
|
||||
} from '@/consts';
|
||||
import { isCloud } from '@/consts/env';
|
||||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
|
@ -16,6 +17,8 @@ import ApiResourcePermissions from '@/pages/ApiResourceDetails/ApiResourcePermis
|
|||
import ApiResourceSettings from '@/pages/ApiResourceDetails/ApiResourceSettings';
|
||||
import ApiResources from '@/pages/ApiResources';
|
||||
import ApplicationDetails from '@/pages/ApplicationDetails';
|
||||
import AdvancedSettings from '@/pages/ApplicationDetails/components/AdvancedSettings';
|
||||
import Settings from '@/pages/ApplicationDetails/components/Settings';
|
||||
import Applications from '@/pages/Applications';
|
||||
import AuditLogDetails from '@/pages/AuditLogDetails';
|
||||
import AuditLogs from '@/pages/AuditLogs';
|
||||
|
@ -71,7 +74,14 @@ function ConsoleContent() {
|
|||
<Route index element={<Applications />} />
|
||||
<Route path="create" element={<Applications />} />
|
||||
<Route path=":id/guide/:guideId" element={<ApplicationDetails />} />
|
||||
<Route path=":id" element={<ApplicationDetails />} />
|
||||
<Route path=":id" element={<ApplicationDetails />}>
|
||||
<Route index element={<Navigate replace to={ApplicationDetailsTabs.Settings} />} />
|
||||
<Route path={ApplicationDetailsTabs.Settings} element={<Settings />} />
|
||||
<Route
|
||||
path={ApplicationDetailsTabs.AdvancedSettings}
|
||||
element={<AdvancedSettings />}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="api-resources">
|
||||
<Route index element={<ApiResources />} />
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
type Application,
|
||||
type SnakeCaseOidcConfig,
|
||||
ApplicationType,
|
||||
customClientMetadataGuard,
|
||||
DomainStatus,
|
||||
|
@ -9,6 +8,7 @@ import { appendPath } from '@silverhand/essentials';
|
|||
import { useContext } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
|
||||
import FormCard from '@/components/FormCard';
|
||||
import { openIdProviderConfigPath } from '@/consts/oidc';
|
||||
|
@ -23,13 +23,13 @@ import useCustomDomain from '@/hooks/use-custom-domain';
|
|||
import { applyDomain } from '@/utils/domain';
|
||||
|
||||
import * as styles from '../index.module.scss';
|
||||
import { type ApplicationDetailsOutletContext } from '../types';
|
||||
|
||||
type Props = {
|
||||
applicationType: ApplicationType;
|
||||
oidcConfig: SnakeCaseOidcConfig;
|
||||
};
|
||||
|
||||
function AdvancedSettings({ applicationType, oidcConfig }: Props) {
|
||||
function AdvancedSettings() {
|
||||
const {
|
||||
app: { type: applicationType },
|
||||
oidcConfig,
|
||||
} = useOutletContext<ApplicationDetailsOutletContext>();
|
||||
const { tenantEndpoint } = useContext(AppDataContext);
|
||||
const {
|
||||
register,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import type { Application } from '@logto/schemas';
|
||||
import { ApplicationType, validateRedirectUrl } from '@logto/schemas';
|
||||
import { useContext } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
|
||||
import FormCard from '@/components/FormCard';
|
||||
import MultiTextInputField from '@/components/MultiTextInputField';
|
||||
import { AppDataContext } from '@/contexts/AppDataProvider';
|
||||
import CopyToClipboard from '@/ds-components/CopyToClipboard';
|
||||
import FormField from '@/ds-components/FormField';
|
||||
import type { MultiTextInputRule } from '@/ds-components/MultiTextInput/types';
|
||||
|
@ -17,12 +20,12 @@ import TextLink from '@/ds-components/TextLink';
|
|||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
|
||||
import * as styles from '../index.module.scss';
|
||||
import { type ApplicationDetailsOutletContext } from '../types';
|
||||
|
||||
type Props = {
|
||||
data: Application;
|
||||
};
|
||||
function Settings() {
|
||||
const { tenantEndpoint } = useContext(AppDataContext);
|
||||
const { app: data } = useOutletContext<ApplicationDetailsOutletContext>();
|
||||
|
||||
function Settings({ data }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { getDocumentationUrl } = useDocumentationUrl();
|
||||
const {
|
||||
|
@ -54,32 +57,21 @@ function Settings({ data }: Props) {
|
|||
placeholder={t('application_details.application_name_placeholder')}
|
||||
/>
|
||||
</FormField>
|
||||
{tenantEndpoint && (
|
||||
<FormField title="application_details.logto_endpoint">
|
||||
<CopyToClipboard
|
||||
value={tenantEndpoint.href}
|
||||
variant="border"
|
||||
className={styles.textField}
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
<FormField title="application_details.description">
|
||||
<TextInput
|
||||
{...register('description')}
|
||||
placeholder={t('application_details.description_placeholder')}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField
|
||||
title="application_details.application_id"
|
||||
tip={(closeTipHandler) => (
|
||||
<Trans
|
||||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
href="https://openid.net/specs/openid-connect-core-1_0.html"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{t('application_details.application_id_tip')}
|
||||
</Trans>
|
||||
)}
|
||||
>
|
||||
<CopyToClipboard value={id} variant="border" className={styles.textField} />
|
||||
</FormField>
|
||||
{[ApplicationType.Traditional, ApplicationType.MachineToMachine].includes(
|
||||
applicationType
|
||||
) && (
|
||||
|
|
|
@ -9,7 +9,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 { useParams } from 'react-router-dom';
|
||||
import { Outlet, useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Delete from '@/assets/icons/delete.svg';
|
||||
|
@ -20,6 +20,7 @@ import DetailsPage from '@/components/DetailsPage';
|
|||
import Drawer from '@/components/Drawer';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
|
||||
import { ApplicationDetailsTabs } from '@/consts';
|
||||
import { openIdProviderConfigPath } from '@/consts/oidc';
|
||||
import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu';
|
||||
import Button from '@/ds-components/Button';
|
||||
|
@ -30,17 +31,15 @@ import TabNav, { TabNavItem } from '@/ds-components/TabNav';
|
|||
import Tag from '@/ds-components/Tag';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
import GuideModal from '../Applications/components/Guide/GuideModal';
|
||||
|
||||
import AdvancedSettings from './components/AdvancedSettings';
|
||||
import GuideDrawer from './components/GuideDrawer';
|
||||
import Settings from './components/Settings';
|
||||
import * as styles from './index.module.scss';
|
||||
import { type ApplicationDetailsOutletContext } from './types';
|
||||
|
||||
const mapToUriFormatArrays = (value?: string[]) =>
|
||||
value?.filter(Boolean).map((uri) => decodeURIComponent(uri));
|
||||
|
@ -72,7 +71,6 @@ function ApplicationDetails() {
|
|||
const formMethods = useForm<Application & { isAdmin: boolean }>({
|
||||
defaultValues: { customClientMetadata: customClientMetadataDefault },
|
||||
});
|
||||
const { getDocumentationUrl } = useDocumentationUrl();
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
|
@ -215,7 +213,14 @@ function ApplicationDetails() {
|
|||
</div>
|
||||
</Card>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/applications/${data.id}`}>{t('general.settings_nav')}</TabNavItem>
|
||||
<TabNavItem href={`/applications/${data.id}/${ApplicationDetailsTabs.Settings}`}>
|
||||
{t('application_details.settings')}
|
||||
</TabNavItem>
|
||||
<TabNavItem
|
||||
href={`/applications/${data.id}/${ApplicationDetailsTabs.AdvancedSettings}`}
|
||||
>
|
||||
{t('application_details.advanced_settings')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<FormProvider {...formMethods}>
|
||||
<DetailsForm
|
||||
|
@ -224,8 +229,14 @@ function ApplicationDetails() {
|
|||
onDiscard={reset}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Settings data={data} />
|
||||
<AdvancedSettings applicationType={data.type} oidcConfig={oidcConfig} />
|
||||
<Outlet
|
||||
context={
|
||||
{
|
||||
app: data,
|
||||
oidcConfig,
|
||||
} satisfies ApplicationDetailsOutletContext
|
||||
}
|
||||
/>
|
||||
</DetailsForm>
|
||||
</FormProvider>
|
||||
</>
|
||||
|
|
6
packages/console/src/pages/ApplicationDetails/types.ts
Normal file
6
packages/console/src/pages/ApplicationDetails/types.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { type SnakeCaseOidcConfig, type Application } from '@logto/schemas';
|
||||
|
||||
export type ApplicationDetailsOutletContext = {
|
||||
app: Application;
|
||||
oidcConfig: SnakeCaseOidcConfig;
|
||||
};
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Autorisierungs-Endpoint',
|
||||
authorization_endpoint_tip:
|
||||
'Der Endpoint, der für die Authentifizierung und <a>Authorisierung</a> via OpenID Connect verwendet wird.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'App ID',
|
||||
application_id_tip:
|
||||
'Die eindeutige Anwendungs-ID, die normalerweise von Logto generiert wird. Es steht auch für "<a>client_id</a>" in OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Authorization Endpoint',
|
||||
authorization_endpoint_tip:
|
||||
"The endpoint to perform authentication and authorization. It's used for OpenID Connect <a>Authentication</a>.",
|
||||
logto_endpoint: 'Logto endpoint',
|
||||
application_id: 'App ID',
|
||||
application_id_tip:
|
||||
'The unique application identifier normally generated by Logto. It also stands for “<a>client_id</a>” in OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Endpoint de Autorización',
|
||||
authorization_endpoint_tip:
|
||||
'El endpoint para la autenticación y autorización. Se utiliza para OpenID Connect <a>Autenticación</a>.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'ID de Aplicación',
|
||||
application_id_tip:
|
||||
'El identificador de aplicación único normalmente generado por Logto. También se conoce como “<a>client_id</a>” en OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: "Point de terminaison d'autorisation",
|
||||
authorization_endpoint_tip:
|
||||
"Le point de terminaison pour effectuer l'authentification et l'autorisation. Il est utilisé pour <a>l'authentification</a> OpenID Connect.",
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: "ID de l'application",
|
||||
application_id_tip:
|
||||
"L'identifiant d'application unique généralement généré par Logto. Il signifie également <a>client_id</a> dans OpenID Connect.",
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Endpoint di autorizzazione',
|
||||
authorization_endpoint_tip:
|
||||
"L'endpoint per effettuare l'autenticazione e l'autorizzazione. Viene utilizzato per la connessione OpenID <a>autenticazione</a>.",
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'App ID',
|
||||
application_id_tip:
|
||||
'L\'identificatore univoco dell\'applicazione generato normalmente da Logto. Sta anche per "<a>client_id</a>" in OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: '認可エンドポイント',
|
||||
authorization_endpoint_tip:
|
||||
'認証と認可を実行するエンドポイントです。OpenID Connectの<a>認証</a>に使用されます。',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'アプリID',
|
||||
application_id_tip:
|
||||
'通常Logtoによって生成される一意のアプリケーション識別子です。OpenID Connectでは「<a>client_id</a>」とも呼ばれます。',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: '인증 Endpoint',
|
||||
authorization_endpoint_tip:
|
||||
'인증 및 권한 부여를 진행할 End-Point예요. OpenID Connect <a>인증</a>에서 사용되던 값이에요.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: '앱 ID',
|
||||
application_id_tip:
|
||||
'일반적으로 Logto에서 생성되는 고유한 응용 프로그램 식별자예요. OpenID Connect에서 "<a>client_id</a>"의 약자이기도 해요.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Endpoint autoryzacji',
|
||||
authorization_endpoint_tip:
|
||||
'Endpoint wykorzystywany do autentykacji i autoryzacji. Używany jest dla OpenID Connect <a>Autentykacji</a>.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'ID aplikacji',
|
||||
application_id_tip:
|
||||
'Unikalny identyfikator aplikacji, który jest zwykle generowany przez Logto. Oznacza również „<a>client_id</a>” w OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Endpoint de autorização',
|
||||
authorization_endpoint_tip:
|
||||
'O endpoint para executar autenticação e autorização. É usado para <a>autenticação</a> OpenID Connect.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'ID do aplicativo',
|
||||
application_id_tip:
|
||||
'O identificador exclusivo do aplicativo normalmente gerado pela Logto. Também é conhecido como “<a>client_id</a>” em OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Endpoint de autorização',
|
||||
authorization_endpoint_tip:
|
||||
'O endpoint para realizar a autenticação e autorização. É usado para <a>autenticação</a> OpenID Connect.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'ID da aplicação',
|
||||
application_id_tip:
|
||||
'O identificador exclusivo da aplicação normalmente gerado pelo Logto. Também representa “<a>client_id</a>” no OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Конечная точка авторизации',
|
||||
authorization_endpoint_tip:
|
||||
'Конечная точка для аутентификации и авторизации. Он используется для аутентификации <a> OpenID Connect </a>.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'ID приложения',
|
||||
application_id_tip:
|
||||
'Уникальный идентификатор приложения, обычно генерируемый Logto. Он также означает «<a> client_id </a>» в OpenID Connect.',
|
||||
|
|
|
@ -16,6 +16,7 @@ const application_details = {
|
|||
authorization_endpoint: 'Yetkilendirme bitiş noktası',
|
||||
authorization_endpoint_tip:
|
||||
'Kimlik doğrulama ve yetkilendirme gerçekleştirmek için bitiş noktası. OpenID Connect <a>Authentication</a> için kullanılır.',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: 'Uygulama IDsi',
|
||||
application_id_tip:
|
||||
'Logto tarafından normalde oluşturulan benzersiz uygulama tanımlayıcısıdır. Ayrıca OpenID Connect "client_id" anlamına gelir.',
|
||||
|
|
|
@ -14,6 +14,7 @@ const application_details = {
|
|||
config_endpoint: 'OpenID Provider 配置端点',
|
||||
authorization_endpoint: '授权端点',
|
||||
authorization_endpoint_tip: '进行鉴权与授权的端点。用于 OpenID Connect 中的 <a>鉴权</a> 流程。',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: '应用 ID',
|
||||
application_id_tip:
|
||||
'应用的唯一标识,通常由 Logto 生成。等价于 OpenID Connect 中的 <a>client_id</a>。',
|
||||
|
|
|
@ -14,6 +14,7 @@ const application_details = {
|
|||
config_endpoint: 'OpenID Provider 配置端點',
|
||||
authorization_endpoint: '授權端點',
|
||||
authorization_endpoint_tip: '進行驗證和授權的端點。用於 OpenID Connect 中的<a> 驗證 </a> 流程。',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: '應用程式 ID',
|
||||
application_id_tip:
|
||||
'應用程式的唯一標識,通常由 Logto 生成。等價於 OpenID Connect 中的<a> client_id </a>。',
|
||||
|
|
|
@ -14,6 +14,7 @@ const application_details = {
|
|||
config_endpoint: 'OpenID Provider 配置端點',
|
||||
authorization_endpoint: '授權端點',
|
||||
authorization_endpoint_tip: '進行驗證與授權的端點。用於 OpenID Connect 中的 <a>驗證</a> 流程。',
|
||||
logto_endpoint: 'Logto endpoint', // UNTRANSLATED
|
||||
application_id: '應用程式 ID',
|
||||
application_id_tip:
|
||||
'應用程式的唯一標識,通常由 Logto 生成。相當於 OpenID Connect 中的 <a>client_id</a>。',
|
||||
|
|
Loading…
Reference in a new issue