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}