0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-07 23:01:25 -05:00

refactor(console): implement new jwt customizer delete modal (#5765)

* refactor(console): clean up the global useConfirmModal provider

clean up the global useConfirmModal provider

* refactor(console): implement new jwt customizer delete modal

implement new jwt customizer delete modal
This commit is contained in:
simeng-li 2024-04-22 10:16:40 +08:00 committed by GitHub
parent 585ce7413d
commit fcfa2c7d26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 93 additions and 45 deletions

View file

@ -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<JSX.Element>);
ModalContent: string | (() => Nullable<JSX.Element>);
type: ConfirmModalType;
};
type AppConfirmModalProps = Omit<ConfirmModalState, 'isOpen' | 'type'> & {
type ShowConfirmModalProps = Omit<ConfirmModalState, 'isOpen' | 'type'> & {
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) {
<ConfirmModal
{...restProps}
onConfirm={type === 'confirm' ? handleConfirm : undefined}
onCancel={() => {
handleCancel();
}}
onCancel={handleCancel}
>
{typeof ModalContent === 'string' ? (
ModalContent
) : (
<ModalContent confirm={handleConfirm} cancel={handleCancel} />
)}
{typeof ModalContent === 'string' ? ModalContent : <ModalContent />}
</ConfirmModal>
</AppConfirmModalContext.Provider>
);

View file

@ -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 (
<div className={styles.container}>
@ -67,7 +45,9 @@ function CustomizerItem({ tokenType }: Props) {
type="text"
size="small"
title="general.delete"
onClick={onDelete}
onClick={() => {
onDelete(tokenType);
}}
/>
</div>
</div>

View file

@ -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 (
<ConfirmModal
title="jwt_claims.delete_modal_title"
confirmButtonText="general.delete"
isOpen={isOpen}
isLoading={loading}
onConfirm={onDelete}
onCancel={onCancel}
>
{t('jwt_claims.delete_modal_content')}
</ConfirmModal>
);
}
export default DeleteConfirmModal;

View file

@ -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<LogtoJwtTokenKeyType>();
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')}
</div>
{accessTokenJwtCustomizer ? (
<CustomizerItem tokenType={LogtoJwtTokenKeyType.AccessToken} />
<CustomizerItem
tokenType={LogtoJwtTokenKeyType.AccessToken}
onDelete={onDeleteHandler}
/>
) : (
<CreateButton tokenType={LogtoJwtTokenKeyType.AccessToken} />
)}
@ -50,7 +61,10 @@ function CustomizeJwt() {
{t('jwt_claims.machine_to_machine_jwt.card_description')}
</div>
{clientCredentialsJwtCustomizer ? (
<CustomizerItem tokenType={LogtoJwtTokenKeyType.ClientCredentials} />
<CustomizerItem
tokenType={LogtoJwtTokenKeyType.ClientCredentials}
onDelete={onDeleteHandler}
/>
) : (
<CreateButton tokenType={LogtoJwtTokenKeyType.ClientCredentials} />
)}
@ -59,6 +73,13 @@ function CustomizeJwt() {
</>
)}
</div>
<DeleteConfirmModal
isOpen={!!deleteModalTokenType}
tokenType={deleteModalTokenType}
onCancel={() => {
setDeleteModalTokenType(undefined);
}}
/>
</main>
);
}