import { partial } from 'lodash'; import React, { useEffect, useState } from 'react'; import { DeepMap, FieldError, useForm } from 'react-hook-form'; import { useSelector } from 'react-redux'; import { locationUtil, SelectableValue } from '@grafana/data'; import { config, locationService, reportInteraction } from '@grafana/runtime'; import { Alert, Button, Field, InputControl, Modal, RadioButtonGroup } from '@grafana/ui'; import { DashboardPicker } from 'app/core/components/Select/DashboardPicker'; import { removeDashboardToFetchFromLocalStorage } from 'app/features/dashboard/state/initDashboard'; import { ExploreId } from 'app/types'; import { getExploreItemSelector } from '../state/selectors'; import { setDashboardInLocalStorage, AddToDashboardError } from './addToDashboard'; enum SaveTarget { NewDashboard = 'new-dashboard', ExistingDashboard = 'existing-dashboard', } const SAVE_TARGETS: Array> = [ { label: 'New dashboard', value: SaveTarget.NewDashboard, }, { label: 'Existing dashboard', value: SaveTarget.ExistingDashboard, }, ]; interface SaveTargetDTO { saveTarget: SaveTarget; } interface SaveToNewDashboardDTO extends SaveTargetDTO { saveTarget: SaveTarget.NewDashboard; } interface SaveToExistingDashboard extends SaveTargetDTO { saveTarget: SaveTarget.ExistingDashboard; dashboardUid: string; } type FormDTO = SaveToNewDashboardDTO | SaveToExistingDashboard; function assertIsSaveToExistingDashboardError( errors: DeepMap ): asserts errors is DeepMap { // the shape of the errors object is always compatible with the type above, but we need to // explicitly assert its type so that TS can narrow down FormDTO to SaveToExistingDashboard // when we use it in the form. } function getDashboardURL(dashboardUid?: string) { return dashboardUid ? `d/${dashboardUid}` : 'dashboard/new'; } enum GenericError { UNKNOWN = 'unknown-error', NAVIGATION = 'navigation-error', } interface SubmissionError { error: AddToDashboardError | GenericError; message: string; } interface Props { onClose: () => void; exploreId: ExploreId; } export const AddToDashboardModal = ({ onClose, exploreId }: Props) => { const exploreItem = useSelector(getExploreItemSelector(exploreId))!; const [submissionError, setSubmissionError] = useState(); const { handleSubmit, control, formState: { errors }, watch, } = useForm({ defaultValues: { saveTarget: SaveTarget.NewDashboard }, }); const saveTarget = watch('saveTarget'); const onSubmit = async (openInNewTab: boolean, data: FormDTO) => { setSubmissionError(undefined); const dashboardUid = data.saveTarget === SaveTarget.ExistingDashboard ? data.dashboardUid : undefined; reportInteraction('e2d_submit', { newTab: openInNewTab, saveTarget: data.saveTarget, queries: exploreItem.queries.length, }); try { await setDashboardInLocalStorage({ dashboardUid, datasource: exploreItem.datasourceInstance?.getRef(), queries: exploreItem.queries, queryResponse: exploreItem.queryResponse, }); } catch (error) { switch (error) { case AddToDashboardError.FETCH_DASHBOARD: setSubmissionError({ error, message: 'Could not fetch dashboard information. Please try again.' }); break; case AddToDashboardError.SET_DASHBOARD_LS: setSubmissionError({ error, message: 'Could not add panel to dashboard. Please try again.' }); break; default: setSubmissionError({ error: GenericError.UNKNOWN, message: 'Something went wrong. Please try again.' }); } return; } const dashboardURL = getDashboardURL(dashboardUid); if (!openInNewTab) { onClose(); locationService.push(locationUtil.stripBaseFromUrl(dashboardURL)); return; } const didTabOpen = !!global.open(config.appUrl + dashboardURL, '_blank'); if (!didTabOpen) { setSubmissionError({ error: GenericError.NAVIGATION, message: 'Could not navigate to the selected dashboard. Please try again.', }); removeDashboardToFetchFromLocalStorage(); return; } onClose(); }; useEffect(() => { reportInteraction('e2d_open'); }, []); return (
( )} name="saveTarget" /> {saveTarget === SaveTarget.ExistingDashboard && (() => { assertIsSaveToExistingDashboardError(errors); return ( ( onChange(d?.uid)} /> )} control={control} name="dashboardUid" shouldUnregister rules={{ required: { value: true, message: 'This field is required.' } }} /> ); })()} {submissionError && ( {submissionError.message} )}
); };