0
Fork 0
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:
Charles Zhao 2023-08-17 00:46:32 -05:00 committed by GitHub
parent 6e03b83658
commit 66c75cb0af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 79 additions and 40 deletions

View file

@ -1,3 +1,8 @@
export enum ApplicationDetailsTabs {
Settings = 'settings',
AdvancedSettings = 'advanced-settings',
}
export enum ApiResourceDetailsTabs {
Settings = 'settings',
Permissions = 'permissions',

View file

@ -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 />} />

View file

@ -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,

View file

@ -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
) && (

View file

@ -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>
</>

View file

@ -0,0 +1,6 @@
import { type SnakeCaseOidcConfig, type Application } from '@logto/schemas';
export type ApplicationDetailsOutletContext = {
app: Application;
oidcConfig: SnakeCaseOidcConfig;
};

View file

@ -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.',

View file

@ -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.',

View file

@ -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.',

View file

@ -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.",

View file

@ -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.',

View file

@ -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>」とも呼ばれます。',

View file

@ -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>"의 약자이기도 해요.',

View file

@ -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.',

View file

@ -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.',

View file

@ -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.',

View file

@ -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.',

View file

@ -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.',

View file

@ -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>。',

View file

@ -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>。',

View file

@ -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>。',