Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 118 additions & 11 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@ import {
} from "openstack-uicore-foundation/lib/security/methods";
import IdTokenVerifier from "idtoken-verifier";
import T from "i18n-react";
import { Breadcrumbs } from "react-breadcrumbs";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton";
import MenuIcon from "@mui/icons-material/Menu";
// eslint-disable-next-line
import * as Sentry from "@sentry/react";
import exclusiveSections from "./exclusive-sections.yml";
Expand Down Expand Up @@ -116,6 +122,8 @@ if (exclusiveSections.hasOwnProperty(process.env.APP_CLIENT_NAME)) {
window.EXCLUSIVE_SECTIONS = exclusiveSections[process.env.APP_CLIENT_NAME];
}

const MENU_CLOSE_DELAY_MS = 200;

if (window.SENTRY_DSN && window.SENTRY_DSN !== "") {
console.log("app init sentry ...");
// Initialize Sentry
Expand Down Expand Up @@ -146,6 +154,12 @@ class App extends React.PureComponent {
constructor(props) {
super(props);
props.resetLoading();
this.state = { menuOpen: false };
this.menuCloseTimeout = null;
this.toggleMenu = this.toggleMenu.bind(this);
this.openMenu = this.openMenu.bind(this);
this.cancelMenuClose = this.cancelMenuClose.bind(this);
this.scheduleMenuClose = this.scheduleMenuClose.bind(this);
}

onClickLogin() {
Expand All @@ -156,6 +170,34 @@ class App extends React.PureComponent {
this.props.getTimezones();
}

componentWillUnmount() {
this.cancelMenuClose();
}

toggleMenu() {
this.cancelMenuClose();
this.setState((prevState) => ({ menuOpen: !prevState.menuOpen }));
}

openMenu() {
this.cancelMenuClose();
this.setState({ menuOpen: true });
}

cancelMenuClose() {
if (this.menuCloseTimeout) {
clearTimeout(this.menuCloseTimeout);
this.menuCloseTimeout = null;
}
}

scheduleMenuClose() {
this.cancelMenuClose();
this.menuCloseTimeout = setTimeout(() => {
this.setState({ menuOpen: false });
}, MENU_CLOSE_DELAY_MS);
}

render() {
const {
isLoggedUser,
Expand All @@ -165,6 +207,7 @@ class App extends React.PureComponent {
backUrl,
loading
} = this.props;
const { menuOpen } = this.state;

const idToken = getIdToken();

Expand All @@ -188,23 +231,87 @@ class App extends React.PureComponent {
<Router history={history}>
<div>
<AjaxLoader show={loading} size={120} />
<div className="header" id="page-header">
<div className="header-title">
{T.translate("landing.os_summit_admin")}
<AuthButton
isLoggedUser={isLoggedUser}
picture={profile_pic}
doLogin={this.onClickLogin.bind(this)}
initLogOut={initLogOut}
/>
</div>
</div>
<AppBar
position="static"
id="page-header"
className="header"
elevation={0}
sx={{
bgcolor: "background.paper",
color: "text.primary",
borderBottom: "1px solid #b3b3b3"
}}
>
<Toolbar>
{isLoggedUser && (
<IconButton
edge="start"
aria-label={T.translate("menu.toggle_navigation")}
onClick={this.toggleMenu}
onMouseEnter={this.openMenu}
onMouseLeave={this.scheduleMenuClose}
sx={{ mr: 2 }}
>
<MenuIcon
sx={{ fontSize: "1.75rem", color: "#555555" }}
/>
</IconButton>
)}
<Typography
variant="h6"
component="div"
sx={{
flexGrow: 1,
...(!isLoggedUser && { textAlign: "center" })
}}
>
{T.translate("landing.os_summit_admin")}
</Typography>
{isLoggedUser && (
<AuthButton
isLoggedUser={isLoggedUser}
picture={profile_pic}
doLogin={this.onClickLogin.bind(this)}
initLogOut={initLogOut}
/>
)}
</Toolbar>
{isLoggedUser && (
<Toolbar
variant="dense"
sx={{
minHeight: 36,
borderTop: "1px solid #e0e0e0",
overflowX: "auto"
}}
>
<Breadcrumbs
className="breadcrumbs-wrapper"
separator="/"
/>
</Toolbar>
)}
</AppBar>
{!isLoggedUser && (
<AuthButton
isLoggedUser={isLoggedUser}
picture={profile_pic}
doLogin={this.onClickLogin.bind(this)}
initLogOut={initLogOut}
/>
)}
<Switch>
<AuthorizedRoute
isLoggedUser={isLoggedUser}
backUrl={backUrl}
path="/app"
component={PrimaryLayout}
componentProps={{
menuOpen,
toggleMenu: this.toggleMenu,
onMenuMouseEnter: this.cancelMenuClose,
onMenuMouseLeave: this.scheduleMenuClose
}}
/>
<AuthorizationCallbackRoute
onUserAuth={onUserAuth}
Expand Down
87 changes: 33 additions & 54 deletions src/components/auth-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,40 @@

import React from "react";
import T from "i18n-react/dist/i18n-react";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";

export default class AuthButton extends React.Component {
constructor(props) {
super(props);

this.state = {
showLogOut: false
};

this.toggleLogOut = this.toggleLogOut.bind(this);
const AuthButton = ({ isLoggedUser, doLogin, initLogOut, picture }) => {
if (isLoggedUser) {
return (
<Button
variant="outlined"
color="inherit"
startIcon={<Avatar src={picture} sx={{ width: 24, height: 24 }} />}
onClick={() => {
initLogOut();
}}
>
{T.translate("landing.sign_out")}
</Button>
);
}

toggleLogOut(ev) {
this.setState({ showLogOut: !this.state.showLogOut });
}
return (
<div className="login">
{T.translate("landing.not_logged_in")}
<br />
<br />
<button
className="btn btn-primary btn-lg"
onClick={() => {
doLogin();
}}
>
{T.translate("landing.log_in")}
</button>
</div>
);
};

render() {
let { isLoggedUser, doLogin, initLogOut, picture } = this.props;
let { showLogOut } = this.state;

if (isLoggedUser) {
return (
<div className="user-menu" onClick={this.toggleLogOut}>
<div
className="profile-pic"
style={{ backgroundImage: `url(${picture})` }}
/>
{showLogOut && (
<button
className="btn btn-default logout"
onClick={() => {
initLogOut();
}}
>
{T.translate("landing.sign_out")}
</button>
)}
</div>
);
} else {
return (
<div className="login">
{T.translate("landing.not_logged_in")}
<br />
<br />
<button
className="btn btn-primary btn-lg"
onClick={() => {
doLogin();
}}
>
{T.translate("landing.log_in")}
</button>
</div>
);
}
}
}
export default AuthButton;
90 changes: 41 additions & 49 deletions src/components/menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,36 @@
* limitations under the License.
* */

import React, { useState } from "react";
import React from "react";
import T from "i18n-react/dist/i18n-react";
import { withRouter } from "react-router-dom";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import Divider from "@mui/material/Divider";
import IconButton from "@mui/material/IconButton";
import MenuIcon from "@mui/icons-material/Menu";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import SubMenuItem from "./sub-menu-item";
import MenuItem from "./menu-item";
import ExpandableItem from "./expandable-item";
import Member from "../../models/member";
import { getGlobalItems, getSummitItems } from "./menu-definition";

import styles from "./menu.module.less";
const DRAWER_WIDTH = 260;

const Menu = ({ currentSummit, member, history }) => {
const [menuOpen, setMenuOpen] = useState(false);
const Menu = ({
currentSummit,
member,
history,
menuOpen,
toggleMenu,
onMenuMouseEnter,
onMenuMouseLeave
}) => {
const memberObj = new Member(member);
const globalItems = getGlobalItems();
const summitItems = currentSummit ? getSummitItems(currentSummit.id) : [];

const closeMenu = () => setMenuOpen(false);
const closeMenu = () => {
if (menuOpen) toggleMenu();
};

const onMenuItemClick = (ev, url) => {
ev.preventDefault();
Expand Down Expand Up @@ -73,52 +80,37 @@ const Menu = ({ currentSummit, member, history }) => {
};

return (
<>
{menuOpen && (
<Box
sx={{ position: "fixed", inset: 0, zIndex: 99998 }}
onClick={closeMenu}
onTouchStart={closeMenu}
/>
)}
<Drawer
anchor="left"
open={Boolean(menuOpen)}
onClose={closeMenu}
slotProps={{
root: { keepMounted: true },
paper: {
sx: { width: DRAWER_WIDTH },
onMouseEnter: onMenuMouseEnter,
onMouseLeave: onMenuMouseLeave
}
}}
>
<Box
className={`${styles.wrapper} ${
styles[menuOpen ? "opened" : "closed"]
}`}
role="presentation"
sx={{ width: DRAWER_WIDTH, overflowY: "auto", pb: 3 }}
>
<Box className={styles.burgerButton}>
<IconButton
onClick={() => (menuOpen ? closeMenu() : setMenuOpen(true))}
>
<MenuIcon sx={{ fontSize: "2.5rem", color: "#555555" }} />
</IconButton>
</Box>
<Box
className={styles.menuWrapper}
onMouseEnter={() => setMenuOpen(true)}
onMouseLeave={() => setMenuOpen(false)}
>
<Box className={styles.expandButton}>
<ChevronRightIcon sx={{ fontSize: "2em", color: "#555555" }} />
</Box>
<ExpandableItem label={T.translate("menu.general")} isHeader>
{globalItems.map(drawMenuItem)}
</ExpandableItem>

<Box className={styles.menuItemsWrapper}>
<ExpandableItem label={T.translate("menu.general")} isHeader>
{globalItems.map(drawMenuItem)}
{!!currentSummit?.id && (
<>
<Divider sx={{ my: 2 }} />
<ExpandableItem label={currentSummit.name} isHeader>
{summitItems.map(drawMenuItem)}
</ExpandableItem>

{!!currentSummit?.id && (
<>
<Divider sx={{ my: 2 }} />
<ExpandableItem label={currentSummit.name} isHeader>
{summitItems.map(drawMenuItem)}
</ExpandableItem>
</>
)}
</Box>
</Box>
</>
)}
</Box>
</>
</Drawer>
);
};

Expand Down
Loading
Loading