import { css } from '@emotion/css'; import React, { useContext, useEffect, useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { Subscription } from 'rxjs'; import { GrafanaTheme2 } from '@grafana/data/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { reportInteraction } from '@grafana/runtime/src'; import { Alert, Button, ClipboardButton, Field, HorizontalGroup, Input, useStyles2, Spinner, ModalsContext, useForceUpdate, } from '@grafana/ui/src'; import { Layout } from '@grafana/ui/src/components/Layout/Layout'; import { contextSrv } from 'app/core/services/context_srv'; import { useGetPublicDashboardQuery, useCreatePublicDashboardMutation, useUpdatePublicDashboardMutation, } from 'app/features/dashboard/api/publicDashboardApi'; import { AcknowledgeCheckboxes } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes'; import { Configuration } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/Configuration'; import { Description } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/Description'; import { dashboardHasTemplateVariables, generatePublicDashboardUrl, getUnsupportedDashboardDatasources, publicDashboardPersisted, } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils'; import { ShareModalTabProps } from 'app/features/dashboard/components/ShareModal/types'; import { useIsDesktop } from 'app/features/dashboard/utils/screen'; import { DeletePublicDashboardButton } from 'app/features/manage-dashboards/components/PublicDashboardListTable/DeletePublicDashboardButton'; import { isOrgAdmin } from 'app/features/plugins/admin/permissions'; import { AccessControlAction } from 'app/types'; import { DashboardMetaChangedEvent } from '../../../../../types/events'; import { ShareModal } from '../ShareModal'; interface Props extends ShareModalTabProps {} type SharePublicDashboardAcknowledgmentInputs = { publicAcknowledgment: boolean; dataSourcesAcknowledgment: boolean; usageAcknowledgment: boolean; }; export type SharePublicDashboardInputs = { isAnnotationsEnabled: boolean; isTimeRangeEnabled: boolean; enabledSwitch: boolean; } & SharePublicDashboardAcknowledgmentInputs; export const SharePublicDashboard = (props: Props) => { const forceUpdate = useForceUpdate(); const styles = useStyles2(getStyles); const { showModal, hideModal } = useContext(ModalsContext); const isDesktop = useIsDesktop(); const dashboardVariables = props.dashboard.getVariables(); const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; const { hasPublicDashboard } = props.dashboard.meta; const { isLoading: isGetLoading, data: publicDashboard, isError: isGetError, isFetching, } = useGetPublicDashboardQuery(props.dashboard.uid, { // if we don't have a public dashboard, don't try to load public dashboard skip: !hasPublicDashboard, }); const { reset, handleSubmit, watch, register, formState: { dirtyFields }, } = useForm({ defaultValues: { publicAcknowledgment: false, dataSourcesAcknowledgment: false, usageAcknowledgment: false, isAnnotationsEnabled: false, isTimeRangeEnabled: false, enabledSwitch: false, }, }); const [createPublicDashboard, { isLoading: isSaveLoading }] = useCreatePublicDashboardMutation(); const [updatePublicDashboard, { isLoading: isUpdateLoading }] = useUpdatePublicDashboardMutation(); useEffect(() => { const eventSubs = new Subscription(); eventSubs.add(props.dashboard.events.subscribe(DashboardMetaChangedEvent, forceUpdate)); reportInteraction('grafana_dashboards_public_share_viewed'); return () => eventSubs.unsubscribe(); }, [props.dashboard.events, forceUpdate]); useEffect(() => { const isPublicDashboardPersisted = publicDashboardPersisted(publicDashboard); reset({ publicAcknowledgment: isPublicDashboardPersisted, dataSourcesAcknowledgment: isPublicDashboardPersisted, usageAcknowledgment: isPublicDashboardPersisted, isAnnotationsEnabled: publicDashboard?.annotationsEnabled, isTimeRangeEnabled: publicDashboard?.timeSelectionEnabled, enabledSwitch: publicDashboard?.isEnabled, }); }, [publicDashboard, reset]); const isLoading = isGetLoading || isSaveLoading || isUpdateLoading; const hasWritePermissions = contextSrv.hasAccess(AccessControlAction.DashboardsPublicWrite, isOrgAdmin()); const acknowledged = watch('publicAcknowledgment') && watch('dataSourcesAcknowledgment') && watch('usageAcknowledgment'); const isSaveDisabled = useMemo( () => !hasWritePermissions || !acknowledged || props.dashboard.hasUnsavedChanges() || isLoading || isFetching || isGetError || (!publicDashboardPersisted(publicDashboard) && !dirtyFields.enabledSwitch), [ hasWritePermissions, acknowledged, props.dashboard, isLoading, isGetError, publicDashboard, isFetching, dirtyFields.enabledSwitch, ] ); const isDeleteDisabled = isLoading || isFetching || isGetError; const onSavePublicConfig = async (values: SharePublicDashboardInputs) => { reportInteraction('grafana_dashboards_public_create_clicked'); const req = { dashboard: props.dashboard, payload: { ...publicDashboard!, isEnabled: values.enabledSwitch, annotationsEnabled: values.isAnnotationsEnabled, timeSelectionEnabled: values.isTimeRangeEnabled, }, }; // create or update based on whether we have existing uid hasPublicDashboard ? updatePublicDashboard(req) : createPublicDashboard(req); }; const onDismissDelete = () => { showModal(ShareModal, { dashboard: props.dashboard, onDismiss: hideModal, activeTab: 'share', }); }; return ( <>

Welcome to Grafana public dashboards alpha!

{(isGetLoading || isFetching) && }
{getUnsupportedDashboardDatasources(props.dashboard.panels).length > 0 ? (
{`There are datasources in this dashboard that are unsupported for public dashboards. Panels that use these datasources may not function properly: ${getUnsupportedDashboardDatasources( props.dashboard.panels ).join(', ')}. See the `} docs {' '} for supported datasources.
) : null} {dashboardHasTemplateVariables(dashboardVariables) && !publicDashboardPersisted(publicDashboard) ? ( This dashboard cannot be made public because it has template variables ) : (


{publicDashboardPersisted(publicDashboard) && watch('enabledSwitch') && ( generatePublicDashboardUrl(publicDashboard!)} > Copy } /> )} {hasWritePermissions ? ( props.dashboard.hasUnsavedChanges() ? ( ) : ( dashboardHasTemplateVariables(dashboardVariables) && ( ) ) ) : ( )} {publicDashboard && hasWritePermissions && ( Delete public dashboard )} {(isSaveLoading || isFetching) && } )}
); }; const getStyles = (theme: GrafanaTheme2) => ({ content: css` margin: ${theme.spacing(1, 0, 0, 0)}; `, checkboxes: css` margin: ${theme.spacing(2, 0)}; `, timeRange: css` padding: ${theme.spacing(1, 1)}; margin: ${theme.spacing(0, 0, 2, 0)}; `, publicUrl: css` width: 100%; margin-bottom: ${theme.spacing(0, 0, 3, 0)}; `, });