From e3e8b14569d5fe03c0a69579bbcde5c72a4b0b95 Mon Sep 17 00:00:00 2001 From: simeng-li <simeng@silverhand.io> Date: Thu, 28 Mar 2024 16:32:36 +0800 Subject: [PATCH 1/3] refactor(console): rename the jwtClaims page rename the jwtClaims page --- .../CodeEditorLoadingContext.ts | 0 .../Main.tsx | 0 .../ActionButton/CodeClearButton.tsx | 0 .../ActionButton/CodeRestoreButton.tsx | 0 .../MonacoCodeEditor/ActionButton/index.tsx | 0 .../MonacoCodeEditor/config.ts | 0 .../MonacoCodeEditor/index.module.scss | 0 .../MonacoCodeEditor/index.tsx | 0 .../MonacoCodeEditor/type.ts | 0 .../MonacoCodeEditor/use-editor-height.ts | 0 .../PageLoadingSkeleton/index.module.scss | 0 .../PageLoadingSkeleton/index.tsx | 0 .../ScriptSection.tsx | 0 .../EnvironmentVariablesField.tsx | 0 .../SettingsSection/GuideCard.tsx | 0 .../SettingsSection/InstructionTab.tsx | 0 .../SettingsSection/TestResult.tsx | 0 .../SettingsSection/TestTab.tsx | 0 .../SettingsSection/index.module.scss | 0 .../SettingsSection/index.tsx | 0 .../index.module.scss | 0 .../index.tsx | 31 +++++-------------- .../type.ts | 0 .../use-jwt-customizer.ts | 0 .../utils/config.tsx | 0 .../utils/format.ts | 0 .../utils/type-definitions.ts | 0 27 files changed, 7 insertions(+), 24 deletions(-) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/CodeEditorLoadingContext.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/Main.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/ActionButton/CodeClearButton.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/ActionButton/CodeRestoreButton.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/ActionButton/index.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/config.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/index.module.scss (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/index.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/type.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/MonacoCodeEditor/use-editor-height.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/PageLoadingSkeleton/index.module.scss (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/PageLoadingSkeleton/index.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/ScriptSection.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/EnvironmentVariablesField.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/GuideCard.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/InstructionTab.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/TestResult.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/TestTab.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/index.module.scss (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/SettingsSection/index.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/index.module.scss (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/index.tsx (53%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/type.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/use-jwt-customizer.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/utils/config.tsx (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/utils/format.ts (100%) rename packages/console/src/pages/{JwtClaims => CustomizeJwtDetails}/utils/type-definitions.ts (100%) diff --git a/packages/console/src/pages/JwtClaims/CodeEditorLoadingContext.ts b/packages/console/src/pages/CustomizeJwtDetails/CodeEditorLoadingContext.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/CodeEditorLoadingContext.ts rename to packages/console/src/pages/CustomizeJwtDetails/CodeEditorLoadingContext.ts diff --git a/packages/console/src/pages/JwtClaims/Main.tsx b/packages/console/src/pages/CustomizeJwtDetails/Main.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/Main.tsx rename to packages/console/src/pages/CustomizeJwtDetails/Main.tsx diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/CodeClearButton.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/CodeClearButton.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/CodeRestoreButton.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeRestoreButton.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/CodeRestoreButton.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeRestoreButton.tsx diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/index.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/ActionButton/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/index.tsx diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/config.ts b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/config.ts rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.module.scss similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.module.scss rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.module.scss diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/type.ts b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/type.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/type.ts rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/type.ts diff --git a/packages/console/src/pages/JwtClaims/MonacoCodeEditor/use-editor-height.ts b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/use-editor-height.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/MonacoCodeEditor/use-editor-height.ts rename to packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/use-editor-height.ts diff --git a/packages/console/src/pages/JwtClaims/PageLoadingSkeleton/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss similarity index 100% rename from packages/console/src/pages/JwtClaims/PageLoadingSkeleton/index.module.scss rename to packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss diff --git a/packages/console/src/pages/JwtClaims/PageLoadingSkeleton/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/PageLoadingSkeleton/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx diff --git a/packages/console/src/pages/JwtClaims/ScriptSection.tsx b/packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/ScriptSection.tsx rename to packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/EnvironmentVariablesField.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/EnvironmentVariablesField.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/EnvironmentVariablesField.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/EnvironmentVariablesField.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/GuideCard.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/GuideCard.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/GuideCard.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/GuideCard.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/InstructionTab.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/InstructionTab.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/InstructionTab.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/InstructionTab.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/TestResult.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestResult.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/TestResult.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestResult.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/TestTab.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestTab.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/TestTab.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestTab.tsx diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.module.scss similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/index.module.scss rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.module.scss diff --git a/packages/console/src/pages/JwtClaims/SettingsSection/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/SettingsSection/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.tsx diff --git a/packages/console/src/pages/JwtClaims/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/index.module.scss similarity index 100% rename from packages/console/src/pages/JwtClaims/index.module.scss rename to packages/console/src/pages/CustomizeJwtDetails/index.module.scss diff --git a/packages/console/src/pages/JwtClaims/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/index.tsx similarity index 53% rename from packages/console/src/pages/JwtClaims/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/index.tsx index e6a42dd5a..92c4fc8e0 100644 --- a/packages/console/src/pages/JwtClaims/index.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/index.tsx @@ -1,10 +1,8 @@ import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact'; -import { LogtoJwtTokenPath } from '@logto/schemas'; +import { type LogtoJwtTokenPath } from '@logto/schemas'; import { useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; import CardTitle from '@/ds-components/CardTitle'; -import TabNav, { TabNavItem } from '@/ds-components/TabNav'; import { CodeEditorLoadingContext } from './CodeEditorLoadingContext'; import Main from './Main'; @@ -12,20 +10,12 @@ import PageLoadingSkeleton from './PageLoadingSkeleton'; import * as styles from './index.module.scss'; import useJwtCustomizer from './use-jwt-customizer'; -const tabPhrases = Object.freeze({ - [LogtoJwtTokenPath.AccessToken]: 'user_jwt.card_field', - [LogtoJwtTokenPath.ClientCredentials]: 'machine_to_machine_jwt.card_field', -}); - -const getPagePath = (tokenType: LogtoJwtTokenPath) => `/jwt-customizer/${tokenType}`; - type Props = { - tab: LogtoJwtTokenPath; + tokenType: LogtoJwtTokenPath; + action: 'create' | 'edit'; }; -function JwtClaims({ tab }: Props) { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - +function CustomizeJwtDetails({ tokenType }: Props) { const { isLoading, ...rest } = useJwtCustomizer(); const [isMonacoLoaded, setIsMonacoLoaded] = useState(false); @@ -41,18 +31,11 @@ function JwtClaims({ tab }: Props) { subtitle="jwt_claims.description" className={styles.header} /> - <TabNav className={styles.tabNav}> - {Object.values(LogtoJwtTokenPath).map((tokenType) => ( - <TabNavItem key={tokenType} href={getPagePath(tokenType)} isActive={tokenType === tab}> - {t(`jwt_claims.${tabPhrases[tokenType]}`)} - </TabNavItem> - ))} - </TabNav> - {(isLoading || !isMonacoLoaded) && <PageLoadingSkeleton tokenType={tab} />} + {(isLoading || !isMonacoLoaded) && <PageLoadingSkeleton tokenType={tokenType} />} {!isLoading && ( <CodeEditorLoadingContext.Provider value={codeEditorContextValue}> - <Main tab={tab} {...rest} className={isMonacoLoaded ? undefined : styles.hidden} /> + <Main tab={tokenType} {...rest} className={isMonacoLoaded ? undefined : styles.hidden} /> </CodeEditorLoadingContext.Provider> )} </div> @@ -60,4 +43,4 @@ function JwtClaims({ tab }: Props) { } // eslint-disable-next-line import/no-unused-modules -- will update this later -export default withAppInsights(JwtClaims); +export default withAppInsights(CustomizeJwtDetails); diff --git a/packages/console/src/pages/JwtClaims/type.ts b/packages/console/src/pages/CustomizeJwtDetails/type.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/type.ts rename to packages/console/src/pages/CustomizeJwtDetails/type.ts diff --git a/packages/console/src/pages/JwtClaims/use-jwt-customizer.ts b/packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/use-jwt-customizer.ts rename to packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts diff --git a/packages/console/src/pages/JwtClaims/utils/config.tsx b/packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx similarity index 100% rename from packages/console/src/pages/JwtClaims/utils/config.tsx rename to packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx diff --git a/packages/console/src/pages/JwtClaims/utils/format.ts b/packages/console/src/pages/CustomizeJwtDetails/utils/format.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/utils/format.ts rename to packages/console/src/pages/CustomizeJwtDetails/utils/format.ts diff --git a/packages/console/src/pages/JwtClaims/utils/type-definitions.ts b/packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts similarity index 100% rename from packages/console/src/pages/JwtClaims/utils/type-definitions.ts rename to packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts From 777ef0e483f3de1fd5d0e5a1a539edca33a80d7c Mon Sep 17 00:00:00 2001 From: simeng-li <simeng@silverhand.io> Date: Fri, 29 Mar 2024 10:20:57 +0800 Subject: [PATCH 2/3] refactor(console,phrases): refactor the customize jwt details page refactor the customize jwt details page --- .../ConsoleContent/Sidebar/hook.tsx | 4 +- .../src/containers/ConsoleContent/index.tsx | 2 + packages/console/src/hooks/use-swr-fetcher.ts | 4 +- .../pages/CustomizeJwt/CreateButton/index.tsx | 4 +- .../CustomizeJwt/CustomizerItem/index.tsx | 6 +- .../src/pages/CustomizeJwt/utils/path.ts | 4 +- .../src/pages/CustomizeJwt/utils/type.ts | 1 + .../src/pages/CustomizeJwtDetails/Main.tsx | 112 ---------------- .../ScriptSection/index.module.scss | 17 +++ .../MainContent/ScriptSection/index.tsx | 81 ++++++++++++ .../EnvironmentVariablesField.tsx | 6 +- .../SettingsSection/GuideCard.tsx | 0 .../SettingsSection/InstructionTab.tsx | 12 +- .../SettingsSection/TestResult.tsx | 0 .../SettingsSection/TestTab.tsx | 22 ++-- .../SettingsSection/index.module.scss | 0 .../SettingsSection/index.tsx | 0 .../MainContent/index.module.scss | 17 +++ .../CustomizeJwtDetails/MainContent/index.tsx | 97 ++++++++++++++ .../MonacoCodeEditor/config.ts | 1 - .../MonacoCodeEditor/index.tsx | 18 +-- .../PageLoadingSkeleton/index.module.scss | 15 +++ .../PageLoadingSkeleton/index.tsx | 9 +- .../CustomizeJwtDetails/ScriptSection.tsx | 93 -------------- .../CustomizeJwtDetails/index.module.scss | 38 +----- .../src/pages/CustomizeJwtDetails/index.tsx | 52 +++++--- .../src/pages/CustomizeJwtDetails/type.ts | 21 ++- .../CustomizeJwtDetails/use-data-fetch.ts | 35 +++++ .../CustomizeJwtDetails/use-jwt-customizer.ts | 67 ---------- .../CustomizeJwtDetails/utils/config.tsx | 8 +- .../pages/CustomizeJwtDetails/utils/format.ts | 121 +++++++++--------- .../pages/CustomizeJwtDetails/utils/path.ts | 4 + .../utils/type-definitions.ts | 6 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../de/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../en/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../es/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../fr/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../it/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../ja/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../ko/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../pl-pl/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../pt-br/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../pt-pt/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../ru/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../tr-tr/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../zh-cn/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../zh-hk/translation/admin-console/tabs.ts | 2 +- .../translation/admin-console/jwt-claims.ts | 2 +- .../zh-tw/translation/admin-console/tabs.ts | 2 +- 63 files changed, 460 insertions(+), 477 deletions(-) create mode 100644 packages/console/src/pages/CustomizeJwt/utils/type.ts delete mode 100644 packages/console/src/pages/CustomizeJwtDetails/Main.tsx create mode 100644 packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.module.scss create mode 100644 packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.tsx rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/EnvironmentVariablesField.tsx (95%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/GuideCard.tsx (100%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/InstructionTab.tsx (95%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/TestResult.tsx (100%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/TestTab.tsx (92%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/index.module.scss (100%) rename packages/console/src/pages/CustomizeJwtDetails/{ => MainContent}/SettingsSection/index.tsx (100%) create mode 100644 packages/console/src/pages/CustomizeJwtDetails/MainContent/index.module.scss create mode 100644 packages/console/src/pages/CustomizeJwtDetails/MainContent/index.tsx delete mode 100644 packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx create mode 100644 packages/console/src/pages/CustomizeJwtDetails/use-data-fetch.ts delete mode 100644 packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts create mode 100644 packages/console/src/pages/CustomizeJwtDetails/utils/path.ts diff --git a/packages/console/src/containers/ConsoleContent/Sidebar/hook.tsx b/packages/console/src/containers/ConsoleContent/Sidebar/hook.tsx index ffd9e771f..5c09fa01f 100644 --- a/packages/console/src/containers/ConsoleContent/Sidebar/hook.tsx +++ b/packages/console/src/containers/ConsoleContent/Sidebar/hook.tsx @@ -18,7 +18,7 @@ import Role from '@/assets/icons/role.svg'; import SecurityLock from '@/assets/icons/security-lock.svg'; import EnterpriseSso from '@/assets/icons/single-sign-on.svg'; import Web from '@/assets/icons/web.svg'; -import { isDevFeaturesEnabled, isCloud } from '@/consts/env'; +import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; type SidebarItem = { Icon: FC; @@ -128,7 +128,7 @@ export const useSidebarMenuItems = (): { }, { Icon: JwtClaims, - title: 'jwt_customizer', + title: 'customize_jwt', isHidden: !isDevFeaturesEnabled, }, { diff --git a/packages/console/src/containers/ConsoleContent/index.tsx b/packages/console/src/containers/ConsoleContent/index.tsx index eff2741a0..2bf92992e 100644 --- a/packages/console/src/containers/ConsoleContent/index.tsx +++ b/packages/console/src/containers/ConsoleContent/index.tsx @@ -27,6 +27,7 @@ import AuditLogs from '@/pages/AuditLogs'; import ConnectorDetails from '@/pages/ConnectorDetails'; import Connectors from '@/pages/Connectors'; import CustomizeJwt from '@/pages/CustomizeJwt'; +import CustomizeJwtDetails from '@/pages/CustomizeJwtDetails'; import Dashboard from '@/pages/Dashboard'; import EnterpriseSsoConnectors from '@/pages/EnterpriseSso'; import EnterpriseSsoConnectorDetails from '@/pages/EnterpriseSsoDetails'; @@ -244,6 +245,7 @@ function ConsoleContent() { {isCloud && isDevFeaturesEnabled && ( <Route path="jwt-customizer"> <Route index element={<CustomizeJwt />} /> + <Route path=":tokenType/:action" element={<CustomizeJwtDetails />} /> </Route> )} </Routes> diff --git a/packages/console/src/hooks/use-swr-fetcher.ts b/packages/console/src/hooks/use-swr-fetcher.ts index 9c278cad3..da2acbfa2 100644 --- a/packages/console/src/hooks/use-swr-fetcher.ts +++ b/packages/console/src/hooks/use-swr-fetcher.ts @@ -1,5 +1,5 @@ -import { HTTPError } from 'ky'; import type ky from 'ky'; +import { HTTPError } from 'ky'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import type { Fetcher } from 'swr'; @@ -22,6 +22,7 @@ const useSwrFetcher: UseSwrFetcherHook = <T>(api: KyInstance) => { async (resource: string) => { try { const response = await api.get(resource); + const data = await response.json<T>(); if (typeof resource === 'string' && resource.includes('?')) { @@ -42,6 +43,7 @@ const useSwrFetcher: UseSwrFetcherHook = <T>(api: KyInstance) => { } catch (error: unknown) { if (error instanceof HTTPError) { const { response } = error; + // See https://stackoverflow.com/questions/53511974/javascript-fetch-failed-to-execute-json-on-response-body-stream-is-locked // for why `.clone()` is needed throw new RequestError(response.status, await response.clone().json()); diff --git a/packages/console/src/pages/CustomizeJwt/CreateButton/index.tsx b/packages/console/src/pages/CustomizeJwt/CreateButton/index.tsx index 81d8db182..52a76cf3b 100644 --- a/packages/console/src/pages/CustomizeJwt/CreateButton/index.tsx +++ b/packages/console/src/pages/CustomizeJwt/CreateButton/index.tsx @@ -1,8 +1,8 @@ import { type LogtoJwtTokenPath } from '@logto/schemas'; -import { useNavigate } from 'react-router-dom'; import PlusIcon from '@/assets/icons/plus.svg'; import Button from '@/ds-components/Button'; +import useTenantPathname from '@/hooks/use-tenant-pathname'; import { getPagePath } from '@/pages/CustomizeJwt/utils/path'; import * as styles from './index.module.scss'; @@ -13,7 +13,7 @@ type Props = { function CreateButton({ tokenType }: Props) { const link = getPagePath(tokenType, 'create'); - const navigate = useNavigate(); + const { navigate } = useTenantPathname(); return ( <Button diff --git a/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx b/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx index 74254e592..e566dec0e 100644 --- a/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx +++ b/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx @@ -1,7 +1,6 @@ import { LogtoJwtTokenPath } from '@logto/schemas'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; import { useSWRConfig } from 'swr'; import DeletIcon from '@/assets/icons/delete.svg'; @@ -9,6 +8,7 @@ import EditIcon from '@/assets/icons/edit.svg'; import Button from '@/ds-components/Button'; import useApi from '@/hooks/use-api'; import { useConfirmModal } from '@/hooks/use-confirm-modal'; +import useTenantPathname from '@/hooks/use-tenant-pathname'; import { getApiPath, getPagePath } from '@/pages/CustomizeJwt/utils/path'; import * as styles from './index.module.scss'; @@ -21,7 +21,7 @@ function CustomizerItem({ tokenType }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const apiLink = getApiPath(tokenType); const editLink = getPagePath(tokenType, 'edit'); - const navigate = useNavigate(); + const { navigate } = useTenantPathname(); const { show } = useConfirmModal(); const { mutate } = useSWRConfig(); @@ -36,7 +36,7 @@ function CustomizerItem({ tokenType }: Props) { if (confirm) { await api.delete(apiLink); - await mutate(apiLink); + await mutate(apiLink, undefined); } }, [api, apiLink, mutate, show, t]); diff --git a/packages/console/src/pages/CustomizeJwt/utils/path.ts b/packages/console/src/pages/CustomizeJwt/utils/path.ts index b3304e7c1..eb8ae2974 100644 --- a/packages/console/src/pages/CustomizeJwt/utils/path.ts +++ b/packages/console/src/pages/CustomizeJwt/utils/path.ts @@ -1,9 +1,11 @@ import { type LogtoJwtTokenPath } from '@logto/schemas'; +import { type Action } from './type'; + export const getApiPath = (tokenType: LogtoJwtTokenPath) => `api/configs/jwt-customizer/${tokenType}`; -export const getPagePath = (tokenType?: LogtoJwtTokenPath, action?: 'create' | 'edit') => { +export const getPagePath = (tokenType?: LogtoJwtTokenPath, action?: Action) => { if (!tokenType) { return '/customize-jwt'; } diff --git a/packages/console/src/pages/CustomizeJwt/utils/type.ts b/packages/console/src/pages/CustomizeJwt/utils/type.ts new file mode 100644 index 000000000..4c1266ed0 --- /dev/null +++ b/packages/console/src/pages/CustomizeJwt/utils/type.ts @@ -0,0 +1 @@ +export type Action = 'create' | 'edit'; diff --git a/packages/console/src/pages/CustomizeJwtDetails/Main.tsx b/packages/console/src/pages/CustomizeJwtDetails/Main.tsx deleted file mode 100644 index 2fa5e1e8b..000000000 --- a/packages/console/src/pages/CustomizeJwtDetails/Main.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { - type AccessTokenJwtCustomizer, - LogtoJwtTokenPath, - type ClientCredentialsJwtCustomizer, -} from '@logto/schemas'; -import classNames from 'classnames'; -import { useMemo } from 'react'; -import { useForm, FormProvider } from 'react-hook-form'; -import { type KeyedMutator } from 'swr'; - -import SubmitFormChangesActionBar from '@/components/SubmitFormChangesActionBar'; -import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; -import useApi from '@/hooks/use-api'; -import { trySubmitSafe } from '@/utils/form'; - -import ScriptSection from './ScriptSection'; -import SettingsSection from './SettingsSection'; -import * as styles from './index.module.scss'; -import { type JwtClaimsFormType } from './type'; -import { - formatResponseDataToFormData, - formatFormDataToRequestData, - getApiPath, -} from './utils/format'; - -type Props = { - className?: string; - tab: LogtoJwtTokenPath; - accessTokenJwtCustomizer: AccessTokenJwtCustomizer | undefined; - clientCredentialsJwtCustomizer: ClientCredentialsJwtCustomizer | undefined; - mutateAccessTokenJwtCustomizer: KeyedMutator<AccessTokenJwtCustomizer>; - mutateClientCredentialsJwtCustomizer: KeyedMutator<ClientCredentialsJwtCustomizer>; -}; - -function Main({ - className, - tab, - accessTokenJwtCustomizer, - clientCredentialsJwtCustomizer, - mutateAccessTokenJwtCustomizer, - mutateClientCredentialsJwtCustomizer, -}: Props) { - const api = useApi(); - - const userJwtClaimsForm = useForm<JwtClaimsFormType>({ - defaultValues: formatResponseDataToFormData( - LogtoJwtTokenPath.AccessToken, - accessTokenJwtCustomizer - ), - }); - - const machineToMachineJwtClaimsForm = useForm<JwtClaimsFormType>({ - defaultValues: formatResponseDataToFormData( - LogtoJwtTokenPath.ClientCredentials, - clientCredentialsJwtCustomizer - ), - }); - - const activeForm = useMemo( - () => - tab === LogtoJwtTokenPath.AccessToken ? userJwtClaimsForm : machineToMachineJwtClaimsForm, - [machineToMachineJwtClaimsForm, tab, userJwtClaimsForm] - ); - - const { - formState: { isDirty, isSubmitting }, - reset, - handleSubmit, - } = activeForm; - - const onSubmitHandler = handleSubmit( - trySubmitSafe(async (data) => { - if (isSubmitting) { - return; - } - - const { tokenType } = data; - const payload = formatFormDataToRequestData(data); - - await api.put(getApiPath(tokenType), { json: payload }); - - const mutate = - tokenType === LogtoJwtTokenPath.AccessToken - ? mutateAccessTokenJwtCustomizer - : mutateClientCredentialsJwtCustomizer; - - const result = await mutate(); - - reset(formatResponseDataToFormData(tokenType, result)); - }) - ); - - return ( - <> - <FormProvider {...activeForm}> - <form className={classNames(styles.tabContent, className)}> - <ScriptSection /> - <SettingsSection /> - </form> - </FormProvider> - <SubmitFormChangesActionBar - isOpen={isDirty} - isSubmitting={isSubmitting} - onDiscard={reset} - onSubmit={onSubmitHandler} - /> - <UnsavedChangesAlertModal hasUnsavedChanges={isDirty && !isSubmitting} onConfirm={reset} /> - </> - ); -} - -export default Main; diff --git a/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.module.scss new file mode 100644 index 000000000..fd959ef67 --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.module.scss @@ -0,0 +1,17 @@ +@use '@/scss/underscore' as _; + + +.codePanel { + position: relative; + display: flex; + flex-direction: column; + + .cardTitle { + font: var(--font-label-2); + margin-bottom: _.unit(3); + } + + .flexGrow { + flex-grow: 1; + } +} diff --git a/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.tsx new file mode 100644 index 000000000..d1d488f8e --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/ScriptSection/index.tsx @@ -0,0 +1,81 @@ +/* Code Editor for the custom JWT claims script. */ +import { LogtoJwtTokenPath } from '@logto/schemas'; +import { useCallback, useContext, useMemo } from 'react'; +import { Controller, useFormContext, useWatch } from 'react-hook-form'; + +import { CodeEditorLoadingContext } from '@/pages/CustomizeJwtDetails/CodeEditorLoadingContext'; +import MonacoCodeEditor, { type ModelSettings } from '@/pages/CustomizeJwtDetails/MonacoCodeEditor'; +import { type JwtCustomizerForm } from '@/pages/CustomizeJwtDetails/type'; +import { + accessTokenJwtCustomizerModel, + clientCredentialsModel, +} from '@/pages/CustomizeJwtDetails/utils/config'; +import { buildEnvironmentVariablesTypeDefinition } from '@/pages/CustomizeJwtDetails/utils/type-definitions'; + +import * as styles from './index.module.scss'; + +function ScriptSection() { + const { watch, control } = useFormContext<JwtCustomizerForm>(); + const tokenType = watch('tokenType'); + + // Need to use useWatch hook to subscribe the mutation of the environmentVariables field + // Otherwise, the default watch function's return value won't mutate when the environmentVariables field changes + const envVariables = useWatch({ + control, + name: 'environmentVariables', + }); + + const environmentVariablesTypeDefinition = useMemo( + () => buildEnvironmentVariablesTypeDefinition(envVariables), + [envVariables] + ); + + // Get the active model based on the token type + const activeModel = useMemo<ModelSettings>( + () => + tokenType === LogtoJwtTokenPath.AccessToken + ? accessTokenJwtCustomizerModel + : clientCredentialsModel, + [tokenType] + ); + + // Set the Monaco editor loaded state to true when the editor is mounted + const { setIsMonacoLoaded } = useContext(CodeEditorLoadingContext); + + const onMountHandler = useCallback(() => { + setIsMonacoLoaded(true); + }, [setIsMonacoLoaded]); + + return ( + <Controller + // Force rerender the controller when the token type changes + // Otherwise the input field will not be updated + key={tokenType} + control={control} + name="script" + render={({ field: { onChange, value }, formState: { defaultValues } }) => ( + <MonacoCodeEditor + className={styles.flexGrow} + enabledActions={['restore', 'copy']} + models={[activeModel]} + activeModelName={activeModel.name} + value={value} + environmentVariablesDefinition={environmentVariablesTypeDefinition} + onChange={(newValue) => { + // If the value is the same as the default code and the original form script value is undefined, reset the value to undefined as well + if (newValue === activeModel.defaultValue && !defaultValues?.script) { + onChange(''); + return; + } + + // Input value should not be undefined for react-hook-form @see https://react-hook-form.com/docs/usecontroller/controller + onChange(newValue ?? ''); + }} + onMountHandler={onMountHandler} + /> + )} + /> + ); +} + +export default ScriptSection; diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/EnvironmentVariablesField.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/EnvironmentVariablesField.tsx similarity index 95% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/EnvironmentVariablesField.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/EnvironmentVariablesField.tsx index 87c803c2f..31ccfdf09 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/EnvironmentVariablesField.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/EnvironmentVariablesField.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import FormField from '@/ds-components/FormField'; import KeyValueInputField from '@/ds-components/KeyValueInputField'; -import { type JwtClaimsFormType } from '../type'; +import { type JwtCustomizerForm } from '../../type'; const isValidKey = (key: string) => { return /^\w+$/.test(key); @@ -26,10 +26,10 @@ function EnvironmentVariablesField({ className }: Props) { errors: { environmentVariables: envVariableErrors }, submitCount, }, - } = useFormContext<JwtClaimsFormType>(); + } = useFormContext<JwtCustomizerForm>(); // Read the form controller from the context @see {@link https://react-hook-form.com/docs/usefieldarray} - const { fields, remove, append } = useFieldArray<JwtClaimsFormType>({ + const { fields, remove, append } = useFieldArray<JwtCustomizerForm>({ name: 'environmentVariables', }); diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/GuideCard.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/GuideCard.tsx similarity index 100% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/GuideCard.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/GuideCard.tsx diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/InstructionTab.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/InstructionTab.tsx similarity index 95% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/InstructionTab.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/InstructionTab.tsx index 380cf37b3..475b5a011 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/InstructionTab.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/InstructionTab.tsx @@ -4,18 +4,18 @@ import classNames from 'classnames'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { type JwtClaimsFormType } from '../type'; +import { type JwtCustomizerForm } from '../../type'; import { + environmentVariablesCodeExample, + fetchExternalDataCodeExample, sampleCodeEditorOptions, typeDefinitionCodeEditorOptions, - fetchExternalDataCodeExample, - environmentVariablesCodeExample, -} from '../utils/config'; +} from '../../utils/config'; import { accessTokenPayloadTypeDefinition, clientCredentialsPayloadTypeDefinition, jwtCustomizerUserContextTypeDefinition, -} from '../utils/type-definitions'; +} from '../../utils/type-definitions'; import EnvironmentVariablesField from './EnvironmentVariablesField'; import GuideCard, { CardType } from './GuideCard'; @@ -29,7 +29,7 @@ type Props = { function InstructionTab({ isActive }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { watch } = useFormContext<JwtClaimsFormType>(); + const { watch } = useFormContext<JwtCustomizerForm>(); const tokenType = watch('tokenType'); return ( diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestResult.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/TestResult.tsx similarity index 100% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestResult.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/TestResult.tsx diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestTab.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/TestTab.tsx similarity index 92% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestTab.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/TestTab.tsx index 95dc6a8fd..931be97a0 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/TestTab.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/TestTab.tsx @@ -11,14 +11,14 @@ import Button from '@/ds-components/Button'; import Card from '@/ds-components/Card'; import useApi from '@/hooks/use-api'; -import MonacoCodeEditor, { type ModelControl, type ModelSettings } from '../MonacoCodeEditor'; -import { type JwtClaimsFormType } from '../type'; +import MonacoCodeEditor, { type ModelControl, type ModelSettings } from '../../MonacoCodeEditor'; +import { type JwtCustomizerForm } from '../../type'; import { accessTokenPayloadTestModel, clientCredentialsPayloadTestModel, userContextTestModel, -} from '../utils/config'; -import { formatFormDataToTestRequestPayload } from '../utils/format'; +} from '../../utils/config'; +import { formatFormDataToTestRequestPayload } from '../../utils/format'; import TestResult, { type TestResultData } from './TestResult'; import * as styles from './index.module.scss'; @@ -45,7 +45,7 @@ function TestTab({ isActive }: Props) { const [activeModelName, setActiveModelName] = useState<string>(); const api = useApi({ hideErrorToast: true }); - const { watch, control, formState, getValues } = useFormContext<JwtClaimsFormType>(); + const { watch, control, formState, getValues } = useFormContext<JwtCustomizerForm>(); const tokenType = watch('tokenType'); const editorModels = useMemo( @@ -99,13 +99,13 @@ function TestTab({ isActive }: Props) { }, [api, getValues]); const getModelControllerProps = useCallback( - ({ value, onChange }: ControllerRenderProps<JwtClaimsFormType, 'testSample'>): ModelControl => { + ({ value, onChange }: ControllerRenderProps<JwtCustomizerForm, 'testSample'>): ModelControl => { return { value: - activeModelName === userContextTestModel.name ? value?.contextSample : value?.tokenSample, + activeModelName === userContextTestModel.name ? value.contextSample : value.tokenSample, onChange: (newValue: string | undefined) => { // Form value is a object we need to update the specific field - const updatedValue: JwtClaimsFormType['testSample'] = { + const updatedValue: JwtCustomizerForm['testSample'] = { ...value, ...conditional( activeModelName === userContextTestModel.name && { @@ -133,11 +133,7 @@ function TestTab({ isActive }: Props) { ); const validateSampleCode = useCallback( - (value: JwtClaimsFormType['testSample']) => { - if (!value) { - return true; - } - + (value: JwtCustomizerForm['testSample']) => { for (const [_, sampleCode] of Object.entries(value)) { if (sampleCode) { try { diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/index.module.scss similarity index 100% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.module.scss rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/index.module.scss diff --git a/packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/index.tsx similarity index 100% rename from packages/console/src/pages/CustomizeJwtDetails/SettingsSection/index.tsx rename to packages/console/src/pages/CustomizeJwtDetails/MainContent/SettingsSection/index.tsx diff --git a/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.module.scss new file mode 100644 index 000000000..e6da1aa29 --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.module.scss @@ -0,0 +1,17 @@ +@use '@/scss/underscore' as _; + + +.content { + display: flex; + flex-direction: row; + flex-grow: 1; + + > * { + flex: 1; + margin-bottom: _.unit(6); + + &:first-child { + margin-right: _.unit(3); + } + } +} diff --git a/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.tsx new file mode 100644 index 000000000..483fbbe25 --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/MainContent/index.tsx @@ -0,0 +1,97 @@ +import { type LogtoJwtTokenPath } from '@logto/schemas'; +import classNames from 'classnames'; +import { FormProvider, useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom'; +import { type KeyedMutator } from 'swr'; + +import SubmitFormChangesActionBar from '@/components/SubmitFormChangesActionBar'; +import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; +import useApi from '@/hooks/use-api'; +import { trySubmitSafe } from '@/utils/form'; + +import { type Action, type JwtCustomizer, type JwtCustomizerForm } from '../type'; +import { formatFormDataToRequestData, formatResponseDataToFormData } from '../utils/format'; +import { getApiPath } from '../utils/path'; + +import ScriptSection from './ScriptSection'; +import SettingsSection from './SettingsSection'; +import * as styles from './index.module.scss'; + +type Props<T extends LogtoJwtTokenPath> = { + className?: string; + token: T; + data?: JwtCustomizer<T>; + mutate: KeyedMutator<JwtCustomizer<T>>; + action: Action; +}; + +function MainContent<T extends LogtoJwtTokenPath>({ + className, + token, + data, + mutate, + action, +}: Props<T>) { + const api = useApi(); + const navigate = useNavigate(); + + const methods = useForm<JwtCustomizerForm>({ + defaultValues: formatResponseDataToFormData(token, data), + }); + + const { + formState: { isDirty, isSubmitting }, + reset, + handleSubmit, + } = methods; + + const onSubmitHandler = handleSubmit( + trySubmitSafe(async (data) => { + if (isSubmitting) { + return; + } + + const { tokenType } = data; + const payload = formatFormDataToRequestData(data); + + await api.put(getApiPath(tokenType), { json: payload }); + + if (action === 'create') { + navigate(-1); + return; + } + + const result = await mutate(); + + reset(formatResponseDataToFormData(tokenType, result)); + }) + ); + + return ( + <> + <FormProvider {...methods}> + <form className={classNames(styles.content, className)}> + <ScriptSection /> + <SettingsSection /> + </form> + </FormProvider> + <SubmitFormChangesActionBar + // Always show the action bar if is the create mode + isOpen={isDirty || action === 'create'} + isSubmitting={isSubmitting} + onDiscard={ + // If the form is in create mode, navigate back to the previous page + action === 'create' + ? () => { + navigate(-1); + } + : reset + } + onSubmit={onSubmitHandler} + /> + <UnsavedChangesAlertModal hasUnsavedChanges={isDirty && !isSubmitting} onConfirm={reset} /> + </> + ); +} + +export default MainContent; diff --git a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts index 3906b9189..fbf03cecb 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts +++ b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/config.ts @@ -17,7 +17,6 @@ export const defaultOptions: EditorProps['options'] = { minimap: { enabled: false, }, - wordWrap: 'on', renderLineHighlight: 'none', fontFamily: 'Roboto Mono, monospace', fontSize: 14, diff --git a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx index 485048dc3..377810106 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/index.tsx @@ -1,4 +1,4 @@ -import { Editor, type BeforeMount, type OnMount, useMonaco } from '@monaco-editor/react'; +import { Editor, useMonaco, type BeforeMount, type OnMount } from '@monaco-editor/react'; import { type Nullable } from '@silverhand/essentials'; import classNames from 'classnames'; import { useCallback, useEffect, useMemo, useRef } from 'react'; @@ -6,16 +6,15 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; import CopyToClipboard from '@/ds-components/CopyToClipboard'; import { onKeyDownHandler } from '@/utils/a11y'; -import CodeClearButton from './ActionButton/CodeClearButton.js'; import CodeRestoreButton from './ActionButton/CodeRestoreButton.js'; -import { logtoDarkTheme, defaultOptions } from './config.js'; +import { defaultOptions, logtoDarkTheme } from './config.js'; import * as styles from './index.module.scss'; import type { IStandaloneCodeEditor, ModelSettings } from './type.js'; import useEditorHeight from './use-editor-height.js'; -export type { ModelSettings, ModelControl } from './type.js'; +export type { ModelControl, ModelSettings } from './type.js'; -type ActionButtonType = 'clear' | 'restore' | 'copy'; +type ActionButtonType = 'restore' | 'copy'; type Props = { className?: string; @@ -136,15 +135,6 @@ function MonacoCodeEditor({ ))} </div> <div className={styles.actionButtons}> - {enabledActions.includes('clear') && ( - <CodeClearButton - onClick={() => { - if (activeModel) { - onChange?.(undefined); - } - }} - /> - )} {enabledActions.includes('restore') && ( <CodeRestoreButton onClick={() => { diff --git a/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss index df0f8cec6..8986a550e 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss +++ b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.module.scss @@ -1,5 +1,20 @@ @use '@/scss/underscore' as _; +.content { + display: flex; + flex-direction: row; + flex-grow: 1; + + > * { + flex: 1; + margin-bottom: _.unit(6); + + &:first-child { + margin-right: _.unit(3); + } + } +} + .blockShimmer { @include _.shimmering-animation; border-radius: 8px; diff --git a/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx index cefdb11fa..b100710d6 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/PageLoadingSkeleton/index.tsx @@ -3,8 +3,6 @@ import classNames from 'classnames'; import Card from '@/ds-components/Card'; -import * as pageLayoutStyles from '../index.module.scss'; - import * as styles from './index.module.scss'; type Props = { @@ -13,11 +11,8 @@ type Props = { function PageLoadingSkeleton({ tokenType }: Props) { return ( - <div className={pageLayoutStyles.tabContent}> - <Card className={pageLayoutStyles.codePanel}> - <div className={classNames(styles.textShimmer, styles.title)} /> - <div className={styles.blockShimmer} /> - </Card> + <div className={styles.content}> + <div className={styles.blockShimmer} /> <div> <div className={classNames(styles.textShimmer, styles.large)} /> <Card className={styles.card}> diff --git a/packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx b/packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx deleted file mode 100644 index 6a4912a60..000000000 --- a/packages/console/src/pages/CustomizeJwtDetails/ScriptSection.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* Code Editor for the custom JWT claims script. */ -import { LogtoJwtTokenPath } from '@logto/schemas'; -import { useMemo, useContext, useCallback } from 'react'; -import { useFormContext, Controller, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; - -import Card from '@/ds-components/Card'; - -import { CodeEditorLoadingContext } from './CodeEditorLoadingContext'; -import MonacoCodeEditor, { type ModelSettings } from './MonacoCodeEditor'; -import * as styles from './index.module.scss'; -import { type JwtClaimsFormType } from './type'; -import { accessTokenJwtCustomizerModel, clientCredentialsModel } from './utils/config'; -import { buildEnvironmentVariablesTypeDefinition } from './utils/type-definitions'; - -const titlePhrases = Object.freeze({ - [LogtoJwtTokenPath.AccessToken]: 'user_jwt', - [LogtoJwtTokenPath.ClientCredentials]: 'machine_to_machine_jwt', -}); - -function ScriptSection() { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - const { watch, control } = useFormContext<JwtClaimsFormType>(); - - const tokenType = watch('tokenType'); - - // Need to use useWatch hook to subscribe the mutation of the environmentVariables field - // Otherwise, the default watch function's return value won't mutate when the environmentVariables field changes - const envVariables = useWatch({ - control, - name: 'environmentVariables', - }); - - const environmentVariablesTypeDefinition = useMemo( - () => buildEnvironmentVariablesTypeDefinition(envVariables), - [envVariables] - ); - - const { setIsMonacoLoaded } = useContext(CodeEditorLoadingContext); - - const activeModel = useMemo<ModelSettings>( - () => - tokenType === LogtoJwtTokenPath.AccessToken - ? accessTokenJwtCustomizerModel - : clientCredentialsModel, - [tokenType] - ); - - const onMountHandler = useCallback(() => { - setIsMonacoLoaded(true); - }, [setIsMonacoLoaded]); - - return ( - <Card className={styles.codePanel}> - <div className={styles.cardTitle}> - {t('jwt_claims.code_editor_title', { - token: t(`jwt_claims.${titlePhrases[tokenType]}`), - })} - </div> - <Controller - // Force rerender the controller when the token type changes - // Otherwise the input field will not be updated - key={tokenType} - control={control} - name="script" - render={({ field: { onChange, value }, formState: { defaultValues } }) => ( - <MonacoCodeEditor - className={styles.flexGrow} - enabledActions={['clear', 'copy']} - models={[activeModel]} - activeModelName={activeModel.name} - value={value} - environmentVariablesDefinition={environmentVariablesTypeDefinition} - onChange={(newValue) => { - // If the value is the same as the default code and the original form script value is undefined, reset the value to undefined as well - if (newValue === activeModel.defaultValue && !defaultValues?.script) { - onChange(''); - return; - } - - // Input value should not be undefined for react-hook-form @see https://react-hook-form.com/docs/usecontroller/controller - onChange(newValue ?? ''); - }} - onMountHandler={onMountHandler} - /> - )} - /> - </Card> - ); -} - -export default ScriptSection; diff --git a/packages/console/src/pages/CustomizeJwtDetails/index.module.scss b/packages/console/src/pages/CustomizeJwtDetails/index.module.scss index 0f019347f..225d3c08a 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/index.module.scss +++ b/packages/console/src/pages/CustomizeJwtDetails/index.module.scss @@ -10,41 +10,7 @@ margin-bottom: _.unit(4); } - .tabNav { - margin-bottom: _.unit(4); + .hidden { + display: none; } } - -.tabContent { - display: flex; - flex-direction: row; - flex-grow: 1; - - > * { - flex: 1; - margin-bottom: _.unit(6); - - &:first-child { - margin-right: _.unit(3); - } - } -} - -.codePanel { - position: relative; - display: flex; - flex-direction: column; - - .cardTitle { - font: var(--font-label-2); - margin-bottom: _.unit(3); - } - - .flexGrow { - flex-grow: 1; - } -} - -.hidden { - display: none; -} diff --git a/packages/console/src/pages/CustomizeJwtDetails/index.tsx b/packages/console/src/pages/CustomizeJwtDetails/index.tsx index 92c4fc8e0..47f91704e 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/index.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/index.tsx @@ -1,22 +1,26 @@ import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact'; import { type LogtoJwtTokenPath } from '@logto/schemas'; import { useMemo, useState } from 'react'; +import { useParams } from 'react-router-dom'; -import CardTitle from '@/ds-components/CardTitle'; +import DetailsPage from '@/components/DetailsPage'; +import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import { CodeEditorLoadingContext } from './CodeEditorLoadingContext'; -import Main from './Main'; +import MainContent from './MainContent'; import PageLoadingSkeleton from './PageLoadingSkeleton'; import * as styles from './index.module.scss'; -import useJwtCustomizer from './use-jwt-customizer'; +import { pageParamsGuard, type Action } from './type'; +import useDataFetch from './use-data-fetch'; type Props = { tokenType: LogtoJwtTokenPath; - action: 'create' | 'edit'; + action: Action; }; -function CustomizeJwtDetails({ tokenType }: Props) { - const { isLoading, ...rest } = useJwtCustomizer(); +function CustomizeJwtDetails({ tokenType, action }: Props) { + const { isLoading, error, ...rest } = useDataFetch(tokenType, action); + const [isMonacoLoaded, setIsMonacoLoaded] = useState(false); const codeEditorContextValue = useMemo( @@ -25,22 +29,38 @@ function CustomizeJwtDetails({ tokenType }: Props) { ); return ( - <div className={styles.container}> - <CardTitle - title="jwt_claims.title" - subtitle="jwt_claims.description" - className={styles.header} - /> + <DetailsPage + backLink="/customize-jwt" + backLinkTitle="jwt_claims.title" + className={styles.container} + > {(isLoading || !isMonacoLoaded) && <PageLoadingSkeleton tokenType={tokenType} />} {!isLoading && ( <CodeEditorLoadingContext.Provider value={codeEditorContextValue}> - <Main tab={tokenType} {...rest} className={isMonacoLoaded ? undefined : styles.hidden} /> + <MainContent + action={action} + token={tokenType} + {...rest} + className={isMonacoLoaded ? undefined : styles.hidden} + /> </CodeEditorLoadingContext.Provider> )} - </div> + </DetailsPage> ); } -// eslint-disable-next-line import/no-unused-modules -- will update this later -export default withAppInsights(CustomizeJwtDetails); +// Guard the parameters to ensure they are valid +function CustomizeJwtDetailsWrapper() { + const { tokenType, action } = useParams(); + + const params = pageParamsGuard.safeParse({ tokenType, action }); + + if (!params.success) { + return <EmptyDataPlaceholder />; + } + + return <CustomizeJwtDetails tokenType={params.data.tokenType} action={params.data.action} />; +} + +export default withAppInsights(CustomizeJwtDetailsWrapper); diff --git a/packages/console/src/pages/CustomizeJwtDetails/type.ts b/packages/console/src/pages/CustomizeJwtDetails/type.ts index a4f0aa187..b52599ba7 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/type.ts +++ b/packages/console/src/pages/CustomizeJwtDetails/type.ts @@ -1,11 +1,24 @@ -import type { LogtoJwtTokenPath } from '@logto/schemas'; +import type { AccessTokenJwtCustomizer, ClientCredentialsJwtCustomizer } from '@logto/schemas'; +import { LogtoJwtTokenPath } from '@logto/schemas'; +import { z } from 'zod'; -export type JwtClaimsFormType = { +export type JwtCustomizerForm = { tokenType: LogtoJwtTokenPath; - script?: string; + script: string; environmentVariables?: Array<{ key: string; value: string }>; - testSample?: { + testSample: { contextSample?: string; tokenSample?: string; }; }; + +export type Action = 'create' | 'edit'; + +export type JwtCustomizer<T extends LogtoJwtTokenPath> = T extends LogtoJwtTokenPath.AccessToken + ? AccessTokenJwtCustomizer + : ClientCredentialsJwtCustomizer; + +export const pageParamsGuard = z.object({ + tokenType: z.nativeEnum(LogtoJwtTokenPath), + action: z.union([z.literal('create'), z.literal('edit')]), +}); diff --git a/packages/console/src/pages/CustomizeJwtDetails/use-data-fetch.ts b/packages/console/src/pages/CustomizeJwtDetails/use-data-fetch.ts new file mode 100644 index 000000000..abb51bc4c --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/use-data-fetch.ts @@ -0,0 +1,35 @@ +import { type LogtoJwtTokenPath } from '@logto/schemas'; +import { type ResponseError } from '@withtyped/client'; +import useSWR from 'swr'; + +import useApi from '@/hooks/use-api'; +import useSwrFetcher from '@/hooks/use-swr-fetcher'; +import { shouldRetryOnError } from '@/utils/request'; + +import { type Action, type JwtCustomizer } from './type'; +import { getApiPath } from './utils/path'; + +const useDataFetch = <T extends LogtoJwtTokenPath>(tokenType: T, action: Action) => { + const apiPath = getApiPath(tokenType); + const fetchApi = useApi({ hideErrorToast: true }); + const fetcher = useSwrFetcher<JwtCustomizer<T>>(fetchApi); + + // Return undefined if action is create + const { isLoading, data, mutate, error } = useSWR<JwtCustomizer<T>, ResponseError>( + action === 'create' ? undefined : apiPath, + { + fetcher, + shouldRetryOnError: shouldRetryOnError({ ignore: [404] }), + } + ); + + return { + // Show global loading status only if any of the fetchers are loading and no errors are present + isLoading: isLoading && !error, + data, + mutate, + error, + }; +}; + +export default useDataFetch; diff --git a/packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts b/packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts deleted file mode 100644 index 4bd3ab65e..000000000 --- a/packages/console/src/pages/CustomizeJwtDetails/use-jwt-customizer.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - LogtoJwtTokenPath, - type AccessTokenJwtCustomizer, - type ClientCredentialsJwtCustomizer, -} from '@logto/schemas'; -import { type ResponseError } from '@withtyped/client'; -import { useMemo } from 'react'; -import useSWR from 'swr'; - -import useApi from '@/hooks/use-api'; -import useSwrFetcher from '@/hooks/use-swr-fetcher'; -import { shouldRetryOnError } from '@/utils/request'; - -import { getApiPath } from './utils/format'; - -function useJwtCustomizer() { - const fetchApi = useApi({ hideErrorToast: true }); - const accessTokenFetcher = useSwrFetcher<AccessTokenJwtCustomizer>(fetchApi); - const clientCredentialsFetcher = useSwrFetcher<ClientCredentialsJwtCustomizer>(fetchApi); - - const { - data: accessTokenJwtCustomizer, - mutate: mutateAccessTokenJwtCustomizer, - isLoading: isAccessTokenJwtDataLoading, - error: accessTokenError, - } = useSWR<AccessTokenJwtCustomizer, ResponseError>(getApiPath(LogtoJwtTokenPath.AccessToken), { - fetcher: accessTokenFetcher, - shouldRetryOnError: shouldRetryOnError({ ignore: [404] }), - }); - - const { - data: clientCredentialsJwtCustomizer, - mutate: mutateClientCredentialsJwtCustomizer, - isLoading: isClientCredentialsJwtDataLoading, - error: clientCredentialsError, - } = useSWR<ClientCredentialsJwtCustomizer, ResponseError>( - getApiPath(LogtoJwtTokenPath.ClientCredentials), - { - fetcher: clientCredentialsFetcher, - shouldRetryOnError: shouldRetryOnError({ ignore: [404] }), - } - ); - - // Show global loading status only if any of the fetchers are loading and no errors are present - const isLoading = - (isAccessTokenJwtDataLoading && !accessTokenError) || - (isClientCredentialsJwtDataLoading && !clientCredentialsError); - - return useMemo( - () => ({ - accessTokenJwtCustomizer, - clientCredentialsJwtCustomizer, - isLoading, - mutateAccessTokenJwtCustomizer, - mutateClientCredentialsJwtCustomizer, - }), - [ - accessTokenJwtCustomizer, - clientCredentialsJwtCustomizer, - isLoading, - mutateAccessTokenJwtCustomizer, - mutateClientCredentialsJwtCustomizer, - ] - ); -} - -export default useJwtCustomizer; diff --git a/packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx b/packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx index ac49ff535..bfafec17d 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx +++ b/packages/console/src/pages/CustomizeJwtDetails/utils/config.tsx @@ -71,7 +71,7 @@ declare global { export { exports as default }; `; -const defaultAccessTokenJwtCustomizerCode = `/** +export const defaultAccessTokenJwtCustomizerCode = `/** * This function is called to get custom claims for the JWT token. * * @param {${JwtCustomizerTypeDefinitionKey.AccessTokenPayload}} token -The JWT token. @@ -86,7 +86,7 @@ exports.getCustomJwtClaims = async (token, data) => { return {}; }`; -const defaultClientCredentialsJwtCustomizerCode = `/** +export const defaultClientCredentialsJwtCustomizerCode = `/** * This function is called to get custom claims for the JWT token. * * @param {${JwtCustomizerTypeDefinitionKey.ClientCredentialsPayload}} token -The JWT token. @@ -101,7 +101,7 @@ exports.getCustomJwtClaims = async (token) => { export const accessTokenJwtCustomizerModel: ModelSettings = { name: 'user-jwt.ts', - title: 'TypeScript', + title: 'User access token', language: 'typescript', defaultValue: defaultAccessTokenJwtCustomizerCode, extraLibs: [ @@ -118,7 +118,7 @@ export const accessTokenJwtCustomizerModel: ModelSettings = { export const clientCredentialsModel: ModelSettings = { name: 'machine-to-machine-jwt.ts', - title: 'TypeScript', + title: 'Machine-to-machine token', language: 'typescript', defaultValue: defaultClientCredentialsJwtCustomizerCode, extraLibs: [ diff --git a/packages/console/src/pages/CustomizeJwtDetails/utils/format.ts b/packages/console/src/pages/CustomizeJwtDetails/utils/format.ts index f6f33abfb..9e8e76899 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/utils/format.ts +++ b/packages/console/src/pages/CustomizeJwtDetails/utils/format.ts @@ -1,13 +1,11 @@ -import { - LogtoJwtTokenPath, - type AccessTokenJwtCustomizer, - type ClientCredentialsJwtCustomizer, -} from '@logto/schemas'; +import { LogtoJwtTokenPath, type AccessTokenJwtCustomizer } from '@logto/schemas'; -import type { JwtClaimsFormType } from '../type'; +import type { JwtCustomizer, JwtCustomizerForm } from '../type'; import { + defaultAccessTokenJwtCustomizerCode, defaultAccessTokenPayload, + defaultClientCredentialsJwtCustomizerCode, defaultClientCredentialsPayload, defaultUserTokenContextData, } from './config'; @@ -22,35 +20,9 @@ const formatEnvVariablesResponseToFormData = ( return Object.entries(enVariables).map(([key, value]) => ({ key, value })); }; -const formatSampleCodeJsonToString = (sampleJson?: AccessTokenJwtCustomizer['contextSample']) => { - if (!sampleJson) { - return; - } - - return JSON.stringify(sampleJson, null, 2); -}; - -export const formatResponseDataToFormData = <T extends LogtoJwtTokenPath>( - tokenType: T, - data?: T extends LogtoJwtTokenPath.AccessToken - ? AccessTokenJwtCustomizer - : ClientCredentialsJwtCustomizer -): JwtClaimsFormType => { - return { - script: data?.script ?? '', // React-hook-form won't mutate the value if it's undefined - tokenType, - environmentVariables: formatEnvVariablesResponseToFormData(data?.envVars) ?? [ - { key: '', value: '' }, - ], - testSample: { - tokenSample: formatSampleCodeJsonToString(data?.tokenSample), - // Technically, contextSample is always undefined for client credentials token type - contextSample: formatSampleCodeJsonToString(data?.contextSample), - }, - }; -}; - -const formatEnvVariablesFormData = (envVariables: JwtClaimsFormType['environmentVariables']) => { +const formatEnvVariablesFormDataToRequest = ( + envVariables: JwtCustomizerForm['environmentVariables'] +) => { if (!envVariables) { return; } @@ -64,6 +36,14 @@ const formatEnvVariablesFormData = (envVariables: JwtClaimsFormType['environment return Object.fromEntries(entries.map(({ key, value }) => [key, value])); }; +const formatSampleCodeJsonToString = (sampleJson?: AccessTokenJwtCustomizer['contextSample']) => { + if (!sampleJson) { + return; + } + + return JSON.stringify(sampleJson, null, 2); +}; + const formatSampleCodeStringToJson = (sampleCode?: string) => { if (!sampleCode) { return; @@ -75,14 +55,46 @@ const formatSampleCodeStringToJson = (sampleCode?: string) => { } catch {} }; -export const formatFormDataToRequestData = (data: JwtClaimsFormType) => { +const defaultValues = Object.freeze({ + [LogtoJwtTokenPath.AccessToken]: { + script: defaultAccessTokenJwtCustomizerCode, + tokenSample: defaultAccessTokenPayload, + contextSample: defaultUserTokenContextData, + }, + [LogtoJwtTokenPath.ClientCredentials]: { + script: defaultClientCredentialsJwtCustomizerCode, + tokenSample: defaultClientCredentialsPayload, + contextSample: undefined, + }, +}); + +export const formatResponseDataToFormData = <T extends LogtoJwtTokenPath>( + tokenType: T, + data?: JwtCustomizer<T> +): JwtCustomizerForm => { return { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- parse empty string as undefined - script: data.script || undefined, - envVars: formatEnvVariablesFormData(data.environmentVariables), - tokenSample: formatSampleCodeStringToJson(data.testSample?.tokenSample), - // Technically, contextSample is always undefined for client credentials token type - contextSample: formatSampleCodeStringToJson(data.testSample?.contextSample), + tokenType, + script: data?.script ?? defaultValues[tokenType].script, + environmentVariables: formatEnvVariablesResponseToFormData(data?.envVars) ?? [ + { key: '', value: '' }, + ], + testSample: { + tokenSample: formatSampleCodeJsonToString( + data?.tokenSample ?? defaultValues[tokenType].tokenSample + ), + contextSample: formatSampleCodeJsonToString( + data?.contextSample ?? defaultValues[tokenType].contextSample + ), + }, + }; +}; + +export const formatFormDataToRequestData = (data: JwtCustomizerForm) => { + return { + script: data.script, + envVars: formatEnvVariablesFormDataToRequest(data.environmentVariables), + tokenSample: formatSampleCodeStringToJson(data.testSample.tokenSample), + contextSample: formatSampleCodeStringToJson(data.testSample.contextSample), }; }; @@ -91,27 +103,18 @@ export const formatFormDataToTestRequestPayload = ({ script, environmentVariables, testSample, -}: JwtClaimsFormType) => { - const defaultTokenSample = - tokenType === LogtoJwtTokenPath.AccessToken - ? defaultAccessTokenPayload - : defaultClientCredentialsPayload; - - const defaultContextSample = - tokenType === LogtoJwtTokenPath.AccessToken ? defaultUserTokenContextData : undefined; - +}: JwtCustomizerForm) => { return { tokenType, payload: { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- parse empty string as undefined - script: script || undefined, - envVars: formatEnvVariablesFormData(environmentVariables), - tokenSample: formatSampleCodeStringToJson(testSample?.tokenSample) ?? defaultTokenSample, + script, + envVars: formatEnvVariablesFormDataToRequest(environmentVariables), + tokenSample: + formatSampleCodeStringToJson(testSample.tokenSample) ?? + defaultValues[tokenType].tokenSample, contextSample: - formatSampleCodeStringToJson(testSample?.contextSample) ?? defaultContextSample, + formatSampleCodeStringToJson(testSample.contextSample) ?? + defaultValues[tokenType].contextSample, }, }; }; - -export const getApiPath = (tokenType: LogtoJwtTokenPath) => - `api/configs/jwt-customizer/${tokenType}`; diff --git a/packages/console/src/pages/CustomizeJwtDetails/utils/path.ts b/packages/console/src/pages/CustomizeJwtDetails/utils/path.ts new file mode 100644 index 000000000..aa4477e53 --- /dev/null +++ b/packages/console/src/pages/CustomizeJwtDetails/utils/path.ts @@ -0,0 +1,4 @@ +import { type LogtoJwtTokenPath } from '@logto/schemas'; + +export const getApiPath = (tokenType: LogtoJwtTokenPath) => + `api/configs/jwt-customizer/${tokenType}`; diff --git a/packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts b/packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts index 630468315..a5e3fe8f8 100644 --- a/packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts +++ b/packages/console/src/pages/CustomizeJwtDetails/utils/type-definitions.ts @@ -1,11 +1,11 @@ import { JwtCustomizerTypeDefinitionKey, accessTokenPayloadTypeDefinition, - jwtCustomizerUserContextTypeDefinition, clientCredentialsPayloadTypeDefinition, + jwtCustomizerUserContextTypeDefinition, } from '@/consts/jwt-customizer-type-definition'; -import { type JwtClaimsFormType } from '../type'; +import { type JwtCustomizerForm } from '../type'; export { JwtCustomizerTypeDefinitionKey, @@ -24,7 +24,7 @@ export const buildClientCredentialsJwtCustomizerContextTsDefinition = () => `declare ${clientCredentialsPayloadTypeDefinition}`; export const buildEnvironmentVariablesTypeDefinition = ( - envVariables?: JwtClaimsFormType['environmentVariables'] + envVariables?: JwtCustomizerForm['environmentVariables'] ) => { const typeDefinition = envVariables ? `{ diff --git a/packages/phrases/src/locales/de/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/de/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/de/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/de/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/de/translation/admin-console/tabs.ts b/packages/phrases/src/locales/de/translation/admin-console/tabs.ts index 1cdcbfc21..6924fbc0d 100644 --- a/packages/phrases/src/locales/de/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/de/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Einstellungen', mfa: 'Multi-Faktor-Authentifizierung', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Signierschlüssel', organization_template: 'Organisationstemplate', }; diff --git a/packages/phrases/src/locales/en/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/en/translation/admin-console/jwt-claims.ts index cbb38a36c..2cedb36c7 100644 --- a/packages/phrases/src/locales/en/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/en/translation/admin-console/jwt-claims.ts @@ -1,5 +1,5 @@ const jwt_claims = { - title: 'JWT claims', + title: 'Custom JWT', description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', user_jwt: { diff --git a/packages/phrases/src/locales/en/translation/admin-console/tabs.ts b/packages/phrases/src/locales/en/translation/admin-console/tabs.ts index 05176456a..0000f788c 100644 --- a/packages/phrases/src/locales/en/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/en/translation/admin-console/tabs.ts @@ -14,7 +14,7 @@ const tabs = { docs: 'Docs', tenant_settings: 'Settings', mfa: 'Multi-factor auth', - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Signing keys', organization_template: 'Organization template', }; diff --git a/packages/phrases/src/locales/es/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/es/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/es/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/es/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/es/translation/admin-console/tabs.ts b/packages/phrases/src/locales/es/translation/admin-console/tabs.ts index dfd37b30d..cd4218156 100644 --- a/packages/phrases/src/locales/es/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/es/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Configuraciones del inquilino', mfa: 'Autenticación multifactor', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Claves de firma', organization_template: 'Plantilla de organización', }; diff --git a/packages/phrases/src/locales/fr/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/fr/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/fr/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/fr/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/fr/translation/admin-console/tabs.ts b/packages/phrases/src/locales/fr/translation/admin-console/tabs.ts index 3f708cecb..f5934376d 100644 --- a/packages/phrases/src/locales/fr/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/fr/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Paramètres du locataire', mfa: 'Authentification multi-facteur', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Clés de signature', organization_template: "Modèle d'organisation", }; diff --git a/packages/phrases/src/locales/it/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/it/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/it/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/it/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/it/translation/admin-console/tabs.ts b/packages/phrases/src/locales/it/translation/admin-console/tabs.ts index f2e3fd937..7bfabb42d 100644 --- a/packages/phrases/src/locales/it/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/it/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Impostazioni', mfa: 'Autenticazione multi-fattore', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Chiavi di firma', organization_template: 'Modello di organizzazione', }; diff --git a/packages/phrases/src/locales/ja/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/ja/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/ja/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/ja/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/ja/translation/admin-console/tabs.ts b/packages/phrases/src/locales/ja/translation/admin-console/tabs.ts index 0ae76dc2f..bcc8f098f 100644 --- a/packages/phrases/src/locales/ja/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/ja/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: '設定', mfa: 'Multi-factor auth', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: '署名キー', organization_template: '組織テンプレート', }; diff --git a/packages/phrases/src/locales/ko/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/ko/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/ko/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/ko/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/ko/translation/admin-console/tabs.ts b/packages/phrases/src/locales/ko/translation/admin-console/tabs.ts index a1b7c3867..173312316 100644 --- a/packages/phrases/src/locales/ko/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/ko/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: '테넌트 설정', mfa: '다중 요소 인증', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: '서명 키', organization_template: '조직 템플릿', }; diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/pl-pl/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/tabs.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/tabs.ts index f78b8c422..1ecbd96c6 100644 --- a/packages/phrases/src/locales/pl-pl/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Ustawienia', mfa: 'Multi-factor auth', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Klucze do podpisu', organization_template: 'Szablon organizacji', }; diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/pt-br/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/pt-br/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/tabs.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/tabs.ts index da0a6b130..043196801 100644 --- a/packages/phrases/src/locales/pt-br/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/pt-br/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Configurações', mfa: 'Autenticação de multi-fator', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Chaves de assinatura', organization_template: 'Modelo de organização', }; diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/pt-pt/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/tabs.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/tabs.ts index 5160cd383..6b5cebe4e 100644 --- a/packages/phrases/src/locales/pt-pt/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Definições do inquilino', mfa: 'Autenticação multi-fator', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Chaves de assinatura', organization_template: 'Modelo de organização', }; diff --git a/packages/phrases/src/locales/ru/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/ru/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/ru/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/ru/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/ru/translation/admin-console/tabs.ts b/packages/phrases/src/locales/ru/translation/admin-console/tabs.ts index 795488f95..61c881c5a 100644 --- a/packages/phrases/src/locales/ru/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/ru/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Настройки', mfa: 'Multi-factor auth', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'Ключи подписи', organization_template: 'Шаблон организации', }; diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/tr-tr/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/tabs.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/tabs.ts index 8eaa11913..767355695 100644 --- a/packages/phrases/src/locales/tr-tr/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: 'Ayarlar', mfa: 'Çoklu faktörlü kimlik doğrulama', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: 'İmza anahtarları', organization_template: 'Kuruluş şablonu', }; diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/zh-cn/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/tabs.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/tabs.ts index c4e0446b8..0cab45285 100644 --- a/packages/phrases/src/locales/zh-cn/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: '租户设置', mfa: '多因素认证', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: '签名密钥', organization_template: '组织模板', }; diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/zh-hk/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/tabs.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/tabs.ts index 63290d16c..d79ad4d8d 100644 --- a/packages/phrases/src/locales/zh-hk/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: '租戶設置', mfa: '多重認證', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: '簽署密鑰', organization_template: '組織模板', }; diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/jwt-claims.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/jwt-claims.ts index 9aa5ff9d8..9714214c4 100644 --- a/packages/phrases/src/locales/zh-tw/translation/admin-console/jwt-claims.ts +++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/jwt-claims.ts @@ -1,6 +1,6 @@ const jwt_claims = { /** UNTRANSLATED */ - title: 'JWT claims', + title: 'Custom JWT', /** UNTRANSLATED */ description: 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/tabs.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/tabs.ts index f139b3bdc..091000283 100644 --- a/packages/phrases/src/locales/zh-tw/translation/admin-console/tabs.ts +++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/tabs.ts @@ -15,7 +15,7 @@ const tabs = { tenant_settings: '租戶設定', mfa: '多重認證', /** UNTRANSLATED */ - jwt_customizer: 'JWT Claims', + customize_jwt: 'JWT Claims', signing_keys: '簽署密鑰', organization_template: '組織模板', }; From 59ffb43cf9d7ff5e8c2ffc4e54f87daea36304f3 Mon Sep 17 00:00:00 2001 From: simeng-li <simeng@silverhand.io> Date: Fri, 29 Mar 2024 10:49:55 +0800 Subject: [PATCH 3/3] refactor(console): remove unused element remove unused element --- .../ActionButton/CodeClearButton.tsx | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx diff --git a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx b/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx deleted file mode 100644 index 7a08a129b..000000000 --- a/packages/console/src/pages/CustomizeJwtDetails/MonacoCodeEditor/ActionButton/CodeClearButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useTranslation } from 'react-i18next'; - -import ClearIcon from '@/assets/icons/clear.svg'; - -import ActionButton from './index'; - -type Props = { - onClick: () => void; -}; - -function CodeClearButton({ onClick }: Props) { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - return ( - <ActionButton - actionTip={t('jwt_claims.clear')} - actionSuccessTip={t('jwt_claims.cleared')} - icon={<ClearIcon />} - onClick={onClick} - /> - ); -} - -export default CodeClearButton;