Skip to content

Commit

Permalink
[209] Improve the navbar used by the project views
Browse files Browse the repository at this point in the history
Bug: #209
Signed-off-by: Stéphane Bégaudeau <[email protected]>
  • Loading branch information
sbegaudeau committed Jul 23, 2023
1 parent c5e407a commit e844145
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 130 deletions.
8 changes: 1 addition & 7 deletions frontend/svalyn-studio-app/src/domains/DomainsShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,15 @@
*/

import Container from '@mui/material/Container';
import Link from '@mui/material/Link';
import Toolbar from '@mui/material/Toolbar';
import { Link as RouterLink } from 'react-router-dom';
import { Navbar } from '../navbars/Navbar';
import { DomainsShellProps } from './DomainsShell.types';

export const DomainsShell = ({ children }: DomainsShellProps) => {
return (
<>
<div>
<Navbar>
<Link component={RouterLink} to="/domains" color="inherit" underline="hover" fontWeight={800}>
Domains
</Link>
</Navbar>
<Navbar />
<Container maxWidth="lg">
<Toolbar />

Expand Down
137 changes: 26 additions & 111 deletions frontend/svalyn-studio-app/src/navbars/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Stéphane Bégaudeau.
* Copyright (c) 2022, 2023 Stéphane Bégaudeau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
Expand All @@ -18,35 +18,24 @@
*/

import { gql, useQuery } from '@apollo/client';
import HelpIcon from '@mui/icons-material/Help';
import HomeIcon from '@mui/icons-material/Home';
import LogoutIcon from '@mui/icons-material/Logout';
import MailOutlineIcon from '@mui/icons-material/MailOutline';
import HubOutlinedIcon from '@mui/icons-material/HubOutlined';
import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone';
import PersonIcon from '@mui/icons-material/Person';
import SearchIcon from '@mui/icons-material/Search';
import SettingsIcon from '@mui/icons-material/Settings';
import AppBar from '@mui/material/AppBar';
import Avatar from '@mui/material/Avatar';
import Badge from '@mui/material/Badge';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Link from '@mui/material/Link';
import Toolbar from '@mui/material/Toolbar';
import { useContext, useEffect, useState } from 'react';
import { Navigate, Link as RouterLink } from 'react-router-dom';
import { getCookie } from '../cookies/getCookie';
import { Link as RouterLink } from 'react-router-dom';
import { Svalyn } from '../icons/Svalyn';
import { PaletteContext } from '../palette/PaletteContext';
import { PaletteContextValue } from '../palette/PaletteContext.types';
import { ErrorSnackbar } from '../snackbar/ErrorSnackbar';
import { GetViewerData, GetViewerVariables, NavbarProps, NavbarState } from './Navbar.types';
import { SearchButton } from './SearchButton';
import { UserMenu } from './UserMenu';
const { VITE_BACKEND_URL } = import.meta.env;

const getViewerQuery = gql`
Expand All @@ -64,7 +53,6 @@ export const Navbar = ({ children }: NavbarProps) => {
const [state, setState] = useState<NavbarState>({
viewer: null,
anchorElement: null,
redirectToLogin: false,
message: null,
});

Expand Down Expand Up @@ -93,27 +81,6 @@ export const Navbar = ({ children }: NavbarProps) => {
};
const handleCloseUserMenu = () => setState((prevState) => ({ ...prevState, anchorElement: null }));

const handleLogout: React.MouseEventHandler<HTMLLIElement> = () => {
const csrfToken = getCookie('XSRF-TOKEN');

fetch(`${VITE_BACKEND_URL}/api/logout`, {
method: 'POST',
credentials: 'include',
mode: 'cors',
headers: {
'X-XSRF-TOKEN': csrfToken,
},
}).then(() => {
setState((prevState) => ({ ...prevState, redirectToLogin: true }));
});
};

if (state.redirectToLogin) {
return <Navigate to="/login" />;
}

var isApple = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);

return (
<>
<AppBar position="static">
Expand All @@ -133,32 +100,23 @@ export const Navbar = ({ children }: NavbarProps) => {
marginLeft: 'auto',
}}
>
<Button
sx={{ color: 'inherit', border: (theme) => `1px solid ${theme.palette.background.paper}` }}
startIcon={<SearchIcon fontSize="small" color="inherit" />}
onClick={handleOnSearchClick}
size="small"
<Link
component={RouterLink}
to="/domains"
color="inherit"
underline="hover"
fontWeight={800}
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: (theme) => theme.spacing(0.5),
}}
>
Search...
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
border: (theme) => `1px solid ${theme.palette.background.paper}`,
borderRadius: '3px',
marginLeft: (theme) => theme.spacing(4),
fontSize: '0.75rem',
fontWeight: '700',
lineHeight: '20px',
padding: '0px 4px',
fontFamily: 'sans-serif',
opacity: 0.7,
}}
>
{isApple ? '⌘ ' : 'Ctrl '}+ K
</Box>
</Button>
<HubOutlinedIcon fontSize="inherit" />
Domains
</Link>
<SearchButton onClick={handleOnSearchClick} />
<IconButton component={RouterLink} to="/notifications" size="small" color="inherit">
<Badge badgeContent={state.viewer.unreadNotificationsCount} color="secondary">
<NotificationsNoneIcon />
Expand All @@ -167,59 +125,16 @@ export const Navbar = ({ children }: NavbarProps) => {
<IconButton onClick={handleOpenUserMenu}>
<Avatar alt={state.viewer.name} src={state.viewer.imageUrl} sx={{ width: 24, height: 24 }} />
</IconButton>
<Menu
<UserMenu
name={state.viewer.name}
username={state.viewer.username}
open={Boolean(state.anchorElement)}
anchorEl={state.anchorElement}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
onClose={handleCloseUserMenu}
keepMounted
>
<ListItem sx={{ paddingTop: '0', paddingBottom: '0' }}>
<ListItemText primary="Signed in as" secondary={state.viewer.name} />
</ListItem>
<MenuItem component={RouterLink} to="/" onClick={handleCloseUserMenu}>
<ListItemIcon>
<HomeIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Dashboard</ListItemText>
</MenuItem>
<MenuItem
component={RouterLink}
to={`/profiles/${state.viewer.username}`}
onClick={handleCloseUserMenu}
>
<ListItemIcon>
<PersonIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Profile</ListItemText>
</MenuItem>
<MenuItem component={RouterLink} to="/invitations" onClick={handleCloseUserMenu}>
<ListItemIcon>
<MailOutlineIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Invitations</ListItemText>
</MenuItem>
<MenuItem component={RouterLink} to="/settings" onClick={handleCloseUserMenu}>
<ListItemIcon>
<SettingsIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Settings</ListItemText>
</MenuItem>
<MenuItem component={RouterLink} to="/help" onClick={handleCloseUserMenu}>
<ListItemIcon>
<HelpIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Help</ListItemText>
</MenuItem>
<Divider />
<MenuItem onClick={handleLogout}>
<ListItemIcon>
<LogoutIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Sign out</ListItemText>
</MenuItem>
</Menu>
/>
</Box>
</>
) : null}
Expand Down
1 change: 0 additions & 1 deletion frontend/svalyn-studio-app/src/navbars/Navbar.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export interface NavbarProps {
export interface NavbarState {
viewer: Viewer | null;
anchorElement: HTMLElement | null;
redirectToLogin: boolean;
message: string | null;
}

Expand Down
55 changes: 55 additions & 0 deletions frontend/svalyn-studio-app/src/navbars/SearchButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 Stéphane Bégaudeau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import SearchIcon from '@mui/icons-material/Search';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import { SearchButtonProps } from './SearchButton.types';

export const SearchButton = ({ onClick }: SearchButtonProps) => {
var isApple = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
return (
<Button
sx={{ color: 'inherit', border: (theme) => `1px solid ${theme.palette.background.paper}` }}
startIcon={<SearchIcon fontSize="small" color="inherit" />}
onClick={onClick}
size="small"
>
Search...
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
border: (theme) => `1px solid ${theme.palette.background.paper}`,
borderRadius: '3px',
marginLeft: (theme) => theme.spacing(4),
fontSize: '0.75rem',
fontWeight: '700',
lineHeight: '20px',
padding: '0px 4px',
fontFamily: 'sans-serif',
opacity: 0.7,
}}
>
{isApple ? '⌘ ' : 'Ctrl '}+ K
</Box>
</Button>
);
};
22 changes: 22 additions & 0 deletions frontend/svalyn-studio-app/src/navbars/SearchButton.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 Stéphane Bégaudeau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

export interface SearchButtonProps {
onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined;
}
Loading

0 comments on commit e844145

Please sign in to comment.