💄 Initial UI Root layout

Signed-off-by: Pau Costa <mico@micodev.es>
pull/2/head
Pau Costa Ferrer 2024-02-08 13:27:01 +01:00
parent c71bac5bd2
commit 31ba7bead1
8 changed files with 353 additions and 1655 deletions

1710
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
"@emotion/react": "^11.11.3", "@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8", "@fontsource/roboto": "^5.0.8",
"@mui/icons-material": "^5.15.7", "@mui/icons-material": "^5.15.8",
"@mui/material": "^5.15.7", "@mui/material": "^5.15.7",
"@reduxjs/toolkit": "^2.1.0", "@reduxjs/toolkit": "^2.1.0",
"axios": "^1.6.7", "axios": "^1.6.7",

View File

@ -0,0 +1,23 @@
import { useEffect, useState } from "react";
function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return { width, height };
}
export default function useWindowDimensions() {
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
);
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowDimensions;
}

View File

@ -0,0 +1,30 @@
import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material";
import { useState } from "react";
import FeedIcon from "@mui/icons-material/Feed";
import GlobalIcon from "@mui/icons-material/Public";
import AddIcon from "@mui/icons-material/Add";
import SearchIcon from "@mui/icons-material/Search";
export default function BottomAppBar() {
const [value, setValue] = useState(0);
return (
<Paper
sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}
elevation={8}
>
<BottomNavigation
showLabels
value={value}
onChange={(event, newValue) => {
setValue(newValue);
}}
>
<BottomNavigationAction label="My Feed" icon={<FeedIcon />} />
<BottomNavigationAction label="Global Feed" icon={<GlobalIcon />} />
<BottomNavigationAction label="New Post" icon={<AddIcon />} />
<BottomNavigationAction label="Search" icon={<SearchIcon />} />
</BottomNavigation>
</Paper>
);
}

View File

@ -0,0 +1,12 @@
import NotificationsIcon from "@mui/icons-material/Notifications";
import { Badge, Tooltip } from "@mui/material";
export default function NotificationBell() {
return (
<Badge badgeContent={4} color="secondary">
<Tooltip title="Notifications">
<NotificationsIcon />
</Tooltip>
</Badge>
);
}

View File

@ -0,0 +1,79 @@
import {
Divider,
Drawer,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Toolbar,
} from "@mui/material";
import FeedIcon from "@mui/icons-material/Feed";
import GlobalIcon from "@mui/icons-material/Public";
import AddIcon from "@mui/icons-material/Add";
import SearchIcon from "@mui/icons-material/Search";
export interface SideAppBarProps {
drawerWidth: number;
}
export default function SideAppBar(props: SideAppBarProps) {
const { drawerWidth } = props;
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
},
}}
variant="permanent"
anchor="left"
>
<Toolbar />
<Divider />
<List>
<ListItem key={"myfeed"} disablePadding>
<ListItemButton>
<ListItemIcon>
<FeedIcon />
</ListItemIcon>
<ListItemText primary={"My feed"} />
</ListItemButton>
</ListItem>
<ListItem key={"globalfeed"} disablePadding>
<ListItemButton>
<ListItemIcon>
<GlobalIcon />
</ListItemIcon>
<ListItemText primary={"Global feed"} />
</ListItemButton>
</ListItem>
<ListItem key={"newpost"} disablePadding>
<ListItemButton>
<ListItemIcon>
<AddIcon />
</ListItemIcon>
<ListItemText primary={"New Post"} />
</ListItemButton>
</ListItem>
<ListItem key={"search"} disablePadding>
<ListItemButton>
<ListItemIcon>
<SearchIcon />
</ListItemIcon>
<ListItemText primary={"Search"} />
</ListItemButton>
</ListItem>
</List>
</Drawer>
);
}

View File

@ -0,0 +1,101 @@
import * as React from "react";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import Menu from "@mui/material/Menu";
import Container from "@mui/material/Container";
import Avatar from "@mui/material/Avatar";
import Tooltip from "@mui/material/Tooltip";
import MenuItem from "@mui/material/MenuItem";
import NotificationBell from "./notificationBell";
import {useSelector} from "react-redux";
import {selectUserInfo} from "../app/loginSlice";
const settings = ["Profile", "Account", "Dashboard", "Logout"];
function TopAppBar() {
const userInfo = useSelector(selectUserInfo);
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
null
);
console.log(userInfo)
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
return (
<AppBar position="static">
<Container maxWidth="xl" sx={{ zIndex: 2500 }}>
<Toolbar disableGutters>
<Typography
variant="h6"
noWrap
component="a"
href="#app-bar-with-responsive-menu"
sx={{
mr: 2,
display: "flex",
fontFamily: "monospace",
fontWeight: 700,
letterSpacing: ".3rem",
color: "inherit",
textDecoration: "none",
}}
>
DevSpace
</Typography>
<Box
sx={{
flexGrow: 1,
display: "flex",
justifyContent: "right",
padding: "1rem",
}}
>
<NotificationBell />
</Box>
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar alt={userInfo.firstName} src="/static/images/avatar/2.jpg" />
</IconButton>
</Tooltip>
<Menu
sx={{ mt: "45px" }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{settings.map((setting) => (
<MenuItem key={setting} onClick={handleCloseUserMenu}>
<Typography textAlign="center">{setting}</Typography>
</MenuItem>
))}
</Menu>
</Box>
</Toolbar>
</Container>
</AppBar>
);
}
export default TopAppBar;

View File

@ -1,16 +1,21 @@
import ResponsiveDrawer from "../components/Drawer"; import { Outlet, useNavigate } from "react-router-dom";
import { Outlet, useActionData, useNavigate } from "react-router-dom"; import { Box } from "@mui/material";
import { Button, Grid } from "@mui/material";
import { postLogout, selectLoggedIn, selectUserInfo } from "../app/loginSlice"; import { postLogout, selectLoggedIn, selectUserInfo } from "../app/loginSlice";
import { useAppDispatch } from "../app/store"; import { useAppDispatch } from "../app/store";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useEffect } from "react"; import { useEffect } from "react";
import useWindowDimensions from "../app/hooks/useWindowDimensions";
import SideAppBar from "../components/sideAppBar";
import BottomAppBar from "../components/bottomAppBar";
import TopAppBar from "../components/topAppBar";
const drawerWidth = 240;
export default function Root() { export default function Root() {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const loggedIn = useSelector(selectLoggedIn); const loggedIn = useSelector(selectLoggedIn);
const userInfo = useSelector(selectUserInfo); const { width } = useWindowDimensions();
useEffect(() => { useEffect(() => {
if (!loggedIn) { if (!loggedIn) {
@ -24,9 +29,39 @@ export default function Root() {
return ( return (
<> <>
<Button color="primary" variant="contained" onClick={handleClick}> <Box>
Log Off <TopAppBar />
</Button> </Box>
<Box
sx={{
display: "flex",
flexWrap: "wrap",
flexDirection: { xs: "column", sm: "row" },
}}
>
{width > 600 && (
<Box sx={{ flexBasis: { sm: "auto" } }}>
<SideAppBar drawerWidth={drawerWidth} />
</Box>
)}
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - ${width > 600 ? drawerWidth : 0}px)` },
}}
>
<Outlet />
</Box>
{width <= 600 && (
<Box sx={{ flexBasis: "100%" }}>
<BottomAppBar />
</Box>
)}
</Box>
</> </>
); );
} }