diff --git a/superset-frontend/src/components/ReportModal/index.tsx b/superset-frontend/src/components/ReportModal/index.tsx index 8fbc50f01133..9e817787718f 100644 --- a/superset-frontend/src/components/ReportModal/index.tsx +++ b/superset-frontend/src/components/ReportModal/index.tsx @@ -18,28 +18,39 @@ */ import React, { useState, + // useEffect, useCallback, useReducer, Reducer, FunctionComponent, } from 'react'; -import { styled, css, t } from '@superset-ui/core'; +import { t } from '@superset-ui/core'; +// import { useSingleViewResource } from 'src/views/CRUD/hooks'; + +import { bindActionCreators } from 'redux'; +import { connect, useDispatch } from 'react-redux'; +import { addReport } from 'src/reports/actions/reportState'; import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput'; import Icons from 'src/components/Icons'; -import Modal from 'src/components/Modal'; +import withToasts from 'src/messageToasts/enhancers/withToasts'; import { CronPicker, CronError } from 'src/components/CronPicker'; - -interface ReportProps { - onHide: () => {}; - show: boolean; - props: any; -} +import { + StyledModal, + StyledTopSection, + StyledBottomSection, + StyledIconWrapper, + StyledScheduleTitle, + StyledCronError, + noBottomMargin, + StyledFooterButton, +} from './styles'; interface ReportObject { active: boolean; crontab: string; - dashboard: number; + dashboard?: number; + chart?: number; description?: string; log_retention: number; name: string; @@ -50,6 +61,22 @@ interface ReportObject { validator_config_json: {} | null; validator_type: string; working_timeout: number; + creation_method: string; +} + +interface ReportProps { + addDangerToast: (msg: string) => void; + addSuccessToast: (msg: string) => void; + addReport: (report?: ReportObject) => {}; + onHide: () => {}; + onReportAdd: (report?: ReportObject) => {}; + show: boolean; + userId: number; + userEmail: string; + dashboardId?: number; + chartId?: number; + creationMethod: string; + props: any; } enum ActionType { @@ -94,47 +121,12 @@ const reportReducer = ( } }; -const StyledModal = styled(Modal)` - .ant-modal-body { - padding: 0; - } -`; - -const StyledTopSection = styled.div` - padding: ${({ theme }) => theme.gridUnit * 4}px; -`; - -const StyledBottomSection = styled.div` - border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - padding: ${({ theme }) => theme.gridUnit * 4}px; -`; - -const StyledIconWrapper = styled.span` - span { - margin-right: ${({ theme }) => theme.gridUnit * 2}px; - vertical-align: middle; - } - .text { - vertical-align: middle; - } -`; - -const StyledScheduleTitle = styled.div` - margin-bottom: ${({ theme }) => theme.gridUnit * 7}px; -`; - -const StyledCronError = styled.p` - color: ${({ theme }) => theme.colors.error.base}; -`; - -const noBottomMargin = css` - margin-bottom: 0; -`; - const ReportModal: FunctionComponent = ({ - show = false, + // addDangerToast, + onReportAdd, onHide, - props, + show = false, + ...props }) => { const [currentReport, setCurrentReport] = useReducer< Reducer | null, ReportActionType> @@ -143,6 +135,59 @@ const ReportModal: FunctionComponent = ({ setCurrentReport({ type, payload } as ReportActionType); }, []); const [error, setError] = useState(); + // ---------- comments on lines 21, 28, 125, 139-159 & 182 are being held for edit functionality + // const [hasConnectedReport, setHasConnectedReport] = useState(false); + // const [isLoading, setLoading] = useState(false); + const dispatch = useDispatch(); + + // Report fetch logic + // const { + // state: { resource }, + // } = useSingleViewResource( + // 'report', + // t('report'), + // addDangerToast, + // ); + + // useEffect(() => { + // if (resource?.dashboard) { + // setHasConnectedReport(true); + // } + // }, [resource?.dashboard]); + + const onClose = () => { + // setLoading(false); + onHide(); + }; + + const onSave = async () => { + // Create new Report + const newReportValues: Partial = { + crontab: currentReport?.crontab, + dashboard: props.props.dashboardId, + chart: props.props.chartId, + description: currentReport?.description, + name: currentReport?.name || 'Weekly Report', + owners: [props.props.userId], + recipients: [ + { + recipient_config_json: { target: props.props.userEmail }, + type: 'Email', + }, + ], + type: 'Report', + creation_method: props.props.creationMethod, + }; + + // setLoading(true); + await dispatch(addReport(newReportValues as ReportObject)); + + if (onReportAdd) { + onReportAdd(); + } + + onClose(); + }; const wrappedTitle = ( @@ -151,8 +196,26 @@ const ReportModal: FunctionComponent = ({ ); + const renderModalFooter = ( + <> + + Cancel + + + Add + + + ); + return ( - + = ({ -

Schedule

+

Schedule

Scheduled reports will be sent to your email as a PNG

@@ -217,4 +280,7 @@ const ReportModal: FunctionComponent = ({ ); }; -export default ReportModal; +const mapDispatchToProps = (dispatch: any) => + bindActionCreators({ addReport }, dispatch); + +export default connect(null, mapDispatchToProps)(withToasts(ReportModal)); diff --git a/superset-frontend/src/components/ReportModal/styles.tsx b/superset-frontend/src/components/ReportModal/styles.tsx new file mode 100644 index 000000000000..08fe73e2edc1 --- /dev/null +++ b/superset-frontend/src/components/ReportModal/styles.tsx @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { styled, css } from '@superset-ui/core'; +import Modal from 'src/components/Modal'; +import Button from 'src/components/Button'; + +export const StyledModal = styled(Modal)` + .ant-modal-body { + padding: 0; + } +`; + +export const StyledTopSection = styled.div` + padding: ${({ theme }) => theme.gridUnit * 4}px; +`; + +export const StyledBottomSection = styled.div` + border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + padding: ${({ theme }) => theme.gridUnit * 4}px; +`; + +export const StyledIconWrapper = styled.span` + span { + margin-right: ${({ theme }) => theme.gridUnit * 2}px; + vertical-align: middle; + } + .text { + vertical-align: middle; + } +`; + +export const StyledScheduleTitle = styled.div` + margin-bottom: ${({ theme }) => theme.gridUnit * 7}px; +`; + +export const StyledCronError = styled.p` + color: ${({ theme }) => theme.colors.error.base}; +`; + +export const noBottomMargin = css` + margin-bottom: 0; +`; + +export const StyledFooterButton = styled(Button)` + width: ${({ theme }) => theme.gridUnit * 40}px; +`; diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index 70ac4db56f04..f2bc8d22a8b1 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -603,6 +603,7 @@ class Header extends React.PureComponent { userId: user.userId, userEmail: user.email, dashboardId: dashboardInfo.id, + creationMethod: 'dashboards', }} /> )} diff --git a/superset-frontend/src/explore/components/ExploreChartHeader.jsx b/superset-frontend/src/explore/components/ExploreChartHeader.jsx index e33f8e20a732..aeca9cdf2b15 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader.jsx @@ -280,10 +280,25 @@ export class ExploreChartHeader extends React.PureComponent { isRunning={chartStatus === 'loading'} status={CHART_STATUS_MAP[chartStatus]} /> + + + {this.canAddReports() && this.renderReportModal()} dispatch => { + SupersetClient.post({ + endpoint: `/api/v1/report/`, + jsonPayload: report, + }) + .then(() => { + dispatch({ type: ADD_REPORT, report }); + }) + .catch(() => + dispatch( + addDangerToast(t('An error occurred while creating this report.')), + ), + ); +}; export const EDIT_REPORT = 'EDIT_REPORT'; export function reportEditor(report) { diff --git a/superset-frontend/src/reports/reducers/reportState.js b/superset-frontend/src/reports/reducers/reportState.js index 5946dbd8b059..6493e0ead4be 100644 --- a/superset-frontend/src/reports/reducers/reportState.js +++ b/superset-frontend/src/reports/reducers/reportState.js @@ -18,7 +18,12 @@ */ /* eslint-disable camelcase */ import { alterInArr } from 'src/reduxUtils'; -import { SET_REPORT, EDIT_REPORT, TOGGLE_ACTIVE } from '../actions/reportState'; +import { + SET_REPORT, + ADD_REPORT, + EDIT_REPORT, + TOGGLE_ACTIVE, +} from '../actions/reportState'; export default function reportStateReducer(state = {}, action) { const actionHandlers = { @@ -28,6 +33,12 @@ export default function reportStateReducer(state = {}, action) { report: action.report, }; }, + [ADD_REPORT]() { + return { + ...state, + report: action.report, + }; + }, [EDIT_REPORT]() { return { ...state,