diff --git a/static/app/views/dashboards/editAccessSelector.tsx b/static/app/views/dashboards/editAccessSelector.tsx index dc5f92bbfab0f9..a0ddefd71a74d3 100644 --- a/static/app/views/dashboards/editAccessSelector.tsx +++ b/static/app/views/dashboards/editAccessSelector.tsx @@ -34,6 +34,7 @@ import type { interface EditAccessSelectorProps { dashboard: DashboardDetails | DashboardListItem; + disabled?: boolean; listOnly?: boolean; onChangeEditAccess?: (newDashboardPermissions: DashboardPermissions) => void; } @@ -46,6 +47,7 @@ function EditAccessSelector({ dashboard, onChangeEditAccess, listOnly = false, + disabled = false, }: EditAccessSelectorProps) { const currentUser: User = useUser(); const dashboardCreator: User | undefined = dashboard.createdBy; @@ -320,6 +322,7 @@ function EditAccessSelector({ }} priority="primary" disabled={ + disabled || !userCanEditDashboardPermissions || isEqual(getDashboardPermissions(), { ...dashboard.permissions, @@ -369,14 +372,20 @@ function EditAccessSelector({ onSearch={debounce(val => void onSearch(val), DEFAULT_DEBOUNCE_DURATION)} strategy="fixed" preventOverflowOptions={{mainAxis: false}} + disabled={disabled} /> ); + const tooltipTitle = disabled + ? t('Prebuilt dashboards cannot be edited') + : t('Only the creator of this dashboard can manage editor access'); + return ( {dropdownMenu} diff --git a/static/app/views/dashboards/manage/dashboardGrid.tsx b/static/app/views/dashboards/manage/dashboardGrid.tsx index 12b295fafb3ecb..bcf8977743a0c7 100644 --- a/static/app/views/dashboards/manage/dashboardGrid.tsx +++ b/static/app/views/dashboards/manage/dashboardGrid.tsx @@ -16,6 +16,7 @@ import {IconEllipsis} from 'sentry/icons'; import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; +import {defined} from 'sentry/utils'; import {trackAnalytics} from 'sentry/utils/analytics'; import {useQueryClient} from 'sentry/utils/queryClient'; import withApi from 'sentry/utils/withApi'; @@ -122,6 +123,10 @@ function DashboardGrid({ onConfirm: () => handleDeleteDashboard(dashboard, 'grid'), }); }, + disabled: defined(dashboard.prebuiltId), + tooltip: defined(dashboard.prebuiltId) + ? t('Prebuilt dashboards cannot be deleted') + : undefined, }, ]; diff --git a/static/app/views/dashboards/manage/dashboardTable.tsx b/static/app/views/dashboards/manage/dashboardTable.tsx index f911d81ba47fef..80d73fdf6d3375 100644 --- a/static/app/views/dashboards/manage/dashboardTable.tsx +++ b/static/app/views/dashboards/manage/dashboardTable.tsx @@ -4,6 +4,7 @@ import type {Location} from 'history'; import cloneDeep from 'lodash/cloneDeep'; import {Flex} from '@sentry/scraps/layout'; +import {Tooltip} from '@sentry/scraps/tooltip/tooltip'; import { updateDashboardFavorite, @@ -27,6 +28,7 @@ import {IconCopy, IconDelete, IconStar} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; +import {defined} from 'sentry/utils'; import {trackAnalytics} from 'sentry/utils/analytics'; import {useQueryClient} from 'sentry/utils/queryClient'; import {decodeScalar} from 'sentry/utils/queryString'; @@ -229,7 +231,11 @@ function DashboardTable({ ) : ( - + + + + + ); } @@ -253,6 +259,7 @@ function DashboardTable({ dashboard={dataRow} onChangeEditAccess={onChangeEditAccess} listOnly + disabled={defined(dataRow.prebuiltId)} // Prebuilt dashboards cannot be edited /> ); } @@ -309,7 +316,14 @@ function DashboardTable({ data-test-id="dashboard-delete" icon={} size="sm" - disabled={dashboards && dashboards.length <= 1} + disabled={ + (dashboards && dashboards.length <= 1) || defined(dataRow.prebuiltId) + } + title={ + defined(dataRow.prebuiltId) + ? t('Prebuilt dashboards cannot be deleted') + : undefined + } /> diff --git a/static/app/views/dashboards/manage/index.tsx b/static/app/views/dashboards/manage/index.tsx index 436c9dc7c2bd1a..c462d655f665b4 100644 --- a/static/app/views/dashboards/manage/index.tsx +++ b/static/app/views/dashboards/manage/index.tsx @@ -1,4 +1,4 @@ -import {useEffect, useRef, useState} from 'react'; +import {useEffect, useMemo, useRef, useState} from 'react'; import styled from '@emotion/styled'; import type {Query} from 'history'; import debounce from 'lodash/debounce'; @@ -50,6 +50,7 @@ import OwnedDashboardsTable, { } from 'sentry/views/dashboards/manage/tableView/ownedDashboardsTable'; import type {DashboardsLayout} from 'sentry/views/dashboards/manage/types'; import type {DashboardDetails, DashboardListItem} from 'sentry/views/dashboards/types'; +import {PREBUILT_DASHBOARDS} from 'sentry/views/dashboards/utils/prebuiltConfigs'; import RouteError from 'sentry/views/routeError'; import DashboardGrid from './dashboardGrid'; @@ -155,7 +156,7 @@ function ManageDashboards() { }); const { - data: dashboards, + data: dashboardsWithoutPrebuiltConfigs, isLoading, isError, error, @@ -183,6 +184,29 @@ function ManageDashboards() { } ); + const dashboards = useMemo( + () => + dashboardsWithoutPrebuiltConfigs?.map(dashboard => { + if (dashboard.prebuiltId && dashboard.prebuiltId in PREBUILT_DASHBOARDS) { + return { + ...dashboard, + widgetDisplay: PREBUILT_DASHBOARDS[dashboard.prebuiltId].widgets.map( + widget => widget.displayType + ), + widgetPreview: PREBUILT_DASHBOARDS[dashboard.prebuiltId].widgets.map( + widget => ({ + displayType: widget.displayType, + layout: widget.layout ?? null, + }) + ), + projects: [], + }; + } + return dashboard; + }), + [dashboardsWithoutPrebuiltConfigs] + ); + const ownedDashboards = useOwnedDashboards({ query: decodeScalar(location.query.query, ''), cursor: decodeScalar(location.query[OWNED_CURSOR_KEY], ''), diff --git a/static/app/views/dashboards/types.tsx b/static/app/views/dashboards/types.tsx index a99d11f29b6dd1..e7e5043d4f8649 100644 --- a/static/app/views/dashboards/types.tsx +++ b/static/app/views/dashboards/types.tsx @@ -168,6 +168,7 @@ export type DashboardListItem = { isFavorited?: boolean; lastVisited?: string; permissions?: DashboardPermissions; + prebuiltId?: PrebuiltDashboardId; }; export enum DashboardFilterKeys {