diff --git a/static/app/components/commandPalette/ui/commandPaletteGlobalActions.tsx b/static/app/components/commandPalette/ui/commandPaletteGlobalActions.tsx
index f86c60adf1ddce..11501eb557417f 100644
--- a/static/app/components/commandPalette/ui/commandPaletteGlobalActions.tsx
+++ b/static/app/components/commandPalette/ui/commandPaletteGlobalActions.tsx
@@ -402,8 +402,16 @@ export function GlobalCommandPaletteActions() {
}} limit={4}>
+ {hasPrebuiltDashboards && (
+
+ )}
{hasPrebuiltDashboards && (
diff --git a/static/app/views/dashboards/manage/index.spec.tsx b/static/app/views/dashboards/manage/index.spec.tsx
index fb80f6bb44e322..88ff7a4bf28996 100644
--- a/static/app/views/dashboards/manage/index.spec.tsx
+++ b/static/app/views/dashboards/manage/index.spec.tsx
@@ -15,7 +15,12 @@ import {getPaginationPageLink} from 'sentry/views/organizationStats/utils';
jest.mock('sentry/utils/localStorage');
-const FEATURES = ['dashboards-basic', 'dashboards-edit', 'discover-query'];
+const FEATURES = [
+ 'dashboards-basic',
+ 'dashboards-edit',
+ 'discover-query',
+ 'dashboards-prebuilt-insights-dashboards',
+];
jest.mock('sentry/utils/useNavigate', () => ({
useNavigate: jest.fn(),
@@ -71,7 +76,7 @@ describe('Dashboards > Detail', () => {
organization: mockAuthorizedOrg,
});
- expect(await screen.findByText('All Dashboards')).toBeInTheDocument();
+ expect(await screen.findByText('Custom Dashboards')).toBeInTheDocument();
expect(await screen.findByText('Test Dashboard')).toBeInTheDocument();
diff --git a/static/app/views/dashboards/manage/index.tsx b/static/app/views/dashboards/manage/index.tsx
index e9582598259ee7..e2b5086236a4d8 100644
--- a/static/app/views/dashboards/manage/index.tsx
+++ b/static/app/views/dashboards/manage/index.tsx
@@ -43,7 +43,8 @@ import {useNavigate} from 'sentry/utils/useNavigate';
import {useOrganization} from 'sentry/utils/useOrganization';
import {DashboardCreateLimitWrapper} from 'sentry/views/dashboards/createLimitWrapper';
import DashboardTable from 'sentry/views/dashboards/manage/dashboardTable';
-import type {DashboardsLayout} from 'sentry/views/dashboards/manage/types';
+import {type DashboardsLayout, DashboardsTab} from 'sentry/views/dashboards/manage/types';
+import {getDashboardsTab} from 'sentry/views/dashboards/manage/utils/getDashboardsTab';
import {DashboardFilter, PREBUILT_DASHBOARD_LABEL} from 'sentry/views/dashboards/types';
import {PREBUILT_DASHBOARDS} from 'sentry/views/dashboards/utils/prebuiltConfigs';
import {TopBar} from 'sentry/views/navigation/topBar';
@@ -66,6 +67,18 @@ export const LAYOUT_KEY = 'dashboards-overview-layout';
const GRID = 'grid';
const TABLE = 'table';
+const DASHBOARDS_TAB_TITLES: Record = {
+ [DashboardsTab.CUSTOM]: t('Custom Dashboards'),
+ [DashboardsTab.ALL]: t('All Dashboards'),
+ [DashboardsTab.PREBUILT]: PREBUILT_DASHBOARD_LABEL,
+};
+
+const DASHBOARDS_TAB_API_QUERY: Record = {
+ [DashboardsTab.CUSTOM]: {filter: DashboardFilter.EXCLUDE_PREBUILT},
+ [DashboardsTab.ALL]: {},
+ [DashboardsTab.PREBUILT]: {filter: DashboardFilter.ONLY_PREBUILT},
+};
+
function getDashboardsOverviewLayout(): DashboardsLayout {
const dashboardsLayout = localStorageWrapper.getItem(LAYOUT_KEY);
@@ -118,8 +131,12 @@ function ManageDashboards() {
'dashboards-prebuilt-insights-dashboards'
);
const urlFilter = decodeScalar(location.query.filter) as DashboardFilter | undefined;
- const isOnlyPrebuilt =
- hasPrebuiltDashboards && urlFilter === DashboardFilter.ONLY_PREBUILT;
+ const dashboardsTab = getDashboardsTab(hasPrebuiltDashboards, urlFilter);
+ const isOnlyPrebuilt = dashboardsTab === DashboardsTab.PREBUILT;
+ const pageTitle =
+ dashboardsTab === DashboardsTab.CUSTOM && !hasPrebuiltDashboards
+ ? t('All Dashboards')
+ : DASHBOARDS_TAB_TITLES[dashboardsTab];
const areAiFeaturesAllowed =
!organization.hideAiFeatures && organization.features.includes('gen-ai-features');
@@ -151,9 +168,7 @@ function ManageDashboards() {
pin: 'favorites',
per_page:
dashboardsLayout === GRID ? rowCount * columnCount : DASHBOARD_TABLE_NUM_ROWS,
- ...(isOnlyPrebuilt
- ? {filter: DashboardFilter.ONLY_PREBUILT}
- : {filter: DashboardFilter.EXCLUDE_PREBUILT}),
+ ...DASHBOARDS_TAB_API_QUERY[dashboardsTab],
},
}),
select: selectJsonWithHeaders,
@@ -530,10 +545,7 @@ function ManageDashboards() {
features="dashboards-edit"
renderDisabled={renderNoAccess}
>
-
+
{isError ? (
@@ -545,7 +557,7 @@ function ManageDashboards() {
- {isOnlyPrebuilt ? PREBUILT_DASHBOARD_LABEL : t('All Dashboards')}
+ {pageTitle}
{
[`${ORG}/explore/releases/`, 'Explore', 'Releases'],
[`${ORG}/explore/saved-queries/`, 'Explore', 'All Queries'],
// Dashboards
- [`${ORG}/dashboards/`, 'Dashboards', 'All Dashboards'],
+ [`${ORG}/dashboards/`, 'Dashboards', 'Custom Dashboards'],
// Insights
[`${ORG}/insights/frontend/`, 'Insights', 'Frontend'],
[`${ORG}/insights/backend/`, 'Insights', 'Backend'],
@@ -477,7 +478,7 @@ describe('desktop navigation', () => {
['/explore/logs/', 'Explore', 'Logs'],
['/explore/replays/', 'Explore', 'Replays'],
// Dashboards
- ['/dashboards/', 'Dashboards', 'All Dashboards'],
+ ['/dashboards/', 'Dashboards', 'Custom Dashboards'],
// Insights
['/insights/frontend/', 'Insights', 'Frontend'],
['/insights/backend/', 'Insights', 'Backend'],
diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx
index c5dc4094373dfa..6f218ab0b14538 100644
--- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx
+++ b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.spec.tsx
@@ -11,7 +11,9 @@ describe('DashboardsSecondaryNavigation', () => {
let organization: Organization;
beforeEach(() => {
- organization = OrganizationFixture();
+ organization = OrganizationFixture({
+ features: ['dashboards-prebuilt-insights-dashboards'],
+ });
MockApiClient.addMockResponse({
url: '/organizations/org-slug/group-search-views/starred/',
@@ -46,6 +48,8 @@ describe('DashboardsSecondaryNavigation', () => {
expect(screen.getAllByRole('link').map(el => el.textContent)).toEqual([
'All Dashboards',
+ 'Custom Dashboards',
+ 'Sentry Built',
'Dashboard 9999',
'Dashboard 1',
]);
diff --git a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx
index fc9900422c0b4b..7c6e4fb8e30978 100644
--- a/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx
+++ b/static/app/views/navigation/secondary/sections/dashboards/dashboardsSecondaryNavigation.tsx
@@ -12,6 +12,8 @@ import {useProjects} from 'sentry/utils/useProjects';
import {useUser} from 'sentry/utils/useUser';
import {useGetStarredDashboards} from 'sentry/views/dashboards/hooks/useGetStarredDashboards';
import {DEFAULT_PREBUILT_SORT} from 'sentry/views/dashboards/manage/settings';
+import {DashboardsTab} from 'sentry/views/dashboards/manage/types';
+import {getDashboardsTab} from 'sentry/views/dashboards/manage/utils/getDashboardsTab';
import {DashboardFilter, PREBUILT_DASHBOARD_LABEL} from 'sentry/views/dashboards/types';
import type {DashboardListItem} from 'sentry/views/dashboards/types';
import {isPrimaryNavigationLinkActive} from 'sentry/views/navigation/primary/components';
@@ -29,7 +31,7 @@ export function DashboardsSecondaryNavigation() {
'dashboards-prebuilt-insights-dashboards'
);
const urlFilter = decodeScalar(location.query.filter) as DashboardFilter | undefined;
- const isOnlyPrebuilt = urlFilter === DashboardFilter.ONLY_PREBUILT;
+ const dashboardsTab = getDashboardsTab(hasPrebuiltDashboards, urlFilter);
const isOnDashboardsList = isPrimaryNavigationLinkActive(
`${baseUrl}/`,
location.pathname,
@@ -44,25 +46,38 @@ export function DashboardsSecondaryNavigation() {
+ {hasPrebuiltDashboards ? (
+
+
+ {t('All Dashboards')}
+
+
+ ) : null}
- {t('All Dashboards')}
+ {hasPrebuiltDashboards ? t('Custom Dashboards') : t('All Dashboards')}
{hasPrebuiltDashboards ? (
{PREBUILT_DASHBOARD_LABEL}