navModel.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import { AnyAction, createAction } from '@reduxjs/toolkit';
  2. import { NavIndex, NavModel, NavModelItem } from '@grafana/data';
  3. import config from 'app/core/config';
  4. export function buildInitialState(): NavIndex {
  5. const navIndex: NavIndex = {};
  6. const rootNodes = config.bootData.navTree as NavModelItem[];
  7. buildNavIndex(navIndex, rootNodes);
  8. return navIndex;
  9. }
  10. function buildNavIndex(navIndex: NavIndex, children: NavModelItem[], parentItem?: NavModelItem) {
  11. for (const node of children) {
  12. navIndex[node.id!] = {
  13. ...node,
  14. parentItem: parentItem,
  15. };
  16. if (node.children) {
  17. buildNavIndex(navIndex, node.children, node);
  18. }
  19. }
  20. navIndex['not-found'] = { ...buildWarningNav('Page not found', '404 Error').node };
  21. }
  22. function buildWarningNav(text: string, subTitle?: string): NavModel {
  23. const node = {
  24. text,
  25. subTitle,
  26. icon: 'exclamation-triangle',
  27. };
  28. return {
  29. breadcrumbs: [node],
  30. node: node,
  31. main: node,
  32. };
  33. }
  34. export const initialState: NavIndex = {};
  35. export const updateNavIndex = createAction<NavModelItem>('navIndex/updateNavIndex');
  36. // Since the configuration subtitle includes the organization name, we include this action to update the org name if it changes.
  37. export const updateConfigurationSubtitle = createAction<string>('navIndex/updateConfigurationSubtitle');
  38. export const getItemWithNewSubTitle = (item: NavModelItem, subTitle: string): NavModelItem => ({
  39. ...item,
  40. parentItem: {
  41. ...item.parentItem,
  42. text: item.parentItem?.text ?? '',
  43. subTitle,
  44. },
  45. });
  46. // Redux Toolkit uses ImmerJs as part of their solution to ensure that state objects are not mutated.
  47. // ImmerJs has an autoFreeze option that freezes objects from change which means this reducer can't be migrated to createSlice
  48. // because the state would become frozen and during run time we would get errors because Angular would try to mutate
  49. // the frozen state.
  50. // https://github.com/reduxjs/redux-toolkit/issues/242
  51. export const navIndexReducer = (state: NavIndex = initialState, action: AnyAction): NavIndex => {
  52. if (updateNavIndex.match(action)) {
  53. const newPages: NavIndex = {};
  54. const payload = action.payload;
  55. for (const node of payload.children!) {
  56. newPages[node.id!] = {
  57. ...node,
  58. parentItem: payload,
  59. };
  60. }
  61. return { ...state, ...newPages };
  62. } else if (updateConfigurationSubtitle.match(action)) {
  63. const subTitle = `Organization: ${action.payload}`;
  64. return {
  65. ...state,
  66. cfg: { ...state.cfg, subTitle },
  67. datasources: getItemWithNewSubTitle(state.datasources, subTitle),
  68. users: getItemWithNewSubTitle(state.users, subTitle),
  69. teams: getItemWithNewSubTitle(state.teams, subTitle),
  70. plugins: getItemWithNewSubTitle(state.plugins, subTitle),
  71. 'org-settings': getItemWithNewSubTitle(state['org-settings'], subTitle),
  72. apikeys: getItemWithNewSubTitle(state.apikeys, subTitle),
  73. };
  74. }
  75. return state;
  76. };