Skip to content

Commit

Permalink
Reduce duplicate code for NavBar components
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Arnei committed Dec 16, 2024
1 parent 02e9b65 commit f6df982
Show file tree
Hide file tree
Showing 17 changed files with 445 additions and 884 deletions.
34 changes: 20 additions & 14 deletions src/components/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>("");

const toggleNavigation = () => {
setNavigation(!displayNavigation);
};

useEffect(() => {
const getURL = (language: string) => {
return `/ui/config/admin-ui/${location.pathname.split("/").pop()}.${language}.html`;
Expand All @@ -46,12 +40,24 @@ const About: React.FC = () => {
return (
<span>
<Header />
<NavBar>
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />
<nav>
<Link to="/about/imprint" className={cn({ active: location.pathname === "/about/imprint" })} onClick={() => { }}>{t("ABOUT.IMPRINT")}</Link>
<Link to="/about/privacy" className={cn({ active: location.pathname === "/about/privacy" })} onClick={() => { }}>{t("ABOUT.PRIVACY")}</Link>
</nav>
<NavBar
displayNavigation={displayNavigation}
setNavigation={setNavigation}
links={[
{
path: "/about/imprint",
accessRole: "ROLE_UI_USERS_VIEW",
loadFn: () => { },
text: "ABOUT.IMPRINT"
},
{
path: "/about/privacy",
accessRole: "ROLE_UI_GROUPS_VIEW",
loadFn: () => { },
text: "ABOUT.PRIVACY"
},
]}
>
</NavBar>
<div className="about">
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(aboutContent) }} ></div>
Expand Down
121 changes: 119 additions & 2 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -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<void>
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<React.SetStateAction<boolean>>
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 (
<section className="action-nav-bar" role="navigation">
{/* Display modal for new resource if add button is clicked */}
{ create && (create.isDisplay === undefined || create.isDisplay) && displayNewResourceModal &&
<NewResourceModal
handleClose={hideNewResourceModal}
resource={create.resource}
/>
}

{/* Include Burger-button menu */}
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />

<nav aria-label={navAriaLabel && t(navAriaLabel)}>
{links.map((link) =>
{return (hasAccess(link.accessRole, user) && (
<Link
to={link.path}
className={cn({ active: location.pathname === link.path })}
onClick={() => {
if (location.pathname !== link.path) {
// Reset the current page to first page
dispatch(setOffset(0));
}
link.loadFn(dispatch)
}}
>
{t(link.text)}
</Link>
))}
)}
</nav>

{children}

{create &&
<div className="btn-group">
{hasAccess(create.accessRole, user) && (
<button
className="add"
onClick={showNewResourceModal}
>
<i className="fa fa-plus" />
<span>{t(create.text)}</span>
</button>
)}
</div>
}
</section>
);
};
Expand Down
73 changes: 18 additions & 55 deletions src/components/configuration/Themes.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
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";
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";
Expand All @@ -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 () => {
Expand All @@ -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);
Expand All @@ -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 (
<>
<Header />
<NavBar>
{/* Display modal for new series if add series button is clicked */}
{ displayNewThemesModal &&
<NewResourceModal
handleClose={hideNewThemesModal}
resource={"themes"}
/>
}

{/* Include Burger-button menu*/}
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />

<nav>
{hasAccess("ROLE_UI_THEMES_VIEW", user) && (
<Link
to="/configuration/themes"
className={cn({ active: true })}
onClick={() => loadThemes()}
>
{t("CONFIGURATION.NAVIGATION.THEMES")}
</Link>
)}
</nav>

{/* Add theme button */}
<div className="btn-group">
{hasAccess("ROLE_UI_THEMES_CREATE", user) && (
<button className="add" onClick={() => showNewThemesModal()}>
<i className="fa fa-plus" />
<span>{t("CONFIGURATION.ACTIONS.ADD_THEME")}</span>
</button>
)}
</div>
</NavBar>
<NavBar
displayNavigation={displayNavigation}
setNavigation={setNavigation}
links={[
{
path: "/configuration/themes",
accessRole: "ROLE_UI_THEMES_VIEW",
loadFn: loadThemes,
text: "CONFIGURATION.NAVIGATION.THEMES"
},
]}
create={{
accessRole: "ROLE_UI_THEMES_CREATE",
text: "CONFIGURATION.ACTIONS.ADD_THEME",
resource: "themes",
}}
/>

<MainView open={displayNavigation}>
{/* Include notifications component */}
Expand Down
Loading

0 comments on commit f6df982

Please sign in to comment.