From f6df982d6dce1c419c03e8a49743750e0f4af66c Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 16 Dec 2024 17:30:33 +0100 Subject: [PATCH] Reduce duplicate code for NavBar components This patch attempts to reduce duplicate code by moving components that would usually be passed to the NavBar component INTO the NavBar component. This does come with the trade-off that the NavBar component is now less general. However, as it is the NavBar component was pretty pointless anyway so this should not be an issue. --- src/components/About.tsx | 34 +++-- src/components/NavBar.tsx | 121 ++++++++++++++++- src/components/configuration/Themes.tsx | 73 +++------- src/components/events/Events.tsx | 118 +++------------- src/components/events/Series.tsx | 113 +++------------- .../events/partials/EventsNavigation.tsx | 43 ++++++ src/components/recordings/Recordings.tsx | 40 ++---- src/components/shared/NewResourceModal.tsx | 4 +- src/components/statistics/Statistics.tsx | 40 ++---- src/components/systems/Jobs.tsx | 89 ++---------- src/components/systems/Servers.tsx | 89 ++---------- src/components/systems/Services.tsx | 88 ++---------- .../systems/partials/SystemsNavigation.tsx | 54 ++++++++ src/components/users/Acls.tsx | 122 ++--------------- src/components/users/Groups.tsx | 120 ++--------------- src/components/users/Users.tsx | 127 +++--------------- .../users/partials/UsersNavigation.tsx | 54 ++++++++ 17 files changed, 445 insertions(+), 884 deletions(-) create mode 100644 src/components/events/partials/EventsNavigation.tsx create mode 100644 src/components/systems/partials/SystemsNavigation.tsx create mode 100644 src/components/users/partials/UsersNavigation.tsx diff --git a/src/components/About.tsx b/src/components/About.tsx index 0fbe18e67e..fc0873ff7b 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -2,25 +2,19 @@ import React, { useEffect, useState } from "react"; import Header from "./Header"; import NavBar from "./NavBar"; import Footer from "./Footer"; -import MainNav from "./shared/MainNav"; import { useTranslation } from "react-i18next"; -import { Link, useLocation } from "react-router-dom"; -import cn from "classnames"; +import { useLocation } from "react-router-dom"; import axios from 'axios'; import i18n from "../i18n/i18n"; import DOMPurify from "dompurify"; -const About: React.FC = () => { +const About = () => { const { t } = useTranslation(); const location = useLocation(); const [displayNavigation, setNavigation] = useState(false); const [aboutContent, setAboutContent] = useState(""); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - useEffect(() => { const getURL = (language: string) => { return `/ui/config/admin-ui/${location.pathname.split("/").pop()}.${language}.html`; @@ -46,12 +40,24 @@ const About: React.FC = () => { return (
- - - + { }, + text: "ABOUT.IMPRINT" + }, + { + path: "/about/privacy", + accessRole: "ROLE_UI_GROUPS_VIEW", + loadFn: () => { }, + text: "ABOUT.PRIVACY" + }, + ]} + >
diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 400615382d..e1884470f2 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,12 +1,129 @@ -import React from "react"; +import React, { useState } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { hasAccess } from "../utils/utils"; +import { AppDispatch, useAppDispatch, useAppSelector } from "../store"; +import { getUserInformation } from "../selectors/userInfoSelectors"; +import cn from "classnames"; +import { useTranslation } from "react-i18next"; +import MainNav from "./shared/MainNav"; +import { setOffset } from "../slices/tableSlice"; +import NewResourceModal, { NewResource } from "./shared/NewResourceModal"; +import { useHotkeys } from "react-hotkeys-hook"; /** * Component that renders the nav bar */ -const NavBar: React.FC<{ children: React.ReactNode }> = ({ children }) => { +type LinkType = { + path: string + accessRole: string + loadFn: (dispatch: AppDispatch) => void + text: string +} + +type CreateType = { + accessRole: string + onShowModal?: () => Promise + onHideModal?: () => void + text: string + isDisplay?: boolean + resource: NewResource + hotkeySequence?: string[] + hotkeyDescription?: string +} + +const NavBar = ({ + children, + navAriaLabel, + displayNavigation, + setNavigation, + links, + create, +} : { + children?: React.ReactNode + navAriaLabel?: string + displayNavigation: boolean + setNavigation: React.Dispatch> + links: LinkType[] + create?: CreateType +}) => { + + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const location = useLocation(); + + const user = useAppSelector(state => getUserInformation(state)); + + const [displayNewResourceModal, setNewResourceModal] = useState(false); + + const showNewResourceModal = async () => { + create && create.onShowModal && await create.onShowModal() + setNewResourceModal(true); + }; + + const hideNewResourceModal = () => { + create && create.onHideModal && create.onHideModal() + setNewResourceModal(false); + }; + + const toggleNavigation = () => { + setNavigation(!displayNavigation); + }; + + useHotkeys( + (create && create.hotkeySequence) ?? [], + () => showNewResourceModal(), + { description: t((create && create.hotkeyDescription) ?? "") ?? undefined }, + [showNewResourceModal] + ); + return (
+ {/* Display modal for new resource if add button is clicked */} + { create && (create.isDisplay === undefined || create.isDisplay) && displayNewResourceModal && + + } + + {/* Include Burger-button menu */} + + + + {children} + + {create && +
+ {hasAccess(create.accessRole, user) && ( + + )} +
+ }
); }; diff --git a/src/components/configuration/Themes.tsx b/src/components/configuration/Themes.tsx index fa4fab200c..b697197490 100644 --- a/src/components/configuration/Themes.tsx +++ b/src/components/configuration/Themes.tsx @@ -1,8 +1,5 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import MainNav from "../shared/MainNav"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; @@ -10,13 +7,10 @@ import { themesTemplateMap } from "../../configs/tableConfigs/themesTableMap"; import { getTotalThemes } from "../../selectors/themeSelectors"; import { loadThemesIntoTable } from "../../thunks/tableThunks"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchThemes } from "../../slices/themeSlice"; @@ -31,9 +25,7 @@ const Themes = () => { const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); const [displayNavigation, setNavigation] = useState(false); - const [displayNewThemesModal, setNewThemesModal] = useState(false); - const user = useAppSelector(state => getUserInformation(state)); const themes = useAppSelector(state => getTotalThemes(state)); const loadThemes = async () => { @@ -53,7 +45,7 @@ const Themes = () => { dispatch(editTextFilter("")); // Load themes on mount - loadThemes().then((r) => console.info(r)); + loadThemes(); // Fetch themes every minute let fetchThemesInterval = setInterval(loadThemes, 5000); @@ -62,55 +54,26 @@ const Themes = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - - const showNewThemesModal = () => { - setNewThemesModal(true); - }; - - const hideNewThemesModal = () => { - setNewThemesModal(false); - }; - return ( <>
- - {/* Display modal for new series if add series button is clicked */} - { displayNewThemesModal && - - } - - {/* Include Burger-button menu*/} - - - - - {/* Add theme button */} -
- {hasAccess("ROLE_UI_THEMES_CREATE", user) && ( - - )} -
-
+ {/* Include notifications component */} diff --git a/src/components/events/Events.tsx b/src/components/events/Events.tsx index 0e1337a4f6..4d7698f65d 100644 --- a/src/components/events/Events.tsx +++ b/src/components/events/Events.tsx @@ -1,13 +1,11 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import cn from "classnames"; -import { Link, useLocation } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import TableFilters from "../shared/TableFilters"; -import MainNav from "../shared/MainNav"; import Stats from "../shared/Stats"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import DeleteEventsModal from "./partials/modals/DeleteEventsModal"; import StartTaskModal from "./partials/modals/StartTaskModal"; import EditScheduledEventsModal from "./partials/modals/EditScheduledEventsModal"; @@ -15,22 +13,19 @@ import EditMetadataEventsModal from "./partials/modals/EditMetadataEventsModal"; import { eventsTemplateMap } from "../../configs/tableConfigs/eventsTableMap"; import { loadEventsIntoTable, - loadSeriesIntoTable, } from "../../thunks/tableThunks"; -import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice"; +import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { getTotalEvents, isFetchingAssetUploadOptions as getIsFetchingAssetUploadOptions, isShowActions, } from "../../selectors/eventSelectors"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; -import { useHotkeys } from "react-hotkeys-hook"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { fetchAssetUploadOptions } from "../../thunks/assetsThunks"; @@ -40,9 +35,9 @@ import { fetchEvents, setShowActions, } from "../../slices/eventSlice"; -import { fetchSeries } from "../../slices/seriesSlice"; import EventDetailsModal from "./partials/modals/EventDetailsModal"; import { showModal } from "../../selectors/eventDetailsSelectors"; +import { eventsLinks, loadEvents } from "./partials/EventsNavigation"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -59,7 +54,6 @@ const Events = () => { const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); - const [displayNewEventModal, setNewEventModal] = useState(false); const [displayDeleteModal, setDeleteModal] = useState(false); const [displayStartTaskModal, setStartTaskModal] = useState(false); const [ @@ -77,28 +71,6 @@ const Events = () => { let location = useLocation(); - const loadEvents = async () => { - // Fetching stats from server - dispatch(fetchStats()); - - // Fetching events from server - await dispatch(fetchEvents()); - - // Load events into table - dispatch(loadEventsIntoTable()); - }; - - const loadSeries = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - //fetching series from server - dispatch(fetchSeries()); - - //load series into table - dispatch(loadSeriesIntoTable()); - }; - useEffect(() => { if ("events" !== currentFilterType) { dispatch(fetchFilters("events")) @@ -111,7 +83,7 @@ const Events = () => { dispatch(setShowActions(false)); // Load events on mount - loadEvents().then((r) => console.info(r)); + loadEvents(dispatch); // Function for handling clicks outside of an open dropdown menu const handleClickOutside = (e: MouseEvent) => { @@ -124,7 +96,7 @@ const Events = () => { }; // Fetch events every minute - let fetchEventsInterval = setInterval(loadEvents, 5000); + let fetchEventsInterval = setInterval(() => loadEvents(dispatch), 5000); // Event listener for handle a click outside of dropdown menu window.addEventListener("mousedown", handleClickOutside); @@ -136,24 +108,14 @@ const Events = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.hash]); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - const handleActionMenu = (e: React.MouseEvent) => { e.preventDefault(); setActionMenu(!displayActionMenu); }; - const showNewEventModal = async () => { + const onNewEventModal = async () => { await dispatch(fetchEventMetadata()); await dispatch(fetchAssetUploadOptions()); - - setNewEventModal(true); - }; - - const hideNewEventModal = () => { - setNewEventModal(false); }; const hideDeleteModal = () => { @@ -172,29 +134,26 @@ const Events = () => { setEditMetadataEventsModal(false); }; - useHotkeys( - availableHotkeys.general.NEW_EVENT.sequence, - () => showNewEventModal(), - { - description: t(availableHotkeys.general.NEW_EVENT.description) ?? undefined - }, - [showNewEventModal] - ); - return ( <>
- - { - /* Display modal for new event if add event button is clicked */ - !isFetchingAssetUploadOptions && displayNewEventModal && ( - - ) + {/* Display bulk actions modal if one is chosen from dropdown */} {displayDeleteModal && } @@ -208,45 +167,12 @@ const Events = () => { )} - {/* Include Burger-button menu */} - - - - {/* Include status bar component*/} {hasAccess("ROLE_UI_EVENTS_COUNTERS_VIEW", user) && (
)} - -
- {hasAccess("ROLE_UI_EVENTS_CREATE", user) && ( - - )} -
diff --git a/src/components/events/Series.tsx b/src/components/events/Series.tsx index 4dcce7f070..ae6d1919b0 100644 --- a/src/components/events/Series.tsx +++ b/src/components/events/Series.tsx @@ -1,21 +1,17 @@ import React, { useEffect, useState } from "react"; -import MainNav from "../shared/MainNav"; import { useTranslation } from "react-i18next"; import cn from "classnames"; -import { Link, useLocation } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import DeleteSeriesModal from "./partials/modals/DeleteSeriesModal"; import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap"; import { - loadEventsIntoTable, loadSeriesIntoTable, } from "../../thunks/tableThunks"; -import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice"; +import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { getTotalSeries, isShowActions } from "../../selectors/seriesSeletctor"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; @@ -24,9 +20,7 @@ import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; -import { useHotkeys } from "react-hotkeys-hook"; import { useAppDispatch, useAppSelector } from "../../store"; -import { fetchEvents } from "../../slices/eventSlice"; import { fetchSeries, fetchSeriesMetadata, @@ -34,6 +28,7 @@ import { showActionsSeries, } from "../../slices/seriesSlice"; import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice"; +import { eventsLinks, loadSeries } from "./partials/EventsNavigation"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -46,7 +41,6 @@ const Series = () => { const dispatch = useAppDispatch(); const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); - const [displayNewSeriesModal, setNewSeriesModal] = useState(false); const [displayDeleteSeriesModal, setDeleteSeriesModal] = useState(false); const user = useAppSelector(state => getUserInformation(state)); @@ -57,28 +51,6 @@ const Series = () => { const series = useAppSelector(state => getTotalSeries(state)); const showActions = useAppSelector(state => isShowActions(state)); - const loadEvents = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching stats from server - dispatch(fetchStats()); - - // Fetching events from server - dispatch(fetchEvents()); - - // Load events into table - dispatch(loadEventsIntoTable()); - }; - - const loadSeries = async () => { - //fetching series from server - await dispatch(fetchSeries()); - - //load series into table - dispatch(loadSeriesIntoTable()); - }; - useEffect(() => { if ("series" !== currentFilterType) { dispatch(fetchFilters("series")) @@ -91,7 +63,7 @@ const Series = () => { dispatch(showActionsSeries(false)); // Load events on mount - loadSeries().then((r) => console.info(r)); + loadSeries(dispatch); // Function for handling clicks outside of an dropdown menu const handleClickOutside = (e: MouseEvent) => { @@ -104,7 +76,7 @@ const Series = () => { }; // Fetch series every minute - let fetchSeriesInterval = setInterval(loadSeries, 5000); + let fetchSeriesInterval = setInterval(() => loadSeries(dispatch), 5000); // Event listener for handle a click outside of dropdown menu window.addEventListener("mousedown", handleClickOutside); @@ -116,86 +88,43 @@ const Series = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.hash]); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - const handleActionMenu = (e: React.MouseEvent) => { e.preventDefault(); setActionMenu(!displayActionMenu); }; - const showNewSeriesModal = async () => { + const onNewSeriesModal = async () => { await dispatch(fetchSeriesMetadata()); await dispatch(fetchSeriesThemes()); await dispatch(fetchSeriesDetailsTobiraNew("/")); - - setNewSeriesModal(true); - }; - - const hideNewSeriesModal = () => { - setNewSeriesModal(false); }; const hideDeleteModal = () => { setDeleteSeriesModal(false); }; - useHotkeys( - availableHotkeys.general.NEW_SERIES.sequence, - () => showNewSeriesModal(), - { description: t(availableHotkeys.general.NEW_SERIES.description) ?? undefined }, - [showNewSeriesModal] - ); - return ( <>
- - {/* Display modal for new series if add series button is clicked */} - { displayNewSeriesModal && - + {displayDeleteSeriesModal && ( )} - - {/* Include Burger-button menu */} - - - - -
- {hasAccess("ROLE_UI_SERIES_CREATE", user) && ( - - )} -
diff --git a/src/components/events/partials/EventsNavigation.tsx b/src/components/events/partials/EventsNavigation.tsx new file mode 100644 index 0000000000..5185035005 --- /dev/null +++ b/src/components/events/partials/EventsNavigation.tsx @@ -0,0 +1,43 @@ +import { fetchEvents } from "../../../slices/eventSlice"; +import { fetchSeries } from "../../../slices/seriesSlice"; +import { fetchStats } from "../../../slices/tableFilterSlice"; +import { AppDispatch } from "../../../store"; +import { loadEventsIntoTable, loadSeriesIntoTable } from "../../../thunks/tableThunks"; + +/** + * Utility file for the navigation bar + */ + +export const loadEvents = (dispatch: AppDispatch) => { + // Fetching stats from server + dispatch(fetchStats()); + + // Fetching events from server + dispatch(fetchEvents()); + + // Load events into table + dispatch(loadEventsIntoTable()); +}; + +export const loadSeries = (dispatch: AppDispatch) => { + // fetching series from server + dispatch(fetchSeries()); + + // load series into table + dispatch(loadSeriesIntoTable()); +}; + +export const eventsLinks = [ + { + path: "/events/events", + accessRole: "ROLE_UI_EVENTS_VIEW", + loadFn: loadEvents, + text: "EVENTS.EVENTS.NAVIGATION.EVENTS" + }, + { + path: "/events/series", + accessRole: "ROLE_UI_SERIES_VIEW", + loadFn: loadSeries, + text: "EVENTS.EVENTS.NAVIGATION.SERIES" + } +]; diff --git a/src/components/recordings/Recordings.tsx b/src/components/recordings/Recordings.tsx index b030f0dce9..00d6a7af98 100644 --- a/src/components/recordings/Recordings.tsx +++ b/src/components/recordings/Recordings.tsx @@ -1,8 +1,5 @@ import React, { useEffect, useState } from "react"; -import MainNav from "../shared/MainNav"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; @@ -14,8 +11,6 @@ import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchRecordings } from "../../slices/recordingSlice"; @@ -30,7 +25,6 @@ const Recordings = () => { const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); - const user = useAppSelector(state => getUserInformation(state)); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); const recordings = useAppSelector(state => getTotalRecordings(state)); @@ -51,33 +45,25 @@ const Recordings = () => { dispatch(editTextFilter("")); // Load recordings on mount - loadRecordings().then((r) => console.info(r)); + loadRecordings(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - return ( <>
- - {/* Include Burger-button menu*/} - - - - + {/* Include notifications component */} diff --git a/src/components/shared/NewResourceModal.tsx b/src/components/shared/NewResourceModal.tsx index 848e48df43..94eb4441b2 100644 --- a/src/components/shared/NewResourceModal.tsx +++ b/src/components/shared/NewResourceModal.tsx @@ -12,12 +12,14 @@ import { availableHotkeys } from "../../configs/hotkeysConfig"; /** * This component renders the modal for adding new resources */ +export type NewResource = "events" | "series" | "user" | "group" | "acl" | "themes"; + const NewResourceModal = ({ handleClose, resource }: { handleClose: () => void, - resource: "events" | "series" | "user" | "group" | "acl" | "themes" + resource: NewResource }) => { const { t } = useTranslation(); diff --git a/src/components/statistics/Statistics.tsx b/src/components/statistics/Statistics.tsx index 2d1b46dba8..58a55fd5a8 100644 --- a/src/components/statistics/Statistics.tsx +++ b/src/components/statistics/Statistics.tsx @@ -1,12 +1,9 @@ import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import cn from "classnames"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import MainNav from "../shared/MainNav"; import TimeSeriesStatistics from "../shared/TimeSeriesStatistics"; import { getStatistics, @@ -16,9 +13,7 @@ import { } from "../../selectors/statisticsSelectors"; import { getOrgId, - getUserInformation, } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { fetchUserInfo } from "../../slices/userInfoSlice"; import { useAppDispatch, useAppSelector } from "../../store"; import { @@ -33,7 +28,6 @@ const Statistics: React.FC = () => { const [displayNavigation, setNavigation] = useState(false); const organizationId = useAppSelector(state => getOrgId(state)); - const user = useAppSelector(state => getUserInformation(state)); const statistics = useAppSelector(state => getStatistics(state)); const hasStatistics = useAppSelector(state => getHasStatistics(state)); const hasError = useAppSelector(state => hasStatisticsError(state)); @@ -52,10 +46,6 @@ const Statistics: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - /* generates file name for download-link for a statistic */ const statisticsCsvFileName = (statsTitle: string) => { const sanitizedStatsTitle = statsTitle @@ -71,24 +61,20 @@ const Statistics: React.FC = () => { }; return ( - +
- - {/* Include Burger-button menu */} - - - - + {}, + text: "STATISTICS.NAVIGATION.ORGANIZATION" + } + ]} + /> {/* main view of this page, displays statistics */} diff --git a/src/components/systems/Jobs.tsx b/src/components/systems/Jobs.tsx index ea81b0afa0..012f9f404b 100644 --- a/src/components/systems/Jobs.tsx +++ b/src/components/systems/Jobs.tsx @@ -1,31 +1,22 @@ import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; -import MainNav from "../shared/MainNav"; import Notifications from "../shared/Notifications"; import { jobsTemplateMap } from "../../configs/tableConfigs/jobsTableConfig"; import { getTotalJobs } from "../../selectors/jobSelectors"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { loadJobsIntoTable, - loadServersIntoTable, - loadServicesIntoTable, } from "../../thunks/tableThunks"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchJobs } from "../../slices/jobSlice"; -import { fetchServers } from "../../slices/serverSlice"; -import { fetchServices } from "../../slices/serviceSlice"; +import { loadJobs, systemsLinks } from "./partials/SystemsNavigation"; /** * This component renders the table view of jobs @@ -36,39 +27,8 @@ const Jobs = () => { const [displayNavigation, setNavigation] = useState(false); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - const user = useAppSelector(state => getUserInformation(state)); const jobs = useAppSelector(state => getTotalJobs(state)); - const loadJobs = async () => { - // Fetching jobs from server - await dispatch(fetchJobs()); - - // Load jobs into table - dispatch(loadJobsIntoTable()); - }; - - const loadServers = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching servers from server - dispatch(fetchServers()); - - // Load servers into table - dispatch(loadServersIntoTable()); - }; - - const loadServices = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching services from server - dispatch(fetchServices()); - - // Load services into table - dispatch(loadServicesIntoTable()); - }; - useEffect(() => { if ("jobs" !== currentFilterType) { dispatch(fetchFilters("jobs")); @@ -78,56 +38,23 @@ const Jobs = () => { dispatch(editTextFilter("")); // Load jobs on mount - loadJobs().then((r) => console.info(r)); + loadJobs(dispatch); // Fetch jobs every minute - let fetchJobInterval = setInterval(loadJobs, 5000); + let fetchJobInterval = setInterval(() => loadJobs(dispatch), 5000); return () => clearInterval(fetchJobInterval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - return ( <>
- - {/* Include Burger-button menu*/} - - - - + {/* Include notifications component */} diff --git a/src/components/systems/Servers.tsx b/src/components/systems/Servers.tsx index dea50ec9a2..32ad26be74 100644 --- a/src/components/systems/Servers.tsx +++ b/src/components/systems/Servers.tsx @@ -1,8 +1,5 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import MainNav from "../shared/MainNav"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; @@ -10,22 +7,16 @@ import { serversTemplateMap } from "../../configs/tableConfigs/serversTableMap"; import { getTotalServers } from "../../selectors/serverSelectors"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { - loadJobsIntoTable, loadServersIntoTable, - loadServicesIntoTable, } from "../../thunks/tableThunks"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; -import { fetchServices } from "../../slices/serviceSlice"; -import { fetchJobs } from "../../slices/jobSlice"; import { fetchServers } from "../../slices/serverSlice"; +import { loadServers, systemsLinks } from "./partials/SystemsNavigation"; /** * This component renders the table view of servers @@ -36,39 +27,8 @@ const Servers = () => { const [displayNavigation, setNavigation] = useState(false); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - const user = useAppSelector(state => getUserInformation(state)); const servers = useAppSelector(state => getTotalServers(state)); - const loadServers = async () => { - // Fetching servers from server - await dispatch(fetchServers()); - - // Load servers into table - dispatch(loadServersIntoTable()); - }; - - const loadJobs = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching jobs from server - dispatch(fetchJobs()); - - // Load jobs into table - dispatch(loadJobsIntoTable()); - }; - - const loadServices = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching services from server - dispatch(fetchServices()); - - // Load services into table - dispatch(loadServicesIntoTable()); - }; - useEffect(() => { if ("servers" !== currentFilterType) { dispatch(fetchFilters("servers")); @@ -78,56 +38,23 @@ const Servers = () => { dispatch(editTextFilter("")); // Load servers on mount - loadServers().then((r) => console.info(r)); + loadServers(dispatch); // Fetch servers every minute - let fetchServersInterval = setInterval(loadServers, 5000); + let fetchServersInterval = setInterval(() => loadServers(dispatch), 5000); return () => clearInterval(fetchServersInterval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - return ( <>
- - {/* Include Burger-button menu*/} - - - - + {/* Include notifications component */} diff --git a/src/components/systems/Services.tsx b/src/components/systems/Services.tsx index 672561ef44..b823241050 100644 --- a/src/components/systems/Services.tsx +++ b/src/components/systems/Services.tsx @@ -1,30 +1,22 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; -import MainNav from "../shared/MainNav"; import Notifications from "../shared/Notifications"; import { servicesTemplateMap } from "../../configs/tableConfigs/servicesTableMap"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { - loadJobsIntoTable, loadServicesIntoTable, } from "../../thunks/tableThunks"; import { getTotalServices } from "../../selectors/serviceSelector"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; -import { fetchServers } from "../../slices/serverSlice"; -import { fetchJobs } from "../../slices/jobSlice"; import { fetchServices } from "../../slices/serviceSlice"; +import { loadServices, systemsLinks } from "./partials/SystemsNavigation"; /** * This component renders the table view of services @@ -35,39 +27,8 @@ const Services = () => { const [displayNavigation, setNavigation] = useState(false); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - const user = useAppSelector(state => getUserInformation(state)); const services = useAppSelector(state => getTotalServices(state)); - const loadServices = async () => { - // Fetching services from server - await dispatch(fetchServices()); - - // Load services into table - dispatch(loadServicesIntoTable()); - }; - - const loadJobs = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching jobs from server - dispatch(fetchJobs()); - - // Load jobs into table - dispatch(loadJobsIntoTable()); - }; - - const loadServers = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching servers from server - dispatch(fetchServers()); - - // Load servers into table - dispatch(fetchServers()); - }; - useEffect(() => { if ("services" !== currentFilterType) { dispatch(fetchFilters("services")); @@ -77,56 +38,23 @@ const Services = () => { dispatch(editTextFilter("")); // Load services on mount - loadServices().then((r) => console.info(r)); + loadServices(dispatch); // Fetch services every minute - let fetchServicesInterval = setInterval(loadServices, 5000); + let fetchServicesInterval = setInterval(() => loadServices(dispatch), 5000); return () => clearInterval(fetchServicesInterval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - return ( <>
- - {/* Include Burger-button menu*/} - - - - + {/* Include notifications component */} diff --git a/src/components/systems/partials/SystemsNavigation.tsx b/src/components/systems/partials/SystemsNavigation.tsx new file mode 100644 index 0000000000..e7604f7af7 --- /dev/null +++ b/src/components/systems/partials/SystemsNavigation.tsx @@ -0,0 +1,54 @@ +import { fetchJobs } from "../../../slices/jobSlice"; +import { fetchServers } from "../../../slices/serverSlice"; +import { fetchServices } from "../../../slices/serviceSlice"; +import { AppDispatch } from "../../../store"; +import { loadJobsIntoTable, loadServersIntoTable, loadServicesIntoTable } from "../../../thunks/tableThunks"; + +/** + * Utility file for the navigation bar + */ + +export const loadJobs = async (dispatch: AppDispatch) => { + // Fetching jobs from server + await dispatch(fetchJobs()); + + // Load jobs into table + dispatch(loadJobsIntoTable()); +}; + +export const loadServers = async (dispatch: AppDispatch) => { + // Fetching servers from server + await dispatch(fetchServers()); + + // Load servers into table + dispatch(loadServersIntoTable()); +}; + +export const loadServices = async (dispatch: AppDispatch) => { + // Fetching services from server + await dispatch(fetchServices()); + + // Load services into table + dispatch(loadServicesIntoTable()); +}; + +export const systemsLinks = [ + { + path: "/systems/jobs", + accessRole: "ROLE_UI_JOBS_VIEW", + loadFn: loadJobs, + text: "SYSTEMS.NAVIGATION.JOBS" + }, + { + path: "/systems/SERVERS", + accessRole: "ROLE_UI_SERVERS_VIEW", + loadFn: loadServers, + text: "SYSTEMS.NAVIGATION.SERVERS" + }, + { + path: "/systems/services", + accessRole: "ROLE_UI_SERVICES_VIEW", + loadFn: loadServices, + text: "SYSTEMS.NAVIGATION.SERVICES" + }, +]; diff --git a/src/components/users/Acls.tsx b/src/components/users/Acls.tsx index e018947ae0..6d9caabd9d 100644 --- a/src/components/users/Acls.tsx +++ b/src/components/users/Acls.tsx @@ -1,76 +1,34 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import MainNav from "../shared/MainNav"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import { aclsTemplateMap } from "../../configs/tableConfigs/aclsTableMap"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { loadAclsIntoTable, - loadGroupsIntoTable, - loadUsersIntoTable, } from "../../thunks/tableThunks"; import { getTotalAcls } from "../../selectors/aclSelectors"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { hasAccess } from "../../utils/utils"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchAcls } from "../../slices/aclSlice"; -import { fetchUsers } from "../../slices/userSlice"; -import { fetchGroups } from "../../slices/groupSlice"; +import { loadAcls, usersLinks } from "./partials/UsersNavigation"; /** * This component renders the table view of acls */ -const Acls: React.FC = () => { +const Acls = () => { const { t } = useTranslation(); const [displayNavigation, setNavigation] = useState(false); - const [displayNewAclModal, setNewAclModal] = useState(false); const dispatch = useAppDispatch(); const acls = useAppSelector(state => getTotalAcls(state)); - const user = useAppSelector(state => getUserInformation(state)); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - const loadAcls = async () => { - // Fetching acls from server - await dispatch(fetchAcls()); - - // Load acls into table - dispatch(loadAclsIntoTable()); - }; - - const loadUsers = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching users from server - dispatch(fetchUsers()); - - // Load users into table - dispatch(loadUsersIntoTable()); - }; - - const loadGroups = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching groups from server - dispatch(fetchGroups()); - - // Load groups into table - dispatch(loadGroupsIntoTable()); - }; - useEffect(() => { if ("acls" !== currentFilterType) { dispatch(fetchFilters("acls")); @@ -80,7 +38,7 @@ const Acls: React.FC = () => { dispatch(editTextFilter("")); // Load acls on mount - loadAcls().then((r) => console.info(r)); + loadAcls(dispatch); // Fetch Acls every minute let fetchAclInterval = setInterval(loadAcls, 5000); @@ -89,73 +47,19 @@ const Acls: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - - const showNewAclModal = () => { - setNewAclModal(true); - }; - - const hideNewAclModal = () => { - setNewAclModal(false); - }; - return ( <>
- - {/* Display modal for new acl if add acl button is clicked */} - { displayNewAclModal && - - } - - {/* Include Burger-button menu*/} - - - - - {/* Add acl button */} -
- {hasAccess("ROLE_UI_ACLS_CREATE", user) && ( - - )} -
-
+ {/* Include notifications component */} diff --git a/src/components/users/Groups.tsx b/src/components/users/Groups.tsx index b6e267309c..a96996cee6 100644 --- a/src/components/users/Groups.tsx +++ b/src/components/users/Groups.tsx @@ -1,32 +1,22 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import MainNav from "../shared/MainNav"; -import { Link } from "react-router-dom"; -import cn from "classnames"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import { getTotalGroups } from "../../selectors/groupSelectors"; import { groupsTemplateMap } from "../../configs/tableConfigs/groupsTableMap"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; import { - loadAclsIntoTable, loadGroupsIntoTable, - loadUsersIntoTable, } from "../../thunks/tableThunks"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; -import { fetchAcls } from "../../slices/aclSlice"; import { useAppDispatch, useAppSelector } from "../../store"; -import { fetchUsers } from "../../slices/userSlice"; import { fetchGroups } from "../../slices/groupSlice"; +import { loadGroups, usersLinks } from "./partials/UsersNavigation"; /** * This component renders the table view of groups @@ -35,42 +25,10 @@ const Groups = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); - const [displayNewGroupModal, setNewGroupModal] = useState(false); - const user = useAppSelector(state => getUserInformation(state)); const groups = useAppSelector(state => getTotalGroups(state)); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - const loadGroups = async () => { - // Fetching groups from server - await dispatch(fetchGroups()); - - // Load groups into table - dispatch(loadGroupsIntoTable()); - }; - - const loadUsers = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching users from server - dispatch(fetchUsers()); - - // Load users into table - dispatch(loadUsersIntoTable()); - }; - - const loadAcls = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching acls from server - dispatch(fetchAcls()); - - // Load acls into table - dispatch(loadAclsIntoTable()); - }; - useEffect(() => { if ("groups" !== currentFilterType) { dispatch(fetchFilters("groups")); @@ -80,7 +38,7 @@ const Groups = () => { dispatch(editTextFilter("")); // Load groups on mount - loadGroups().then((r) => console.info(r)); + loadGroups(dispatch); // Fetch groups every minute let fetchGroupsInterval = setInterval(loadGroups, 5000); @@ -89,73 +47,19 @@ const Groups = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - - const showNewGroupModal = () => { - setNewGroupModal(true); - }; - - const hideNewGroupModal = () => { - setNewGroupModal(false); - }; - return ( <>
- - {/* Display modal for new acl if add acl button is clicked */} - { displayNewGroupModal && - - } - - {/* Include Burger-button menu*/} - - - - - {/* Add group button */} -
- {hasAccess("ROLE_UI_GROUPS_CREATE", user) && ( - - )} -
-
+ {/* Include notifications component */} diff --git a/src/components/users/Users.tsx b/src/components/users/Users.tsx index f3c1f8ff1e..311a7e3743 100644 --- a/src/components/users/Users.tsx +++ b/src/components/users/Users.tsx @@ -1,75 +1,34 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; -import cn from "classnames"; -import MainNav from "../shared/MainNav"; import TableFilters from "../shared/TableFilters"; import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; -import NewResourceModal from "../shared/NewResourceModal"; import { usersTemplateMap } from "../../configs/tableConfigs/usersTableMap"; import { getTotalUsers } from "../../selectors/userSelectors"; import { - loadAclsIntoTable, - loadGroupsIntoTable, loadUsersIntoTable, } from "../../thunks/tableThunks"; import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice"; -import { setOffset } from "../../slices/tableSlice"; import Header from "../Header"; import NavBar from "../NavBar"; import MainView from "../MainView"; import Footer from "../Footer"; -import { getUserInformation } from "../../selectors/userInfoSelectors"; -import { hasAccess } from "../../utils/utils"; + import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; -import { fetchAcls } from "../../slices/aclSlice"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchUsers } from "../../slices/userSlice"; -import { fetchGroups } from "../../slices/groupSlice"; +import { loadUsers, usersLinks } from "./partials/UsersNavigation"; /** * This component renders the table view of users */ -const Users: React.FC = () => { +const Users = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); - const [displayNewUserModal, setNewUserModal] = useState(false); - - const users = useAppSelector(state => getTotalUsers(state)); - const user = useAppSelector(state => getUserInformation(state)); - const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); - - const loadUsers = async () => { - // Fetching users from server - await dispatch(fetchUsers()); - - // Load users into table - dispatch(loadUsersIntoTable()); - }; - - const loadGroups = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - - // Fetching groups from server - dispatch(fetchGroups()); - - // Load groups into table - dispatch(loadGroupsIntoTable()); - }; - - const loadAcls = () => { - // Reset the current page to first page - dispatch(setOffset(0)); - // Fetching acls from server - dispatch(fetchAcls()); - - // Load acls into table - dispatch(loadAclsIntoTable()); - }; + const users = useAppSelector(state => getTotalUsers(state)); + const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); useEffect(() => { if ("users" !== currentFilterType) { @@ -80,7 +39,7 @@ const Users: React.FC = () => { dispatch(editTextFilter("")); // Load users on mount - loadUsers().then((r) => console.info(r)); + loadUsers(dispatch); // Fetch users every minute let fetchUsersInterval = setInterval(loadUsers, 5000); @@ -89,73 +48,19 @@ const Users: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const toggleNavigation = () => { - setNavigation(!displayNavigation); - }; - - const showNewUserModal = () => { - setNewUserModal(true); - }; - - const hideNewUserModal = () => { - setNewUserModal(false); - }; - return ( <>
- - {/* Display modal for new acl if add acl button is clicked */} - { displayNewUserModal && - - } - - {/* Include Burger-button menu*/} - - - - - {/* Add user button */} -
- {hasAccess("ROLE_UI_USERS_CREATE", user) && ( - - )} -
-
+ {/* Include notifications component */} diff --git a/src/components/users/partials/UsersNavigation.tsx b/src/components/users/partials/UsersNavigation.tsx new file mode 100644 index 0000000000..6d422c6a94 --- /dev/null +++ b/src/components/users/partials/UsersNavigation.tsx @@ -0,0 +1,54 @@ +import { fetchAcls } from "../../../slices/aclSlice"; +import { fetchGroups } from "../../../slices/groupSlice"; +import { fetchUsers } from "../../../slices/userSlice"; +import { AppDispatch } from "../../../store"; +import { loadAclsIntoTable, loadGroupsIntoTable, loadUsersIntoTable } from "../../../thunks/tableThunks"; + +/** + * Utility file for the navigation bar + */ + +export const loadAcls = async (dispatch: AppDispatch) => { + // Fetching acls from server + await dispatch(fetchAcls()); + + // Load acls into table + dispatch(loadAclsIntoTable()); +}; + +export const loadUsers = async (dispatch: AppDispatch) => { + // Fetching users from server + await dispatch(fetchUsers()); + + // Load users into table + dispatch(loadUsersIntoTable()); +}; + +export const loadGroups = async (dispatch: AppDispatch) => { + // Fetching groups from server + await dispatch(fetchGroups()); + + // Load groups into table + dispatch(loadGroupsIntoTable()); +}; + +export const usersLinks = [ + { + path: "/users/users", + accessRole: "ROLE_UI_USERS_VIEW", + loadFn: loadUsers, + text: "USERS.NAVIGATION.USERS" + }, + { + path: "/users/groups", + accessRole: "ROLE_UI_GROUPS_VIEW", + loadFn: loadGroups, + text: "USERS.NAVIGATION.GROUPS" + }, + { + path: "/users/acls", + accessRole: "ROLE_UI_ACLS_VIEW", + loadFn: loadAcls, + text: "USERS.NAVIGATION.PERMISSIONS" + }, +];