Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve AppBar #2190

Merged
merged 11 commits into from
May 24, 2023
8 changes: 4 additions & 4 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
"wordInDatabase": "This word is already in the database"
},
"appBar": {
"dataEntry": "data entry",
"dataCleanup": "data cleanup",
"statistics": "data statistics"
"dataEntry": "Data Entry",
"dataCleanup": "Data Cleanup",
"statistics": "Data Statistics",
"projectSettings": "Project Settings"
},
"login": {
"title": "Log In",
Expand Down Expand Up @@ -77,7 +78,6 @@
},
"userMenu": {
"siteSettings": "Site Settings",
"projectSettings": "Project Settings",
"userSettings": "User Settings",
"logout": "Log Out",
"userGuide": "User Guide"
Expand Down
32 changes: 15 additions & 17 deletions src/components/AppBar/AppBarComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
import { AppBar, Grid, Hidden, Toolbar } from "@mui/material";
import { AppBar, Grid, Toolbar } from "@mui/material";
import { ReactElement, useEffect, useState } from "react";
import { useLocation } from "react-router-dom";

import { getProjectId } from "backend/localStorage";
import { getBasePath, Path } from "browserHistory";
import Logo from "components/AppBar/Logo";
import NavigationButtons from "components/AppBar/NavigationButtons";
import ProjectNameButton from "components/AppBar/ProjectNameButton";
import ProjectButtons from "components/AppBar/ProjectButtons";
import UserMenu from "components/AppBar/UserMenu";
import { topBarHeight } from "components/LandingPage/TopBar";
import DownloadButton from "components/ProjectExport/DownloadButton";
import theme from "types/theme";

export const appBarHeight = 64;

/** An app bar shown at the top of all logged in pages */
export default function AppBarComponent(): ReactElement {
const location = useLocation();
const [currentTab, setCurrentTab] = useState<Path>(Path.ProjScreen);

useEffect(() => setCurrentTab(getBasePath(location.pathname)), [location]);

return (
<div className="NavigationBar" style={{ marginBottom: topBarHeight }}>
<AppBar position="fixed" style={{ zIndex: theme.zIndex.drawer + 1 }}>
<AppBar
position="fixed"
style={{ maxHeight: appBarHeight, zIndex: theme.zIndex.drawer + 1 }}
>
<Toolbar>
<Grid
container
justifyContent="space-between"
spacing={2}
alignItems="center"
>
<Grid item sm={7} md={6} lg={5}>
<Hidden smDown>
<Logo />
</Hidden>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Logo />
{!!getProjectId() && (
<NavigationButtons currentTab={currentTab} />
)}
</Grid>
<Grid item xs={1} sm={2} md={3} lg={4}>
{!!getProjectId() && (
<ProjectNameButton currentTab={currentTab} />
)}
<Grid item>
{!!getProjectId() && <ProjectButtons currentTab={currentTab} />}
<DownloadButton colorSecondary />
</Grid>
<Grid item>
Expand Down
18 changes: 11 additions & 7 deletions src/components/AppBar/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ import smallLogo from "resources/CombineSmallLogoV1.png";
/** A button that redirects to the home page */
export default function Logo(): ReactElement {
return (
<Button onClick={() => history.push(Path.ProjScreen)} id="logo-button">
<Hidden lgDown>
<img src={logo} height="50" alt="Logo" />
<Button
id="logo-button"
onClick={() => history.push(Path.ProjScreen)}
style={{ minWidth: 0, padding: 0 }}
>
<Hidden mdDown>
<img src={logo} height="45" alt="Logo" />
</Hidden>
<Hidden lgUp mdDown>
<img src={logo} height="40" alt="Logo" />
<Hidden smDown mdUp>
<img src={smallLogo} height="30" alt="Logo" />
</Hidden>
<Hidden mdUp>
<img src={smallLogo} height="35" alt="Logo" />
<Hidden smUp>
<img src={smallLogo} height="15" alt="Logo" />
</Hidden>
</Button>
);
Expand Down
66 changes: 12 additions & 54 deletions src/components/AppBar/NavigationButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,47 @@
import { Button } from "@mui/material";
import React, { ReactElement, useState } from "react";
import { ReactElement } from "react";
import { useTranslation } from "react-i18next";

import { Permission } from "api";
import * as backend from "backend";
import * as LocalStorage from "backend/localStorage";
import history, { Path } from "browserHistory";
import { openTreeAction } from "components/TreeView/TreeViewActions";
import { useAppDispatch } from "types/hooks";
import { appBarHeight } from "components/AppBar/AppBarComponent";
import { tabColor } from "types/theme";

interface NavigationButtonsProps {
currentTab: Path;
}

export async function getIsAdminOrOwner(): Promise<boolean> {
const user = LocalStorage.getCurrentUser();
if (user?.isAdmin) {
return true;
} else {
const projectId = LocalStorage.getProjectId();
const userRoleID = user?.projectRoles[projectId];
if (userRoleID) {
return backend.getUserRole(userRoleID).then((role) => {
return role.permissions.includes(Permission.Owner);
});
}
}
return false;
}

/** A button that redirects to the home page */
export default function NavigationButtons(
props: NavigationButtonsProps
): ReactElement {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const [isAdminOrOwner, setIsAdminOrOwner] = useState<boolean>(false);

getIsAdminOrOwner().then(setIsAdminOrOwner);

return (
<React.Fragment>
<>
<Button
id="data-entry"
onClick={() => {
dispatch(openTreeAction());
history.push(Path.DataEntry);
}}
id={"data-entry"}
onClick={() => history.push(Path.DataEntry)}
color="inherit"
style={{
width: "fit-content",
background: tabColor(props.currentTab, Path.DataEntry),
maxHeight: appBarHeight,
width: "min-content",
}}
>
{t("appBar.dataEntry")}
</Button>
<Button
id="goals"
onClick={() => {
history.push(Path.Goals);
}}
id={"data-cleanup"}
onClick={() => history.push(Path.Goals)}
color="inherit"
style={{
width: "fit-content",
background: tabColor(props.currentTab, Path.Goals),
maxHeight: appBarHeight,
width: "min-content",
}}
>
{t("appBar.dataCleanup")}
</Button>
{isAdminOrOwner && (
<Button
id="statistics"
onClick={() => {
history.push(Path.Statistics);
}}
color="inherit"
style={{
width: "min-content",
background: tabColor(props.currentTab, Path.Statistics),
}}
>
{t("appBar.statistics")}
</Button>
)}
</React.Fragment>
</>
);
}
85 changes: 85 additions & 0 deletions src/components/AppBar/ProjectButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { BarChart, Settings } from "@mui/icons-material";
import { Button, Hidden, Tooltip, Typography } from "@mui/material";
import { ReactElement, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";

import { Permission } from "api/models";
import { getUserRole } from "backend";
import { getCurrentUser, getProjectId } from "backend/localStorage";
import history, { Path } from "browserHistory";
import { StoreState } from "types";
import { tabColor } from "types/theme";

interface ProjectButtonsProps {
currentTab: Path;
}

export async function getIsAdminOrOwner(): Promise<boolean> {
const user = getCurrentUser();
if (!user) {
return false;
}
if (user.isAdmin) {
return true;
}
const userRoleID = user.projectRoles[getProjectId()];
if (userRoleID) {
const role = await getUserRole(userRoleID);
return role.permissions.includes(Permission.Owner);
}
return false;
}

/** A button that redirects to the project settings */
export default function ProjectButtons(
props: ProjectButtonsProps
): ReactElement {
const projectName = useSelector(
(state: StoreState) => state.currentProjectState.project.name
);
const [isAdminOrOwner, setIsAdminOrOwner] = useState<boolean>(false);
const { t } = useTranslation();

useEffect(() => {
getIsAdminOrOwner().then(setIsAdminOrOwner);
}, [setIsAdminOrOwner]);

return (
<>
{isAdminOrOwner && (
<Tooltip title={t("appBar.statistics")}>
<Button
id="project-statistics"
onClick={() => history.push(Path.Statistics)}
color="inherit"
style={{
background: tabColor(props.currentTab, Path.Statistics),
minWidth: 0,
}}
>
<BarChart />
</Button>
</Tooltip>
)}
<Tooltip title={t("appBar.projectSettings")}>
<Button
id="project-settings"
onClick={() => history.push(Path.ProjSettings)}
color="inherit"
style={{
background: tabColor(props.currentTab, Path.ProjSettings),
minWidth: 0,
}}
>
<Settings />
</Button>
</Tooltip>
<Hidden smDown>
<Typography display="inline" style={{ margin: 5 }}>
{projectName}
</Typography>
</Hidden>
</>
);
}
38 changes: 0 additions & 38 deletions src/components/AppBar/ProjectNameButton.tsx

This file was deleted.

25 changes: 20 additions & 5 deletions src/components/AppBar/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import {
Person,
SettingsApplications,
} from "@mui/icons-material";
import { Avatar, Button, Hidden, Menu, MenuItem } from "@mui/material";
import {
Avatar,
Button,
Hidden,
Menu,
MenuItem,
Typography,
} from "@mui/material";
import React, { ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";

Expand Down Expand Up @@ -50,16 +57,24 @@ export default function UserMenu(props: UserMenuProps): ReactElement {
getIsAdmin().then(setIsAdmin);

return (
<React.Fragment>
<>
<Button
aria-controls="user-menu"
aria-haspopup="true"
onClick={handleClick}
color="secondary"
style={{ background: tabColor(props.currentTab, Path.UserSettings) }}
style={{
background: tabColor(props.currentTab, Path.UserSettings),
minWidth: 0,
padding: 0,
}}
id={`avatar-${idAffix}`}
>
<Hidden mdDown>{LocalStorage.getCurrentUser()?.username}</Hidden>
<Hidden mdDown>
<Typography style={{ margin: 5 }}>
{LocalStorage.getCurrentUser()?.username}
</Typography>
</Hidden>
{avatar ? (
<Avatar alt="User avatar" src={avatar} style={{ marginLeft: 5 }} />
) : (
Expand All @@ -76,7 +91,7 @@ export default function UserMenu(props: UserMenuProps): ReactElement {
>
<WrappedUserMenuList isAdmin={isAdmin} onSelect={handleClose} />
</Menu>
</React.Fragment>
</>
);
}

Expand Down
Loading