user menu and user settings can now be opened
This commit is contained in:
parent
c94b8093ff
commit
5c79f9a633
4 changed files with 311 additions and 92 deletions
|
|
@ -27,6 +27,7 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
||||||
|
|
||||||
const signature = getSignatureColour();
|
const signature = getSignatureColour();
|
||||||
const accent = getSignatureAccentColour();
|
const accent = getSignatureAccentColour();
|
||||||
|
const highlight = themeType === "light" ? "black" : "white";
|
||||||
const bg = generateBackgroundShades(themeType)
|
const bg = generateBackgroundShades(themeType)
|
||||||
return {
|
return {
|
||||||
palette: {
|
palette: {
|
||||||
|
|
@ -195,13 +196,10 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
'& .MuiTouchRipple-root': {
|
'& .MuiTouchRipple-root': {
|
||||||
color: accent, // Customize ripple color here
|
color: highlight,
|
||||||
// color: "white"
|
|
||||||
},
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
// color: themeType === "light" ? signature : "black",
|
color: highlight
|
||||||
// color: "white"
|
|
||||||
color: accent
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
57
src/user/UserAvatar.tsx
Normal file
57
src/user/UserAvatar.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { Avatar } from "@mui/material";
|
||||||
|
import { Team, User } from "models";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function stringToColor(string: string) {
|
||||||
|
let hash = 0;
|
||||||
|
let i;
|
||||||
|
|
||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
for (i = 0; i < string.length; i += 1) {
|
||||||
|
hash = string.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = "#";
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i += 1) {
|
||||||
|
const value = (hash >> (i * 8)) & 0xff;
|
||||||
|
color += `00${value.toString(16)}`.slice(-2);
|
||||||
|
}
|
||||||
|
/* eslint-enable no-bitwise */
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringAvatar(name: string) {
|
||||||
|
let letters = "";
|
||||||
|
if (name.split(" ").length > 1) {
|
||||||
|
letters = name.split(" ")[0][0] + name.split(" ")[1][0];
|
||||||
|
} else {
|
||||||
|
letters = name.split(" ")[0][0];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sx: {
|
||||||
|
bgcolor: stringToColor(name)
|
||||||
|
},
|
||||||
|
children: `${letters}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
user: User | Team;
|
||||||
|
className?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UserSettings(props: Props) {
|
||||||
|
const { user, className, style } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
alt={user.name()}
|
||||||
|
src={user.settings.avatar}
|
||||||
|
className={className}
|
||||||
|
style={style}
|
||||||
|
{...stringAvatar(user.name())}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
import { Avatar, Box, Button, IconButton, PaletteColor, Theme, Typography, useTheme } from "@mui/material";
|
import { Avatar, Box, Button, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, PaletteColor, Theme, Typography, useTheme } from "@mui/material";
|
||||||
import { useGlobalState } from "../providers/StateContainer";
|
import { useGlobalState } from "../providers/StateContainer";
|
||||||
import { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
|
import { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { LockOpen, Person, Lock, } from "@mui/icons-material";
|
import { LockOpen, Person, Lock, Settings, SupervisedUserCircle as TeamIcon } from "@mui/icons-material";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import UserTeamName from "./UserTeamName";
|
import UserTeamName from "./UserTeamName";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ThemeIcon from "../common/ThemeIcon";
|
import ThemeIcon from "../common/ThemeIcon";
|
||||||
import { useAuth0 } from "@auth0/auth0-react";
|
import { useAuth0 } from "@auth0/auth0-react";
|
||||||
|
import UserSettings from "./UserSettings";
|
||||||
|
import UserAvatar from "./UserAvatar";
|
||||||
|
import { purple } from "@mui/material/colors";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
userAvatar: {
|
userAvatar: {
|
||||||
|
|
@ -18,6 +21,28 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
height: "32px"
|
height: "32px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
userAvatar2: {
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: theme.palette.secondary["700" as keyof PaletteColor],
|
||||||
|
[theme.breakpoints.down("xs")]: {
|
||||||
|
width: "30px",
|
||||||
|
height: "30px"
|
||||||
|
},
|
||||||
|
position: "relative",
|
||||||
|
top: -6,
|
||||||
|
left: 0
|
||||||
|
},
|
||||||
|
teamAvatar: {
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: theme.palette.secondary["700" as keyof PaletteColor],
|
||||||
|
[theme.breakpoints.down("xs")]: {
|
||||||
|
width: "30px",
|
||||||
|
height: "30px"
|
||||||
|
},
|
||||||
|
position: "absolute",
|
||||||
|
top: -34,
|
||||||
|
left: 8
|
||||||
|
},
|
||||||
profileButton: {
|
profileButton: {
|
||||||
maxWidth: "50vw"
|
maxWidth: "50vw"
|
||||||
},
|
},
|
||||||
|
|
@ -48,101 +73,138 @@ interface Props {
|
||||||
|
|
||||||
export default function UserMenu(props: Props) {
|
export default function UserMenu(props: Props) {
|
||||||
|
|
||||||
const { toggleTheme } = props;
|
const { toggleTheme } = props;
|
||||||
const [{ user, team }] = useGlobalState();
|
const [{ user, team, as }] = useGlobalState();
|
||||||
const { loginWithRedirect } = useAuth0();
|
const { loginWithRedirect } = useAuth0();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const name = user.name();
|
const name = user.name();
|
||||||
const picture = user.settings.avatar;
|
const picture = user.settings.avatar;
|
||||||
const [lockIsHovered, setLockIsHovered] = useState<boolean>(false);
|
const [lockIsHovered, setLockIsHovered] = useState<boolean>(false);
|
||||||
// const hasAdmin = user.hasFeature ? user.hasFeature("admin") : false;
|
const [userMenuIsOpen, setUserMenuIsOpen] = useState(false);
|
||||||
// const allowedToCopyToken = user.allowedTo("copy-token");
|
const [anchorEl, setAnchorEl] = useState<any>(null);
|
||||||
// const hasTeams = user.hasFeature ? user.hasFeature("teams") : false;
|
const [userSettingsIsOpen, setUserSettingsIsOpen] = useState<boolean>(false);
|
||||||
|
// const hasAdmin = user.hasFeature ? user.hasFeature("admin") : false;
|
||||||
|
// const allowedToCopyToken = user.allowedTo("copy-token");
|
||||||
|
const hasTeams = user.hasFeature ? user.hasFeature("teams") : false;
|
||||||
|
|
||||||
const lockHover = () => {
|
const lockHover = () => {
|
||||||
setLockIsHovered(true);
|
setLockIsHovered(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lockNoHover = () => {
|
||||||
|
setLockIsHovered(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
loginWithRedirect()
|
||||||
|
}
|
||||||
|
|
||||||
|
const openUserMenu = (event: any) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
setUserMenuIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeUserMenu = () => {
|
||||||
|
setUserMenuIsOpen(false);
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openUserSettingsDialog = () => {
|
||||||
|
setUserMenuIsOpen(false);
|
||||||
|
setAnchorEl(null);
|
||||||
|
setUserSettingsIsOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
const lockNoHover = () => {
|
const closeUserSettingsDialog = () => {
|
||||||
setLockIsHovered(false);
|
setUserSettingsIsOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLogin = () => {
|
|
||||||
loginWithRedirect()
|
|
||||||
}
|
|
||||||
|
|
||||||
const unauthenticatedUserMenu = () => {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
aria-label="Sign In"
|
|
||||||
onClick={handleLogin}
|
|
||||||
onMouseEnter={lockHover}
|
|
||||||
onMouseLeave={lockNoHover}
|
|
||||||
size="small"
|
|
||||||
className={classes.signIn}>
|
|
||||||
Sign In
|
|
||||||
{lockIsHovered ? (
|
|
||||||
<LockOpen className={classes.rightIcon} fontSize="small" />
|
|
||||||
) : (
|
|
||||||
<Lock className={classes.rightIcon} fontSize="small" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<IconButton onClick={toggleTheme} className={classes.rightIcon} aria-label="Toggle Theme">
|
|
||||||
<ThemeIcon />
|
|
||||||
</IconButton>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (user.id().length < 1) return unauthenticatedUserMenu()
|
|
||||||
|
|
||||||
|
const unauthenticatedUserMenu = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<React.Fragment>
|
||||||
<Button
|
<Button
|
||||||
id="tour-user-menu"
|
variant="outlined"
|
||||||
// aria-owns={userMenuIsOpen ? "userMenu" : undefined}
|
aria-label="Sign In"
|
||||||
aria-haspopup="true"
|
onClick={handleLogin}
|
||||||
// onClick={openUserMenu}
|
onMouseEnter={lockHover}
|
||||||
className={classes.profileButton}>
|
onMouseLeave={lockNoHover}
|
||||||
<Box style={{ display: "flex", flexDirection: "column" }}>
|
size="small"
|
||||||
<UserTeamName user={user} team={team} />
|
className={classes.signIn}>
|
||||||
</Box>
|
Sign In
|
||||||
<Box style={{ marginLeft: theme.spacing(1) }}>
|
{lockIsHovered ? (
|
||||||
{/* {picture && hasTeams ? (
|
<LockOpen className={classes.rightIcon} fontSize="small" />
|
||||||
<div>
|
) : (
|
||||||
<UserAvatar
|
<Lock className={classes.rightIcon} fontSize="small" />
|
||||||
user={user}
|
)}
|
||||||
className={classes.userAvatar2}
|
</Button>
|
||||||
style={{ zIndex: as === "" ? 1000 : 0 }}
|
|
||||||
/>
|
<IconButton onClick={toggleTheme} className={classes.rightIcon} aria-label="Toggle Theme">
|
||||||
<div style={{ position: "relative" }}>
|
<ThemeIcon />
|
||||||
<Avatar
|
</IconButton>
|
||||||
src={team.settings.avatar}
|
</React.Fragment>
|
||||||
className={classes.teamAvatar}
|
);
|
||||||
style={{ background: purple[500] }}>
|
};
|
||||||
<TeamIcon style={{ fontSize: 38 }} />
|
|
||||||
</Avatar>
|
if (user.id().length < 1) return unauthenticatedUserMenu()
|
||||||
</div>
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
id="tour-user-menu"
|
||||||
|
aria-owns={userMenuIsOpen ? "userMenu" : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
onClick={openUserMenu}
|
||||||
|
className={classes.profileButton}>
|
||||||
|
<Box style={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<UserTeamName user={user} team={team} />
|
||||||
|
</Box>
|
||||||
|
<Box style={{ marginLeft: theme.spacing(1) }}>
|
||||||
|
{picture && hasTeams && team.id().length > 0 ? (
|
||||||
|
<div>
|
||||||
|
<UserAvatar
|
||||||
|
user={user}
|
||||||
|
className={classes.userAvatar2}
|
||||||
|
style={{ zIndex: as === "" ? 1000 : 0 }}
|
||||||
|
/>
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<Avatar
|
||||||
|
src={team.settings.avatar}
|
||||||
|
className={classes.teamAvatar}
|
||||||
|
style={{ background: purple[500] }}>
|
||||||
|
<TeamIcon style={{ fontSize: 38 }} />
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
) : picture ? (
|
</div>
|
||||||
<Avatar alt={name} src={picture} className={classes.userAvatar}>
|
) : picture ? (
|
||||||
{!picture && name}
|
<Avatar alt={name} src={picture} className={classes.userAvatar}>
|
||||||
</Avatar>
|
{!picture && name}
|
||||||
) : ( */}
|
</Avatar>
|
||||||
<Avatar alt={name} src={picture} className={classes.userAvatar}>
|
) : (
|
||||||
<Person />
|
<Avatar alt={name} src={picture} className={classes.userAvatar}>
|
||||||
</Avatar>
|
<Person />
|
||||||
{/* )} */}
|
</Avatar>
|
||||||
</Box>
|
)}
|
||||||
|
</Box>
|
||||||
</Button>
|
</Button>
|
||||||
<IconButton onClick={toggleTheme} className={classes.rightIcon} aria-label="Toggle Theme">
|
<IconButton onClick={toggleTheme} className={classes.rightIcon} aria-label="Toggle Theme">
|
||||||
<ThemeIcon />
|
<ThemeIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<Menu
|
||||||
|
id="userMenu"
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={userMenuIsOpen}
|
||||||
|
onClose={closeUserMenu}
|
||||||
|
disableAutoFocusItem>
|
||||||
|
<MenuItem onClick={openUserSettingsDialog} aria-label="Open User Settings" dense>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Settings />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="User Settings" />
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
<UserSettings isOpen={userSettingsIsOpen} closeDialogCallback={closeUserSettingsDialog} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
102
src/user/UserSettings.tsx
Normal file
102
src/user/UserSettings.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { Close } from "@mui/icons-material";
|
||||||
|
import { AppBar, Button, IconButton, Theme, Toolbar, Typography } from "@mui/material";
|
||||||
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
import UserAvatar from "./UserAvatar";
|
||||||
|
import { useGlobalState, useUserAPI } from "providers";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import { blue } from "@mui/material/colors";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
return ({
|
||||||
|
title: {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
marginRight: theme.spacing(2),
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
checkboxIcon: {
|
||||||
|
minWidth: theme.spacing(5),
|
||||||
|
[theme.breakpoints.up("sm")]: {
|
||||||
|
minWidth: theme.spacing(6)
|
||||||
|
},
|
||||||
|
[theme.breakpoints.up("md")]: {
|
||||||
|
minWidth: theme.spacing(7)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shareIcon: {
|
||||||
|
color: blue["600"],
|
||||||
|
"&:hover": {
|
||||||
|
color: blue["700"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
|
margin: theme.spacing(1),
|
||||||
|
position: "absolute",
|
||||||
|
right: theme.spacing(6)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
isOpen: boolean;
|
||||||
|
closeDialogCallback: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UserSettings(props: Props) {
|
||||||
|
const { isOpen, closeDialogCallback } = props;
|
||||||
|
const [{ user }, dispatch] = useGlobalState()
|
||||||
|
const userAPI = useUserAPI();
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
userAPI
|
||||||
|
.updateUser(user.id(), user.protobuf())
|
||||||
|
.then(() => {
|
||||||
|
dispatch({ key: "user", value: user });
|
||||||
|
// success("User settings successfully updated!");
|
||||||
|
// setTemperatureUnit(user.settings.temperatureUnit);
|
||||||
|
// setPressureUnit(user.settings.pressureUnit);
|
||||||
|
// setDistanceUnit(user.settings.distanceUnit);
|
||||||
|
// setGrainUnit(user.settings.grainUnit);
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
error("Error occurred while updating your profile");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
closeDialogCallback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
// setDefaultState();
|
||||||
|
closeDialogCallback();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponsiveDialog
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
open={isOpen}
|
||||||
|
onClose={closeDialog}
|
||||||
|
aria-labelledby="user-settings-dialog">
|
||||||
|
<AppBar position="relative">
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton edge="start" color="inherit" onClick={closeDialog} aria-label="close">
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
<UserAvatar user={user} />
|
||||||
|
<Typography variant="h6" className={classes.title}>
|
||||||
|
User Settings
|
||||||
|
</Typography>
|
||||||
|
<Button color="inherit" onClick={submit}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
{/* {settingsList()} */}
|
||||||
|
</ResponsiveDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue