diff --git a/superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx b/superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx index a5225e8ef4d39..be9e0af16f2c1 100644 --- a/superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx +++ b/superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx @@ -29,7 +29,7 @@ export interface LabeledErrorBoundInputProps { validationMethods: | { onBlur: (value: any) => void } | { onChange: (value: any) => void }; - errorMessage: string | null; + errorMessage?: string | null; helpText?: string; required?: boolean; hasTooltip?: boolean; diff --git a/superset-frontend/src/components/ReportModal/index.tsx b/superset-frontend/src/components/ReportModal/index.tsx index 0963c0fe9f4e7..c8273bd985801 100644 --- a/superset-frontend/src/components/ReportModal/index.tsx +++ b/superset-frontend/src/components/ReportModal/index.tsx @@ -31,6 +31,7 @@ import { connect, useDispatch, useSelector } from 'react-redux'; import { addReport, editReport } from 'src/reports/actions/reports'; import { AlertObject } from 'src/views/CRUD/alert/types'; +import Alert from 'src/components/Alert'; import TimezoneSelector from 'src/components/TimezoneSelector'; import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput'; import Icons from 'src/components/Icons'; @@ -38,6 +39,7 @@ import withToasts from 'src/components/MessageToasts/withToasts'; import { CronError } from 'src/components/CronPicker'; import { RadioChangeEvent } from 'src/components'; import { + antDErrorAlertStyles, StyledModal, StyledTopSection, StyledBottomSection, @@ -73,7 +75,7 @@ export interface ReportObject { working_timeout: number; creation_method: string; force_screenshot: boolean; - error?: string; + error: string; } interface ChartObject { @@ -93,6 +95,7 @@ interface ReportProps { addReport: (report?: ReportObject) => {}; onHide: () => {}; onReportAdd: (report?: ReportObject) => {}; + addDangerToast: (msg: string) => void; show: boolean; userId: number; userEmail: string; @@ -128,9 +131,7 @@ type ReportActionType = } | { type: ActionType.error; - payload: { - name: string[]; - }; + payload: { name: string[] }; }; const TEXT_BASED_VISUALIZATION_TYPES = [ @@ -146,6 +147,10 @@ const NOTIFICATION_FORMATS = { CSV: 'CSV', }; +const defaultErrorMsg = t( + 'We were unable to create your report. Please try again.', +); + const reportReducer = ( state: Partial | null, action: ReportActionType, @@ -171,7 +176,7 @@ const reportReducer = ( case ActionType.error: return { ...state, - error: action.payload.name[0], + error: action.payload?.name[0] || defaultErrorMsg, }; default: return state; @@ -250,9 +255,8 @@ const ReportModal: FunctionComponent = ({ await dispatch(addReport(newReportValues as ReportObject)); onHide(); } catch (e) { - const parsedError = await getClientErrorObject(e); - const errorMessage = parsedError.message; - onReducerChange(ActionType.error, errorMessage); + const { message } = await getClientErrorObject(e); + onReducerChange(ActionType.error, message); } } @@ -315,6 +319,15 @@ const ReportModal: FunctionComponent = ({ ); + const errorAlert = () => ( + antDErrorAlertStyles(theme)} + message={t('Report Creation Error')} + description={currentReport?.error} + /> + ); + return ( = ({ value: target.value, }), }} - errorMessage={currentReport?.error || ''} label="Report Name" data-test="report-name-test" /> - = ({ value: target.value, }), }} - errorMessage="" label={t('Description')} placeholder={t( 'Include a description that will be sent with your report', @@ -401,6 +411,7 @@ const ReportModal: FunctionComponent = ({ /> {isChart && renderMessageContentSection} + {currentReport?.error && errorAlert()} ); }; diff --git a/superset-frontend/src/components/ReportModal/styles.tsx b/superset-frontend/src/components/ReportModal/styles.tsx index cd68b271ebb4e..e34d60bb96dfa 100644 --- a/superset-frontend/src/components/ReportModal/styles.tsx +++ b/superset-frontend/src/components/ReportModal/styles.tsx @@ -111,3 +111,24 @@ export const StyledRadio = styled(Radio)` export const StyledRadioGroup = styled(Radio.Group)` margin-left: ${({ theme }) => theme.gridUnit * 0.5}px; `; + +export const antDErrorAlertStyles = (theme: SupersetTheme) => css` + border: ${theme.colors.error.base} 1px solid; + padding: ${theme.gridUnit * 4}px; + margin: ${theme.gridUnit * 8}px ${theme.gridUnit * 4}px; + color: ${theme.colors.error.dark2}; + .ant-alert-message { + font-size: ${theme.typography.sizes.s + 1}px; + font-weight: bold; + } + .ant-alert-description { + font-size: ${theme.typography.sizes.s + 1}px; + line-height: ${theme.gridUnit * 4}px; + .ant-alert-icon { + margin-right: ${theme.gridUnit * 2.5}px; + font-size: ${theme.typography.sizes.l + 1}px; + position: relative; + top: ${theme.gridUnit / 4}px; + } + } +`;