Files
2024-02-15 17:01:50 +00:00

246 lines
6.7 KiB
TypeScript

import {Fragment} from 'react';
import styled from '@emotion/styled';
import Feature from 'sentry/components/acl/feature';
import FeatureDisabled from 'sentry/components/acl/featureDisabled';
import {Button} from 'sentry/components/button';
import ButtonBar from 'sentry/components/buttonBar';
import Confirm from 'sentry/components/confirm';
import {Hovercard} from 'sentry/components/hovercard';
import {Tooltip} from 'sentry/components/tooltip';
import {IconAdd, IconDownload, IconEdit} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types';
import {trackAnalytics} from 'sentry/utils/analytics';
import {hasDDMFeature} from 'sentry/utils/metrics/features';
import useOrganization from 'sentry/utils/useOrganization';
import {AddWidgetButton} from 'sentry/views/dashboards/addWidget';
import {DataSet} from 'sentry/views/dashboards/widgetBuilder/utils';
import {UNSAVED_FILTERS_MESSAGE} from './detail';
import exportDashboard from './exportDashboard';
import type {DashboardListItem} from './types';
import {DashboardState, MAX_WIDGETS} from './types';
type Props = {
dashboardState: DashboardState;
dashboards: DashboardListItem[];
onAddWidget: (dataset: DataSet) => void;
onCancel: () => void;
onCommit: () => void;
onDelete: () => void;
onEdit: () => void;
organization: Organization;
widgetLimitReached: boolean;
hasUnsavedFilters?: boolean;
};
function Controls({
dashboardState,
dashboards,
hasUnsavedFilters,
widgetLimitReached,
onEdit,
onCommit,
onDelete,
onCancel,
onAddWidget,
}: Props) {
function renderCancelButton(label = t('Cancel')) {
return (
<Button
data-test-id="dashboard-cancel"
size="sm"
onClick={e => {
e.preventDefault();
onCancel();
}}
>
{label}
</Button>
);
}
const organization = useOrganization();
if ([DashboardState.EDIT, DashboardState.PENDING_DELETE].includes(dashboardState)) {
return (
<StyledButtonBar gap={1} key="edit-controls">
{renderCancelButton()}
<Confirm
priority="danger"
message={t('Are you sure you want to delete this dashboard?')}
onConfirm={onDelete}
disabled={dashboards.length <= 1}
>
<Button size="sm" data-test-id="dashboard-delete" priority="danger">
{t('Delete')}
</Button>
</Confirm>
<Button
data-test-id="dashboard-commit"
size="sm"
onClick={e => {
e.preventDefault();
onCommit();
}}
priority="primary"
>
{t('Save and Finish')}
</Button>
</StyledButtonBar>
);
}
if (dashboardState === DashboardState.CREATE) {
return (
<StyledButtonBar gap={1} key="create-controls">
{renderCancelButton()}
<Button
data-test-id="dashboard-commit"
size="sm"
onClick={e => {
e.preventDefault();
onCommit();
}}
priority="primary"
>
{t('Save and Finish')}
</Button>
</StyledButtonBar>
);
}
if (dashboardState === DashboardState.PREVIEW) {
return (
<StyledButtonBar gap={1} key="preview-controls">
{renderCancelButton(t('Go Back'))}
<Button
data-test-id="dashboard-commit"
size="sm"
onClick={e => {
e.preventDefault();
onCommit();
}}
priority="primary"
>
{t('Add Dashboard')}
</Button>
</StyledButtonBar>
);
}
return (
<StyledButtonBar gap={1} key="controls">
<DashboardEditFeature>
{hasFeature => (
<Fragment>
<Feature features="dashboards-import">
<Button
data-test-id="dashboard-export"
onClick={e => {
e.preventDefault();
exportDashboard();
}}
icon={<IconDownload />}
priority="default"
size="sm"
>
{t('Export Dashboard')}
</Button>
</Feature>
<Button
data-test-id="dashboard-edit"
onClick={e => {
e.preventDefault();
onEdit();
}}
icon={<IconEdit />}
disabled={!hasFeature || hasUnsavedFilters}
title={hasUnsavedFilters && UNSAVED_FILTERS_MESSAGE}
priority="default"
size="sm"
>
{t('Edit Dashboard')}
</Button>
{hasFeature ? (
<Tooltip
title={tct('Max widgets ([maxWidgets]) per dashboard reached.', {
maxWidgets: MAX_WIDGETS,
})}
disabled={!widgetLimitReached}
>
{hasDDMFeature(organization) ? (
<AddWidgetButton
onAddWidget={onAddWidget}
aria-label="Add Widget"
priority="primary"
data-test-id="add-widget-library"
/>
) : (
<Button
data-test-id="add-widget-library"
priority="primary"
size="sm"
disabled={widgetLimitReached}
icon={<IconAdd isCircled />}
onClick={() => {
trackAnalytics('dashboards_views.widget_library.opened', {
organization,
});
onAddWidget(DataSet.EVENTS);
}}
>
{t('Add Widget')}
</Button>
)}
</Tooltip>
) : null}
</Fragment>
)}
</DashboardEditFeature>
</StyledButtonBar>
);
}
function DashboardEditFeature({
children,
}: {
children: (hasFeature: boolean) => React.ReactNode;
}) {
const renderDisabled = p => (
<Hovercard
body={
<FeatureDisabled
features={p.features}
hideHelpToggle
featureName={t('Dashboard Editing')}
/>
}
>
{p.children(p)}
</Hovercard>
);
return (
<Feature
hookName="feature-disabled:dashboards-edit"
features="organizations:dashboards-edit"
renderDisabled={renderDisabled}
>
{({hasFeature}) => children(hasFeature)}
</Feature>
);
}
const StyledButtonBar = styled(ButtonBar)`
@media (max-width: ${p => p.theme.breakpoints.small}) {
grid-auto-flow: row;
grid-row-gap: ${space(1)};
width: 100%;
}
`;
export default Controls;