mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 19:33:02 +03:00
Updated AdminX error handling to display validation errors correctly (#19210)
fixes ADM-44
This commit is contained in:
parent
2809703c76
commit
cc4176f0bf
@ -3,7 +3,7 @@ import {showToast} from '@tryghost/admin-x-design-system';
|
||||
import {useCallback} from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import {useFramework} from '../providers/FrameworkProvider';
|
||||
import {APIError, JSONError, ValidationError} from '../utils/errors';
|
||||
import {APIError, ValidationError} from '../utils/errors';
|
||||
|
||||
/**
|
||||
* Generic error handling for API calls. This is enabled by default for queries (can be disabled by
|
||||
@ -20,8 +20,7 @@ const useHandleError = () => {
|
||||
* so this toast is intended as a worst-case fallback message when we don't know what else to do.
|
||||
*
|
||||
*/
|
||||
type HandleErrorReturnType = void | any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const handleError = useCallback((error: unknown, {withToast = true}: {withToast?: boolean} = {}) : HandleErrorReturnType => {
|
||||
const handleError = useCallback((error: unknown, {withToast = true}: {withToast?: boolean} = {}) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
|
||||
@ -41,10 +40,6 @@ const useHandleError = () => {
|
||||
|
||||
toast.remove();
|
||||
|
||||
if (error instanceof JSONError && error.response?.status === 422) {
|
||||
return error.data;
|
||||
}
|
||||
|
||||
if (error instanceof APIError && error.response?.status === 418) {
|
||||
// We use this status in tests to indicate the API request was not mocked -
|
||||
// don't show a toast because it may block clicking things in the test
|
||||
|
@ -1,13 +1,14 @@
|
||||
import AdvancedThemeSettings from './theme/AdvancedThemeSettings';
|
||||
import InvalidThemeModal from './theme/InvalidThemeModal';
|
||||
import InvalidThemeModal, {FatalErrors} from './theme/InvalidThemeModal';
|
||||
import NiceModal, {NiceModalHandler, useModal} from '@ebay/nice-modal-react';
|
||||
import OfficialThemes from './theme/OfficialThemes';
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import ThemeInstalledModal from './theme/ThemeInstalledModal';
|
||||
import ThemePreview from './theme/ThemePreview';
|
||||
import {Breadcrumbs, Button, ConfirmationModal, FileUpload, LimitModal, Modal, PageHeader, TabView, showToast} from '@tryghost/admin-x-design-system';
|
||||
import {HostLimitError, useLimiter} from '../../../hooks/useLimiter';
|
||||
import {InstalledTheme, Theme, ThemesInstallResponseType, isDefaultOrLegacyTheme, useActivateTheme, useBrowseThemes, useInstallTheme, useUploadTheme} from '@tryghost/admin-x-framework/api/themes';
|
||||
import {JSONError} from '@tryghost/admin-x-framework/errors';
|
||||
import {OfficialTheme} from '../../providers/SettingsAppProvider';
|
||||
import {useHandleError} from '@tryghost/admin-x-framework/hooks';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
@ -61,14 +62,6 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
|
||||
|
||||
const [isUploading, setUploading] = useState(false);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleRetry = () => {
|
||||
if (fileInputRef?.current) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (limiter) {
|
||||
// Sending a bad string to make sure it fails (empty string isn't valid)
|
||||
@ -132,7 +125,7 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
|
||||
onActivate?: () => void
|
||||
}) => {
|
||||
let data: ThemesInstallResponseType | undefined;
|
||||
let fatalErrors = null;
|
||||
let fatalErrors: FatalErrors | null = null;
|
||||
|
||||
try {
|
||||
setUploading(true);
|
||||
@ -140,9 +133,11 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
|
||||
setUploading(false);
|
||||
} catch (e) {
|
||||
setUploading(false);
|
||||
const errorsJson = await handleError(e) as {errors?: []};
|
||||
if (errorsJson?.errors) {
|
||||
fatalErrors = errorsJson.errors;
|
||||
|
||||
if (e instanceof JSONError && e.response?.status === 422 && e.data?.errors) {
|
||||
fatalErrors = (e.data.errors as any) as FatalErrors;
|
||||
} else {
|
||||
handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,7 +150,7 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
|
||||
fatalErrors,
|
||||
onRetry: async (modal) => {
|
||||
modal?.remove();
|
||||
handleRetry();
|
||||
handleUpload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ type FatalError = {
|
||||
};
|
||||
};
|
||||
|
||||
type FatalErrors = FatalError[];
|
||||
export type FatalErrors = FatalError[];
|
||||
|
||||
export const ThemeProblemView = ({problem}:{problem: ThemeProblem}) => {
|
||||
const [isExpanded, setExpanded] = useState(false);
|
||||
@ -63,8 +63,7 @@ const InvalidThemeModal: React.FC<{
|
||||
if (fatalErrors) {
|
||||
warningPrompt = <div className="mt-10">
|
||||
<List title="Errors">
|
||||
{fatalErrors?.map((error: any) => error?.details?.errors?.map((err: any) => <ThemeProblemView problem={err} />
|
||||
))}
|
||||
{fatalErrors?.map(error => error?.details?.errors?.map(err => <ThemeProblemView problem={err} />))}
|
||||
</List>
|
||||
</div>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user