Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 6 additions & 17 deletions packages/console/src/contexts/AppConfirmModalProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand All @@ -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({
Expand Down Expand Up @@ -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>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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}>
Expand Down Expand Up @@ -67,7 +45,9 @@ function CustomizerItem({ tokenType }: Props) {
type="text"
size="small"
title="general.delete"
onClick={onDelete}
onClick={() => {
onDelete(tokenType);
}}
/>
</div>
</div>
Expand Down
58 changes: 58 additions & 0 deletions packages/console/src/pages/CustomizeJwt/DeleteConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -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;
25 changes: 23 additions & 2 deletions packages/console/src/pages/CustomizeJwt/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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();

Expand All @@ -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} />
)}
Expand All @@ -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} />
)}
Expand All @@ -59,6 +73,13 @@ function CustomizeJwt() {
</>
)}
</div>
<DeleteConfirmModal
isOpen={!!deleteModalTokenType}
tokenType={deleteModalTokenType}
onCancel={() => {
setDeleteModalTokenType(undefined);
}}
/>
</main>
);
}
Expand Down
Loading