From c1301ddbe4f2c0089a0dba19e8e04aedc69467be Mon Sep 17 00:00:00 2001 From: simeng-li Date: Fri, 19 Apr 2024 16:55:21 +0800 Subject: [PATCH 1/2] refactor(console): clean up the global useConfirmModal provider clean up the global useConfirmModal provider --- .../AppConfirmModalProvider/index.tsx | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/packages/console/src/contexts/AppConfirmModalProvider/index.tsx b/packages/console/src/contexts/AppConfirmModalProvider/index.tsx index 94ea8c16794..3fc7f5c8271 100644 --- a/packages/console/src/contexts/AppConfirmModalProvider/index.tsx +++ b/packages/console/src/contexts/AppConfirmModalProvider/index.tsx @@ -5,27 +5,22 @@ import { createContext, useCallback, useEffect, useMemo, useRef, useState } from import type { ConfirmModalProps } from '@/ds-components/ConfirmModal'; import ConfirmModal from '@/ds-components/ConfirmModal'; -type ModalContentRenderProps = { - confirm: (data?: unknown) => void; - cancel: (data?: unknown) => void; -}; - type ConfirmModalType = 'alert' | 'confirm'; type ConfirmModalState = Omit< ConfirmModalProps, 'onCancel' | 'onConfirm' | 'children' | 'isLoading' > & { - ModalContent: string | ((props: ModalContentRenderProps) => Nullable); + ModalContent: string | (() => Nullable); type: ConfirmModalType; }; -type AppConfirmModalProps = Omit & { +type ShowConfirmModalProps = Omit & { type?: ConfirmModalType; }; type ConfirmModalContextType = { - show: (props: AppConfirmModalProps) => Promise<[boolean, unknown?]>; + show: (props: ShowConfirmModalProps) => Promise<[boolean, unknown?]>; confirm: (data?: unknown) => void; cancel: (data?: unknown) => void; }; @@ -51,7 +46,7 @@ function AppConfirmModalProvider({ children }: Props) { const resolver = useRef<(value: [result: boolean, data?: unknown]) => void>(); - const handleShow = useCallback(async ({ type = 'confirm', ...props }: AppConfirmModalProps) => { + const handleShow = useCallback(async ({ type = 'confirm', ...props }: ShowConfirmModalProps) => { resolver.current?.([false]); setModalState({ @@ -109,15 +104,9 @@ function AppConfirmModalProvider({ children }: Props) { { - handleCancel(); - }} + onCancel={handleCancel} > - {typeof ModalContent === 'string' ? ( - ModalContent - ) : ( - - )} + {typeof ModalContent === 'string' ? ModalContent : } ); From 142e8a5db7c30d77c894b79c1d83ebdbd383967e Mon Sep 17 00:00:00 2001 From: simeng-li Date: Fri, 19 Apr 2024 17:21:18 +0800 Subject: [PATCH 2/2] refactor(console): implement new jwt customizer delete modal implement new jwt customizer delete modal --- .../CustomizeJwt/CustomizerItem/index.tsx | 32 ++-------- .../pages/CustomizeJwt/DeleteConfirmModal.tsx | 58 +++++++++++++++++++ .../console/src/pages/CustomizeJwt/index.tsx | 25 +++++++- 3 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 packages/console/src/pages/CustomizeJwt/DeleteConfirmModal.tsx diff --git a/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx b/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx index dfb7522e114..05c19a8436b 100644 --- a/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx +++ b/packages/console/src/pages/CustomizeJwt/CustomizerItem/index.tsx @@ -1,45 +1,23 @@ import { LogtoJwtTokenKeyType } from '@logto/schemas'; -import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import DeleteIcon from '@/assets/icons/delete.svg'; 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 useJwtCustomizer from '../use-jwt-customizer'; +import { getPagePath } from '@/pages/CustomizeJwt/utils/path'; import * as styles from './index.module.scss'; type Props = { readonly tokenType: LogtoJwtTokenKeyType; + readonly onDelete: (token: LogtoJwtTokenKeyType) => void; }; -function CustomizerItem({ tokenType }: Props) { +function CustomizerItem({ tokenType, onDelete }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const apiLink = getApiPath(tokenType); const editLink = getPagePath(tokenType, 'edit'); const { navigate } = useTenantPathname(); - const { show } = useConfirmModal(); - const { mutate } = useJwtCustomizer(); - - const api = useApi(); - - const onDelete = useCallback(async () => { - const [confirm] = await show({ - title: 'jwt_claims.delete_modal_title', - ModalContent: t('jwt_claims.delete_modal_content'), - confirmButtonText: 'general.delete', - }); - - if (confirm) { - await api.delete(apiLink); - await mutate(); - } - }, [api, apiLink, mutate, show, t]); return (
@@ -67,7 +45,9 @@ function CustomizerItem({ tokenType }: Props) { type="text" size="small" title="general.delete" - onClick={onDelete} + onClick={() => { + onDelete(tokenType); + }} />
diff --git a/packages/console/src/pages/CustomizeJwt/DeleteConfirmModal.tsx b/packages/console/src/pages/CustomizeJwt/DeleteConfirmModal.tsx new file mode 100644 index 00000000000..4910c8f54ec --- /dev/null +++ b/packages/console/src/pages/CustomizeJwt/DeleteConfirmModal.tsx @@ -0,0 +1,58 @@ +import { type LogtoJwtTokenKeyType } from '@logto/schemas'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSWRConfig } from 'swr'; + +import ConfirmModal from '@/ds-components/ConfirmModal'; +import useApi from '@/hooks/use-api'; +import { getApiPath } from '@/pages/CustomizeJwt/utils/path'; + +type Props = { + readonly isOpen: boolean; + readonly tokenType?: LogtoJwtTokenKeyType; + readonly onCancel: () => void; +}; + +function DeleteConfirmModal({ isOpen, tokenType, onCancel }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const [loading, setLoading] = useState(false); + const { mutate } = useSWRConfig(); + + const api = useApi(); + const apiLink = tokenType && getApiPath(tokenType); + + const onDelete = useCallback(async () => { + // If no token type is provided, dismiss the modal + if (!apiLink) { + onCancel(); + return; + } + + setLoading(true); + + try { + // Delete the JWT customizer + await api.delete(apiLink); + // Mutate the SWR cache + await mutate(getApiPath()); + } finally { + setLoading(false); + onCancel(); + } + }, [api, apiLink, mutate, onCancel]); + + return ( + + {t('jwt_claims.delete_modal_content')} + + ); +} + +export default DeleteConfirmModal; diff --git a/packages/console/src/pages/CustomizeJwt/index.tsx b/packages/console/src/pages/CustomizeJwt/index.tsx index 3377fc8427a..39152786824 100644 --- a/packages/console/src/pages/CustomizeJwt/index.tsx +++ b/packages/console/src/pages/CustomizeJwt/index.tsx @@ -1,4 +1,5 @@ import { LogtoJwtTokenKeyType } from '@logto/schemas'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import FormCard, { FormCardSkeleton } from '@/components/FormCard'; @@ -7,12 +8,19 @@ import FormField from '@/ds-components/FormField'; import CreateButton from './CreateButton'; import CustomizerItem from './CustomizerItem'; +import DeleteConfirmModal from './DeleteConfirmModal'; import * as styles from './index.module.scss'; import useJwtCustomizer from './use-jwt-customizer'; function CustomizeJwt() { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const [deleteModalTokenType, setDeleteModalTokenType] = useState(); + + const onDeleteHandler = useCallback((tokenType: LogtoJwtTokenKeyType) => { + setDeleteModalTokenType(tokenType); + }, []); + const { isLoading, accessTokenJwtCustomizer, clientCredentialsJwtCustomizer } = useJwtCustomizer(); @@ -38,7 +46,10 @@ function CustomizeJwt() { {t('jwt_claims.user_jwt.card_description')} {accessTokenJwtCustomizer ? ( - + ) : ( )} @@ -50,7 +61,10 @@ function CustomizeJwt() { {t('jwt_claims.machine_to_machine_jwt.card_description')} {clientCredentialsJwtCustomizer ? ( - + ) : ( )} @@ -59,6 +73,13 @@ function CustomizeJwt() { )} + { + setDeleteModalTokenType(undefined); + }} + /> ); }