user menu and user settings can now be opened

This commit is contained in:
Carter 2024-11-27 10:44:08 -06:00
parent c94b8093ff
commit 5c79f9a633
4 changed files with 311 additions and 92 deletions

57
src/user/UserAvatar.tsx Normal file
View 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())}
/>
);
}

View file

@ -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 { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
import { makeStyles } from "@mui/styles";
import { LockOpen, Person, Lock, } from "@mui/icons-material";
import { useEffect, useState } from "react";
import { LockOpen, Person, Lock, Settings, SupervisedUserCircle as TeamIcon } from "@mui/icons-material";
import { useState } from "react";
import UserTeamName from "./UserTeamName";
import React from "react";
import ThemeIcon from "../common/ThemeIcon";
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) => ({
userAvatar: {
@ -18,6 +21,28 @@ const useStyles = makeStyles((theme: Theme) => ({
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: {
maxWidth: "50vw"
},
@ -48,101 +73,138 @@ interface Props {
export default function UserMenu(props: Props) {
const { toggleTheme } = props;
const [{ user, team }] = useGlobalState();
const { loginWithRedirect } = useAuth0();
const classes = useStyles();
const theme = useTheme();
const { toggleTheme } = props;
const [{ user, team, as }] = useGlobalState();
const { loginWithRedirect } = useAuth0();
const classes = useStyles();
const theme = useTheme();
const name = user.name();
const picture = user.settings.avatar;
const [lockIsHovered, setLockIsHovered] = 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 name = user.name();
const picture = user.settings.avatar;
const [lockIsHovered, setLockIsHovered] = useState<boolean>(false);
const [userMenuIsOpen, setUserMenuIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<any>(null);
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 = () => {
setLockIsHovered(true);
};
const lockHover = () => {
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 = () => {
setLockIsHovered(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 closeUserSettingsDialog = () => {
setUserSettingsIsOpen(false)
}
const unauthenticatedUserMenu = () => {
return (
<>
<React.Fragment>
<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 ? (
<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>
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()
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>
) : picture ? (
<Avatar alt={name} src={picture} className={classes.userAvatar}>
{!picture && name}
</Avatar>
) : ( */}
<Avatar alt={name} src={picture} className={classes.userAvatar}>
<Person />
</Avatar>
{/* )} */}
</Box>
</div>
) : picture ? (
<Avatar alt={name} src={picture} className={classes.userAvatar}>
{!picture && name}
</Avatar>
) : (
<Avatar alt={name} src={picture} className={classes.userAvatar}>
<Person />
</Avatar>
)}
</Box>
</Button>
<IconButton onClick={toggleTheme} className={classes.rightIcon} aria-label="Toggle Theme">
<ThemeIcon />
</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
View 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>
);
}