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

Add profile menu + reorganize sidebar/header #421

Merged
merged 6 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions site/src/component/AppHeader/AppHeader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
}
}

.school-term {
margin-bottom: 0;
margin-right: 0.75em;
}

.navbar {
position: sticky;
z-index: 300;
Expand Down
13 changes: 9 additions & 4 deletions site/src/component/AppHeader/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import './AppHeader.scss';

import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { setSidebarStatus } from '../../store/slices/uiSlice';
import Profile from './Profile';
import { isDesktop, isMobile } from 'react-device-detect';

const AppHeader: FC = () => {
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -43,9 +45,11 @@ const AppHeader: FC = () => {
<div className="navbar-nav">
<div className="navbar-left">
{/* Hamburger Menu */}
<div className="navbar-menu">
<List className="navbar-menu-icon" onClick={toggleMenu} />
</div>
{isMobile && (
<div className="navbar-menu">
<List className="navbar-menu-icon" onClick={toggleMenu} />
</div>
)}

{/* Toggle Course and Professor */}
{(coursesActive || professorsActive) && (
Expand Down Expand Up @@ -82,7 +86,7 @@ const AppHeader: FC = () => {
</div>

{/* Week */}
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center', height: '100%' }}>
<div className="beta" style={{ margin: 'auto 12px' }}>
<Popup
className="beta-popup"
Expand Down Expand Up @@ -135,6 +139,7 @@ const AppHeader: FC = () => {
<p className="school-term" style={{ height: '1rem', lineHeight: '1rem' }}>
{week}
</p>
{isDesktop && <Profile />}
</div>
</div>
</header>
Expand Down
136 changes: 136 additions & 0 deletions site/src/component/AppHeader/Profile.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
.navbar-profile {
height: 70%;
}

.navbar-profile-pic {
height: 100%;
border-radius: 100%;

&:hover {
cursor: pointer;
}
}

#profile-popover,
#theme-popover {
font-size: 1rem;
padding: 1em;
background-color: var(--overlay1);
border-radius: var(--border-radius);
border: 1px solid var(--background);
max-width: 94vw;

.arrow::before {
border-bottom-color: var(--background);
}

h1 {
font-size: 1.5em;
}

h2 {
font-size: 1em;
color: var(--text-light);
}
}

.profile-popover__header {
display: flex;
min-height: 6vh;
margin-bottom: 0.75em;

img {
margin-right: 0.75em;
height: 6vh;
border-radius: 100%;
}

button {
border: none;
background: none;
font-size: 1.5em;
line-height: 1em;
padding: 0 0.5em;
border-radius: var(--border-radius);
}

button:hover {
background-color: var(--overlay2);
}
}

#theme-popover {
padding: 0.5em;

ul {
list-style: none;
padding: 0;
margin: 0;
}

li div {
display: inline;
}

a {
color: var(--text);
}
}

.profile-popover__links {
ul {
list-style: none;
padding: 0;
margin: 0;
}

li div {
display: inline;
}

a {
color: var(--text);
}

button {
border: none;
background: none;
width: 100%;
}

.divider-before::before {
display: block;
content: '';
width: 100%;
height: 1px;
background-color: var(--overlay2);
margin: 0.25em 0;
}
}

.profile-popover__link,
.theme-popover__link {
display: flex;
gap: 10px;
padding: 10px;
border-radius: var(--border-radius);

&:hover {
background-color: var(--overlay2);
}
}

.profile-popover__link.active,
.theme-popover__link.active {
color: var(--peterportal-primary-color-1);
}

i.login-icon {
margin-right: 0.75em;
}

.theme-button:hover,
.theme-popover__link:hover {
cursor: pointer;
text-decoration: underline;
}
164 changes: 164 additions & 0 deletions site/src/component/AppHeader/Profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { useCookies } from 'react-cookie';
import { useContext, useEffect, useState } from 'react';
import ThemeContext from '../../style/theme-context';
import { Button, OverlayTrigger, Popover } from 'react-bootstrap';
import { Icon } from 'semantic-ui-react';
import './Profile.scss';
import { NavLink } from 'react-router-dom';
import { ArrowLeftCircleFill } from 'react-bootstrap-icons';

type ProfileMenuTab = 'default' | 'theme';

const Profile = () => {
const { darkMode, setTheme, usingSystemTheme } = useContext(ThemeContext);
const [cookies] = useCookies(['user']);
const [show, setShow] = useState(false);
const [tab, setTab] = useState<ProfileMenuTab>('default');

const { name, email, picture }: { name: string; email: string; picture: string } = cookies?.user ?? {};
const isLoggedIn: boolean = cookies.user;

useEffect(() => {
if (!show) {
setTimeout(() => setTab('default'), 150); // popover transition time is 150ms
}
}, [show]);

const DefaultTab = (
<>
<div className="profile-popover__header">
<img src={picture} alt={name} />
<span>
<h1>{name}</h1>
<h2>{email}</h2>
</span>
</div>
<div className="profile-popover__links">
<ul>
<li className="divider-before">
<NavLink
className={({ isActive }) => 'profile-popover__link' + (isActive ? ' active' : '')}
to="/reviews"
onClick={() => setShow(false)}
>
<div>
<Icon name="sticky note outline" size="large" />
</div>
Your Reviews
</NavLink>
</li>
<li>
<button className="theme-button profile-popover__link" onClick={() => setTab('theme')}>
<div>
<Icon name={usingSystemTheme ? 'laptop' : darkMode ? 'moon outline' : 'sun outline'} size="large" />
</div>
Theme
</button>
</li>
<li className="divider-before">
<a href={`/api/users/logout`} className="profile-popover__link">
<div>
<Icon name="sign out" size="large" />
</div>
Log Out
</a>
</li>
</ul>
</div>
</>
);

const ThemeTab = (
<>
<div className="profile-popover__header">
<button onClick={() => setTab('default')}>
<ArrowLeftCircleFill />
</button>
</div>
<div className="profile-popover__links">
<ul>
<li className="divider-before">
<button
className={`theme-popover__link${!usingSystemTheme && !darkMode ? ' active' : ''}`}
onClick={() => setTheme('light')}
>
<div>
<Icon name="sun outline" size="large" />
</div>
Light
</button>
</li>
<li>
<button
className={`theme-popover__link${!usingSystemTheme && darkMode ? ' active' : ''}`}
onClick={() => setTheme('dark')}
>
<div>
<Icon name="moon outline" size="large" />
</div>
Dark
</button>
</li>
<li>
<button
className={`theme-popover__link${usingSystemTheme ? ' active' : ''}`}
onClick={() => setTheme('system')}
>
<div>
<Icon name="laptop" size="large" />
</div>
System
</button>
</li>
</ul>
</div>
</>
);

const ProfilePopover = (
<Popover id="profile-popover">
{tab === 'default' && DefaultTab}
{tab === 'theme' && ThemeTab}
</Popover>
);

return (
<>
{isLoggedIn ? (
<div className="navbar-profile">
<OverlayTrigger
rootClose
trigger="click"
placement="bottom"
overlay={ProfilePopover}
show={show}
onToggle={setShow}
popperConfig={{
modifiers: [
{
name: 'offset',
options: {
offset: [-100, 15],
},
},
],
}}
>
<img src={picture} alt={name} className="navbar-profile-pic" />
</OverlayTrigger>
</div>
) : (
<a href={`/api/users/auth/google`}>
<Button variant={darkMode ? 'dark' : 'light'}>
<span>
<Icon name="sign in" className="login-icon" />
</span>
Log In
</Button>
</a>
)}
</>
);
};

export default Profile;
Loading
Loading