Compare commits
2 Commits
ash/react-
...
alerting/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32975dd0a8 | ||
|
|
f7b7c53a09 |
@@ -446,9 +446,32 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
||||
}
|
||||
|
||||
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Alert rules", SubTitle: "Rules that determine whether an alert will fire", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
||||
})
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingNavigationV2) {
|
||||
// New navigation: Alert rules parent (tabs managed on frontend)
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Alert rules", SubTitle: "Rules that determine whether an alert will fire", Id: "alert-rules", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
||||
})
|
||||
|
||||
// Add child nav items for permission checking (tabs will be rendered by frontend)
|
||||
// Alert rules list tab
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Alert rules", Id: "alert-rules-list", Url: s.cfg.AppSubURL + "/alerting/list",
|
||||
})
|
||||
|
||||
// Recently deleted tab (check additional feature flags)
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if c.GetOrgRole() == org.RoleAdmin && s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertRuleRestore) && s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingRuleRecoverDeleted) {
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Recently deleted", Id: "alert-rules-recently-deleted", Url: s.cfg.AppSubURL + "/alerting/recently-deleted",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Legacy navigation
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Alert rules", SubTitle: "Rules that determine whether an alert will fire", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
contactPointsPerms := []ac.Evaluator{
|
||||
@@ -508,12 +531,16 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
||||
}
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if c.GetOrgRole() == org.RoleAdmin && s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertRuleRestore) && s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingRuleRecoverDeleted) {
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Recently deleted",
|
||||
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
|
||||
Id: "alerts/recently-deleted",
|
||||
Url: s.cfg.AppSubURL + "/alerting/recently-deleted",
|
||||
})
|
||||
// Only show as standalone item in legacy navigation (V2 shows it as a tab under Alert rules)
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if !s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingNavigationV2) {
|
||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||
Text: "Recently deleted",
|
||||
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
|
||||
Id: "alerts/recently-deleted",
|
||||
Url: s.cfg.AppSubURL + "/alerting/recently-deleted",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if c.GetOrgRole() == org.RoleAdmin {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useLocation } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { NavModelItem } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { useSelector } from 'app/types/store';
|
||||
|
||||
export function useAlertRulesNav() {
|
||||
const location = useLocation();
|
||||
const navIndex = useSelector((state) => state.navIndex);
|
||||
|
||||
// Check if V2 navigation is enabled
|
||||
const useV2Nav = config.featureToggles.alertingNavigationV2;
|
||||
|
||||
if (!useV2Nav) {
|
||||
// Legacy navigation: return simple navId
|
||||
return {
|
||||
navId: 'alert-list',
|
||||
pageNav: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// V2 Navigation: Create tabs structure
|
||||
const alertRulesNav = navIndex['alert-rules'];
|
||||
|
||||
if (!alertRulesNav) {
|
||||
// Fallback to legacy if nav item doesn't exist
|
||||
return {
|
||||
navId: 'alert-list',
|
||||
pageNav: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// All available tabs
|
||||
const allTabs = [
|
||||
{
|
||||
id: 'alert-rules-list',
|
||||
text: t('alerting.navigation.alert-rules', 'Alert rules'),
|
||||
url: '/alerting/list',
|
||||
active: location.pathname === '/alerting/list',
|
||||
icon: 'list-ul',
|
||||
parentItem: alertRulesNav,
|
||||
},
|
||||
{
|
||||
id: 'alert-rules-recently-deleted',
|
||||
text: t('alerting.navigation.recently-deleted', 'Recently deleted'),
|
||||
url: '/alerting/recently-deleted',
|
||||
active: location.pathname === '/alerting/recently-deleted',
|
||||
icon: 'history',
|
||||
parentItem: alertRulesNav,
|
||||
},
|
||||
].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 Alert rules page with tabs as children
|
||||
const pageNav: NavModelItem = {
|
||||
...alertRulesNav,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
children: allTabs as NavModelItem[],
|
||||
};
|
||||
|
||||
return {
|
||||
navId: 'alert-rules',
|
||||
pageNav,
|
||||
};
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import { shouldUsePrometheusRulesPrimary } from '../featureToggles';
|
||||
import { useCombinedRuleNamespaces } from '../hooks/useCombinedRuleNamespaces';
|
||||
import { useFilteredRules, useRulesFilter } from '../hooks/useFilteredRules';
|
||||
import { useUnifiedAlertingSelector } from '../hooks/useUnifiedAlertingSelector';
|
||||
import { useAlertRulesNav } from '../navigation/useAlertRulesNav';
|
||||
import { fetchAllPromAndRulerRulesAction, fetchAllPromRulesAction, fetchRulerRulesAction } from '../state/actions';
|
||||
import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
|
||||
import { GRAFANA_RULES_SOURCE_NAME, getAllRulesSourceNames } from '../utils/datasource';
|
||||
@@ -38,6 +39,7 @@ const LIMIT_ALERTS = INSTANCES_DISPLAY_LIMIT + 1;
|
||||
const prometheusRulesPrimary = shouldUsePrometheusRulesPrimary();
|
||||
|
||||
const RuleListV1 = () => {
|
||||
const { navId, pageNav } = useAlertRulesNav();
|
||||
const dispatch = useDispatch();
|
||||
const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []);
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
@@ -119,7 +121,8 @@ const RuleListV1 = () => {
|
||||
// We don't want to show the Loading... indicator for the whole page.
|
||||
// We show separate indicators for Grafana-managed and Cloud rules
|
||||
<AlertingPageWrapper
|
||||
navId="alert-list"
|
||||
navId={navId}
|
||||
pageNav={pageNav}
|
||||
isLoading={false}
|
||||
renderTitle={(title) => <RuleListPageTitle title={title} />}
|
||||
actions={<RuleListActionButtons hasAlertRulesCreated={hasAlertRulesCreated} />}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useListViewMode } from '../components/rules/Filter/RulesViewModeSelecto
|
||||
import { AIAlertRuleButtonComponent } from '../enterprise-components/AI/AIGenAlertRuleButton/addAIAlertRuleButton';
|
||||
import { AlertingAction, useAlertingAbility } from '../hooks/useAbilities';
|
||||
import { useRulesFilter } from '../hooks/useFilteredRules';
|
||||
import { useAlertRulesNav } from '../navigation/useAlertRulesNav';
|
||||
import { getRulesDataSources } from '../utils/datasource';
|
||||
|
||||
import { FilterView } from './FilterView';
|
||||
@@ -123,10 +124,12 @@ export function RuleListActions() {
|
||||
|
||||
export default function RuleListPage() {
|
||||
const { isApplying } = useApplyDefaultSearch();
|
||||
const { navId, pageNav } = useAlertRulesNav();
|
||||
|
||||
return (
|
||||
<AlertingPageWrapper
|
||||
navId="alert-list"
|
||||
navId={navId}
|
||||
pageNav={pageNav}
|
||||
renderTitle={(title) => <RuleListPageTitle title={title} />}
|
||||
isLoading={isApplying}
|
||||
actions={<RuleListActions />}
|
||||
|
||||
@@ -1929,6 +1929,10 @@
|
||||
"select-group": "Select group",
|
||||
"select-namespace": "Select namespace"
|
||||
},
|
||||
"navigation": {
|
||||
"alert-rules": "Alert rules",
|
||||
"recently-deleted": "Recently deleted"
|
||||
},
|
||||
"need-help-info": {
|
||||
"need-help": "Need help?"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user