diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 837cbe3ad81..9a05f87fbca 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -563,42 +563,7 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na }) } - // 4. Insights (parent with tabs: System Insights, Alert state history) - var insightsChildren []*navtree.NavLink - - // System Insights - if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) { - insightsChildren = append(insightsChildren, &navtree.NavLink{ - Text: "System Insights", SubTitle: "View system insights and analytics", Id: "insights-system", Url: s.cfg.AppSubURL + "/alerting/insights", Icon: "chart-line", - }) - } - - // Alert state history - //nolint:staticcheck // not yet migrated to OpenFeature - if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingCentralAlertHistory) { - if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead))) { - insightsChildren = append(insightsChildren, &navtree.NavLink{ - Text: "Alert state history", - SubTitle: "View a history of all alert events generated by your Grafana-managed alert rules. All alert events are displayed regardless of whether silences or mute timings are set.", - Id: "insights-history", - Url: s.cfg.AppSubURL + "/alerting/history", - Icon: "history", - }) - } - } - - if len(insightsChildren) > 0 { - alertChildNavs = append(alertChildNavs, &navtree.NavLink{ - Text: "Insights", - SubTitle: "Analytics and history for alerting", - Id: "insights", - Url: s.cfg.AppSubURL + "/alerting/insights", - Icon: "chart-line", - Children: insightsChildren, - }) - } - - // 5. Settings (parent with tab: Settings) + // 4. Settings (parent with tab: Settings) if c.GetOrgRole() == org.RoleAdmin { settingsChildren := []*navtree.NavLink{ { diff --git a/pkg/services/navtree/navtreeimpl/navtree_alerting_test.go b/pkg/services/navtree/navtreeimpl/navtree_alerting_test.go index 96c7a223d49..8fe57ccb39f 100644 --- a/pkg/services/navtree/navtreeimpl/navtree_alerting_test.go +++ b/pkg/services/navtree/navtreeimpl/navtree_alerting_test.go @@ -118,7 +118,7 @@ func TestBuildAlertNavLinks_FeatureToggle(t *testing.T) { require.NotEmpty(t, navLink.Children) // Verify all expected parent items exist with children - expectedParents := []string{"alert-rules", "notification-config", "insights", "alerting-settings"} + expectedParents := []string{"alert-rules", "notification-config", "alerting-settings"} for _, parentId := range expectedParents { parent := findNavLink(navLink, parentId) require.NotNil(t, parent, "Should have %s parent in V2 navigation", parentId) @@ -170,7 +170,7 @@ func TestBuildAlertNavLinks_V2(t *testing.T) { require.NotEmpty(t, navLink.Children) // Verify all parent items exist with children - parentIds := []string{"alert-rules", "notification-config", "insights", "alerting-settings"} + parentIds := []string{"alert-rules", "notification-config", "alerting-settings"} for _, parentId := range parentIds { parent := findNavLink(navLink, parentId) require.NotNil(t, parent, "Should have parent %s in V2 navigation", parentId) @@ -189,7 +189,6 @@ func TestBuildAlertNavLinks_V2(t *testing.T) { }{ {"alert-rules", []string{"alert-rules-list", "alert-rules-recently-deleted"}}, {"notification-config", []string{"notification-config-contact-points", "notification-config-policies", "notification-config-templates", "notification-config-time-intervals"}}, - {"insights", []string{"insights-system", "insights-history"}}, } for _, tt := range tests { diff --git a/public/app/features/alerting/routes.tsx b/public/app/features/alerting/routes.tsx index e9e12a6b9fc..3d0e65dc2f2 100644 --- a/public/app/features/alerting/routes.tsx +++ b/public/app/features/alerting/routes.tsx @@ -223,13 +223,6 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] { ) ), }, - { - path: '/alerting/insights', - roles: evaluateAccess([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleExternalRead]), - component: importAlertingComponent( - () => import(/* webpackChunkName: "InsightsPage" */ 'app/features/alerting/unified/insights/InsightsPage') - ), - }, { path: '/alerting/recently-deleted/', roles: () => ['Admin'], diff --git a/public/app/features/alerting/unified/insights/InsightsPage.tsx b/public/app/features/alerting/unified/insights/InsightsPage.tsx deleted file mode 100644 index be48508d490..00000000000 --- a/public/app/features/alerting/unified/insights/InsightsPage.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useMemo } from 'react'; - -import { Trans, t } from '@grafana/i18n'; - -import { AlertingPageWrapper } from '../components/AlertingPageWrapper'; -import { getInsightsScenes, insightsIsAvailable } from '../home/Insights'; -import { useInsightsNav } from '../navigation/useInsightsNav'; -import { isLocalDevEnv } from '../utils/misc'; -import { withPageErrorBoundary } from '../withPageErrorBoundary'; - -function InsightsPage() { - const insightsEnabled = insightsIsAvailable() || isLocalDevEnv(); - const { navId, pageNav } = useInsightsNav(); - // Memoize the scene so it's only created once and properly initialized - const insightsScene = useMemo(() => getInsightsScenes(), []); - - if (!insightsEnabled) { - return ( - -
- - Insights are not available. Please configure the required data sources. - -
-
- ); - } - - return ( - - - - ); -} - -export default withPageErrorBoundary(InsightsPage); diff --git a/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx b/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx deleted file mode 100644 index 35122d5c0e9..00000000000 --- a/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { getWrapper } from 'test/test-utils'; - -import { config } from '@grafana/runtime'; -import { configureStore } from 'app/store/configureStore'; - -import { useInsightsNav } from './useInsightsNav'; - -describe('useInsightsNav', () => { - const mockNavIndex = { - insights: { - id: 'insights', - text: 'Insights', - url: '/alerting/insights', - }, - 'insights-system': { - id: 'insights-system', - text: 'System Insights', - url: '/alerting/insights', - }, - 'insights-history': { - id: 'insights-history', - text: 'Alert state history', - url: '/alerting/history', - }, - 'alerts-history': { - id: 'alerts-history', - text: 'History', - url: '/alerting/history', - }, - }; - - const defaultPreloadedState = { - navIndex: mockNavIndex, - }; - - beforeEach(() => { - config.featureToggles.alertingNavigationV2 = false; - }); - - it('should return legacy navId when feature flag is off', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/history'], - }, - }); - - const { result } = renderHook(() => useInsightsNav(), { wrapper }); - - expect(result.current.navId).toBe('alerts-history'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return V2 navigation when feature flag is on', () => { - config.featureToggles.alertingNavigationV2 = true; - const store = configureStore(defaultPreloadedState); - const wrapper = getWrapper({ - store, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/insights'], - }, - }); - - const { result } = renderHook(() => useInsightsNav(), { wrapper }); - - expect(result.current.navId).toBe('insights'); - expect(result.current.pageNav).toBeDefined(); - // eslint-disable-next-line testing-library/no-node-access - expect(result.current.pageNav?.children).toBeDefined(); - }); - - it('should set active tab based on current path', () => { - config.featureToggles.alertingNavigationV2 = true; - const store = configureStore(defaultPreloadedState); - const wrapper = getWrapper({ - store, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/history'], - }, - }); - - const { result } = renderHook(() => useInsightsNav(), { wrapper }); - - // eslint-disable-next-line testing-library/no-node-access - const historyTab = result.current.pageNav?.children?.find((tab) => tab.id === 'insights-history'); - expect(historyTab?.active).toBe(true); - }); - - it('should filter tabs based on permissions', () => { - config.featureToggles.alertingNavigationV2 = true; - const limitedNavIndex = { - insights: mockNavIndex.insights, - 'insights-system': mockNavIndex['insights-system'], - // Missing 'insights-history' - user doesn't have permission - }; - const store = configureStore({ - navIndex: limitedNavIndex, - }); - const wrapper = getWrapper({ - store, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/insights'], - }, - }); - - const { result } = renderHook(() => useInsightsNav(), { wrapper }); - - // eslint-disable-next-line testing-library/no-node-access - expect(result.current.pageNav?.children?.length).toBe(1); - // eslint-disable-next-line testing-library/no-node-access - expect(result.current.pageNav?.children?.[0].id).toBe('insights-system'); - }); -}); diff --git a/public/app/features/alerting/unified/navigation/useInsightsNav.ts b/public/app/features/alerting/unified/navigation/useInsightsNav.ts deleted file mode 100644 index 7d888c30d5c..00000000000 --- a/public/app/features/alerting/unified/navigation/useInsightsNav.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { useLocation } from 'react-router-dom-v5-compat'; - -import { NavModelItem } from '@grafana/data'; -import { t } from '@grafana/i18n'; -import { useSelector } from 'app/types/store'; - -import { shouldUseAlertingNavigationV2 } from '../featureToggles'; - -export function useInsightsNav() { - const location = useLocation(); - const navIndex = useSelector((state) => state.navIndex); - const useV2Nav = shouldUseAlertingNavigationV2(); - - // If V2 navigation is not enabled, return legacy navId - if (!useV2Nav) { - if (location.pathname === '/alerting/history') { - return { - navId: 'alerts-history', - pageNav: undefined, - }; - } - // For insights page, it doesn't exist in legacy, so return undefined - return { - navId: undefined, - pageNav: undefined, - }; - } - - const insightsNav = navIndex.insights; - if (!insightsNav) { - // Fallback to legacy - if (location.pathname === '/alerting/history') { - return { - navId: 'alerts-history', - pageNav: undefined, - }; - } - return { - navId: undefined, - pageNav: undefined, - }; - } - - // All available tabs - const allTabs = [ - { - id: 'insights-system', - text: t('alerting.navigation.system-insights', 'System Insights'), - url: '/alerting/insights', - active: location.pathname === '/alerting/insights', - icon: 'chart-line', - parentItem: insightsNav, - }, - { - id: 'insights-history', - text: t('alerting.navigation.alert-state-history', 'Alert state history'), - url: '/alerting/history', - active: location.pathname === '/alerting/history', - icon: 'history', - parentItem: insightsNav, - }, - ].filter((tab) => { - // Filter based on permissions - if nav item doesn't exist, user doesn't have permission - const navItem = navIndex[tab.id]; - return navItem !== undefined; - }); - - // Create pageNav that represents the Insights page with tabs as children - const pageNav: NavModelItem = { - ...insightsNav, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - children: allTabs as NavModelItem[], - }; - - return { - navId: 'insights', - pageNav, - }; -}