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

fix(console): reset form as soon as JWT customizer is created #5612

Merged
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LogtoJwtTokenPath } from '@logto/schemas';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useSWRConfig } from 'swr';

import DeletIcon from '@/assets/icons/delete.svg';
import EditIcon from '@/assets/icons/edit.svg';
Expand All @@ -11,6 +10,8 @@ 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 * as styles from './index.module.scss';

type Props = {
Expand All @@ -23,7 +24,7 @@ function CustomizerItem({ tokenType }: Props) {
const editLink = getPagePath(tokenType, 'edit');
const { navigate } = useTenantPathname();
const { show } = useConfirmModal();
const { mutate } = useSWRConfig();
const { mutate } = useJwtCustomizer();

const api = useApi();

Expand All @@ -36,7 +37,7 @@ function CustomizerItem({ tokenType }: Props) {

if (confirm) {
await api.delete(apiLink);
await mutate(apiLink, undefined);
await mutate();
}
}, [api, apiLink, mutate, show, t]);

Expand Down
53 changes: 18 additions & 35 deletions packages/console/src/pages/CustomizeJwt/use-jwt-customizer.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,40 @@
import {
LogtoJwtTokenPath,
type AccessTokenJwtCustomizer,
type ClientCredentialsJwtCustomizer,
} from '@logto/schemas';
import { LogtoJwtTokenKey, type JwtCustomizerConfigs } 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/path';

function useJwtCustomizer() {
const fetchApi = useApi({ hideErrorToast: true });
const accessTokenFetcher = useSwrFetcher<AccessTokenJwtCustomizer>(fetchApi);
const clientCredentialsFetcher = useSwrFetcher<ClientCredentialsJwtCustomizer>(fetchApi);

const fetcher = useSwrFetcher<JwtCustomizerConfigs[]>(fetchApi);
const {
data: accessTokenJwtCustomizer,
isLoading: isAccessTokenJwtDataLoading,
error: accessTokenError,
} = useSWR<AccessTokenJwtCustomizer, ResponseError>(getApiPath(LogtoJwtTokenPath.AccessToken), {
fetcher: accessTokenFetcher,
shouldRetryOnError: shouldRetryOnError({ ignore: [404] }),
data,
isLoading: isDataLoading,
error,
mutate,
} = useSWR<JwtCustomizerConfigs[], ResponseError>(getApiPath(), {
fetcher,
});
const isLoading = isDataLoading && !error;

const {
data: clientCredentialsJwtCustomizer,
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(() => {
const { value: accessTokenJwtCustomizer } =
data?.find(({ key }) => key === LogtoJwtTokenKey.AccessToken) ?? {};
const { value: clientCredentialsJwtCustomizer } =
data?.find(({ key }) => key === LogtoJwtTokenKey.ClientCredentials) ?? {};

return useMemo(
() => ({
return {
accessTokenJwtCustomizer,
clientCredentialsJwtCustomizer,
isLoading,
}),
[accessTokenJwtCustomizer, clientCredentialsJwtCustomizer, isLoading]
);
mutate,
};
}, [data, isLoading, mutate]);
}

export default useJwtCustomizer;
9 changes: 7 additions & 2 deletions packages/console/src/pages/CustomizeJwt/utils/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { type LogtoJwtTokenPath } from '@logto/schemas';

import { type Action } from './type';

export const getApiPath = (tokenType: LogtoJwtTokenPath) =>
`api/configs/jwt-customizer/${tokenType}`;
export const getApiPath = (tokenType?: LogtoJwtTokenPath) => {
if (!tokenType) {
return 'api/configs/jwt-customizer';
}

return `api/configs/jwt-customizer/${tokenType}`;
};

export const getPagePath = (tokenType?: LogtoJwtTokenPath, action?: Action) => {
if (!tokenType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import useApi from '@/hooks/use-api';
import { trySubmitSafe } from '@/utils/form';

import useJwtCustomizer from '../../CustomizeJwt/use-jwt-customizer';
import { type Action, type JwtCustomizer, type JwtCustomizerForm } from '../type';
import { formatFormDataToRequestData, formatResponseDataToFormData } from '../utils/format';
import { getApiPath } from '../utils/path';
Expand All @@ -34,6 +35,7 @@ function MainContent<T extends LogtoJwtTokenPath>({
}: Props<T>) {
const api = useApi();
const navigate = useNavigate();
const { mutate: mutateJwtCustomizers } = useJwtCustomizer();

const methods = useForm<JwtCustomizerForm>({
defaultValues: formatResponseDataToFormData(token, data),
Expand All @@ -56,14 +58,20 @@ function MainContent<T extends LogtoJwtTokenPath>({

await api.put(getApiPath(tokenType), { json: payload });

if (action === 'create') {
navigate(-1);
return;
}

const result = await mutate();

reset(formatResponseDataToFormData(tokenType, result));

/**
* Should `reset` (to set `isDirty` to false) before navigating back to the custom JWT listing page.
darcyYe marked this conversation as resolved.
Show resolved Hide resolved
* Otherwise, the unsaved changes alert modal will be triggered on clicking `create` button, which
* is not expected.
*/
if (action === 'create') {
// Refresh the JWT customizers list to reflect the latest changes.
await mutateJwtCustomizers();
navigate(-1);
}
})
);

Expand Down
Loading