import { partial } from 'lodash'; import { ReactElement, useEffect, useState } from 'react'; import { Controller, DeepMap, FieldError, FieldErrors, useForm } from 'react-hook-form'; import { SelectableValue, TimeRange } from '@grafana/data'; import { reportInteraction } from '@grafana/runtime'; import { Panel } from '@grafana/schema'; import { Alert, Button, Field, Modal, RadioButtonGroup } from '@grafana/ui'; import { DashboardPicker } from 'app/core/components/Select/DashboardPicker'; import { contextSrv } from 'app/core/core'; import { AccessControlAction } from 'app/types'; import { addToDashboard, SubmissionError } from './addToDashboard'; enum SaveTarget { NewDashboard = 'new-dashboard', ExistingDashboard = 'existing-dashboard', } interface SaveTargetDTO { saveTarget: SaveTarget; } interface SaveToNewDashboardDTO extends SaveTargetDTO { saveTarget: SaveTarget.NewDashboard; } interface SaveToExistingDashboard extends SaveTargetDTO { saveTarget: SaveTarget.ExistingDashboard; dashboardUid: string; } type FormDTO = SaveToNewDashboardDTO | SaveToExistingDashboard; export interface Props { onClose: () => void; buildPanel: (options: TOptions) => Panel; timeRange?: TimeRange; options: TOptions; children?: React.ReactNode; } export function AddToDashboardForm({ onClose, buildPanel, timeRange, options, children, }: Props): ReactElement { const [submissionError, setSubmissionError] = useState(); const { handleSubmit, control, formState: { errors }, watch, } = useForm({ defaultValues: { saveTarget: SaveTarget.NewDashboard }, }); const canCreateDashboard = contextSrv.hasPermission(AccessControlAction.DashboardsCreate); const canWriteDashboard = contextSrv.hasPermission(AccessControlAction.DashboardsWrite); const saveTargets: Array> = []; if (canCreateDashboard) { saveTargets.push({ label: 'New dashboard', value: SaveTarget.NewDashboard, }); } if (canWriteDashboard) { saveTargets.push({ label: 'Existing dashboard', value: SaveTarget.ExistingDashboard, }); } const saveTarget = saveTargets.length > 1 ? watch('saveTarget') : saveTargets[0].value; const onSubmit = (openInNewTab: boolean, data: FormDTO) => { setSubmissionError(undefined); const dashboardUid = data.saveTarget === SaveTarget.ExistingDashboard ? data.dashboardUid : undefined; const panel = buildPanel(options); reportInteraction('e_2_d_submit', { newTab: openInNewTab, saveTarget: data.saveTarget, queries: panel.targets, }); const error = addToDashboard({ dashboardUid, panel, openInNewTab, timeRange }); if (error) { setSubmissionError(error); return; } onClose(); }; useEffect(() => { reportInteraction('e_2_d_open'); }, []); return (
{/* For custom form options */} {children} {saveTargets.length > 1 && ( ( )} 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} )} ); } function assertIsSaveToExistingDashboardError( errors: FieldErrors ): 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. }