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

Update CodePair Workspace Page Layout with Recent Figma Design #207

Merged
merged 11 commits into from
Jun 21, 2024
162 changes: 48 additions & 114 deletions frontend/src/components/drawers/WorkspaceDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,39 @@
import {
Avatar,
Button,
Divider,
Drawer,
IconButton,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
Stack,
} from "@mui/material";
import MoreVertIcon from "@mui/icons-material/MoreVert";
import { useSelector } from "react-redux";
import { selectUser } from "../../store/userSlice";
import { MouseEventHandler, useState } from "react";
import ProfilePopover from "../popovers/ProfilePopover";
import { useNavigate } from "react-router-dom";
import { MouseEventHandler, useMemo, useState } from "react";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import WorkspaceListPopover from "../popovers/WorkspaceListPopover";
import AddIcon from "@mui/icons-material/Add";
import CreateModal from "../modals/CreateModal";
import { useCreateDocumentMutation } from "../../hooks/api/workspaceDocument";
import ThemeButton from "../common/ThemeButton";
import PeopleIcon from "@mui/icons-material/People";
import MemberModal from "../modals/MemberModal";
import { selectWorkspace } from "../../store/workspaceSlice";
import { DRAWER_WIDTH, WorkspaceDrawerHeader } from "../layouts/WorkspaceLayout";
import { useLocation, useNavigate, useParams } from "react-router-dom";

const DRAWER_WIDTH = 240;
interface WorkspaceDrawerProps {
open: boolean;
}

function WorkspaceDrawer() {
function WorkspaceDrawer(props: WorkspaceDrawerProps) {
const { open } = props;
const location = useLocation();
const params = useParams();
const navigate = useNavigate();
const userStore = useSelector(selectUser);
const workspaceStore = useSelector(selectWorkspace);
const { mutateAsync: createDocument } = useCreateDocumentMutation(
workspaceStore.data?.id || ""
);
const [profileAnchorEl, setProfileAnchorEl] = useState<(EventTarget & Element) | null>(null);
const [workspaceListAnchorEl, setWorkspaceListAnchorEl] = useState<
(EventTarget & Element) | null
>(null);
const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false);
const [memberModalOpen, setMemberModalOpen] = useState(false);

const handleOpenProfilePopover: MouseEventHandler = (event) => {
setProfileAnchorEl(event.currentTarget);
};

const handleCloseProfilePopover = () => {
setProfileAnchorEl(null);
};
const currentPage = useMemo(() => {
return location.pathname.split("/")[2] ?? "main";
}, [location.pathname]);

const handleOpenWorkspacePopover: MouseEventHandler = (event) => {
setWorkspaceListAnchorEl(event.currentTarget);
Expand All @@ -61,18 +43,8 @@ function WorkspaceDrawer() {
setWorkspaceListAnchorEl(null);
};

const handleCreateWorkspace = async (data: { title: string }) => {
const document = await createDocument(data);

navigate(document.id);
};

const handleCreateWorkspaceModalOpen = () => {
setCreateWorkspaceModalOpen((prev) => !prev);
};

const handleMemberModalOpen = () => {
setMemberModalOpen((prev) => !prev);
const handleNavigateToMember = () => {
navigate(`/${params.workspaceSlug}/member`);
};

return (
Expand All @@ -85,88 +57,50 @@ function WorkspaceDrawer() {
boxSizing: "border-box",
},
}}
variant="permanent"
variant="persistent"
anchor="left"
open
open={open}
>
<ListItem disablePadding>
<ListItemButton onClick={handleOpenWorkspacePopover}>
<ListItemText
primary={workspaceStore.data?.title}
primaryTypographyProps={{
variant: "subtitle1",
noWrap: true,
}}
<WorkspaceDrawerHeader>
<ListItem disablePadding>
<ListItemButton onClick={handleOpenWorkspacePopover}>
<ListItemText
primary={workspaceStore.data?.title}
primaryTypographyProps={{
variant: "subtitle1",
noWrap: true,
}}
/>
<ListItemSecondaryAction>
<IconButton>
{workspaceListAnchorEl ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</ListItemSecondaryAction>
</ListItemButton>
<WorkspaceListPopover
open={Boolean(workspaceListAnchorEl)}
anchorEl={workspaceListAnchorEl}
onClose={handleCloseWorkspacePopover}
width={DRAWER_WIDTH - 32}
/>
<ListItemSecondaryAction>
<IconButton>
{workspaceListAnchorEl ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</ListItemSecondaryAction>
</ListItemButton>
<WorkspaceListPopover
open={Boolean(workspaceListAnchorEl)}
anchorEl={workspaceListAnchorEl}
onClose={handleCloseWorkspacePopover}
width={DRAWER_WIDTH - 32}
/>
</ListItem>
<Divider />
<ListItem>
<Button
variant="contained"
startIcon={<AddIcon />}
sx={{
width: 1,
}}
onClick={handleCreateWorkspaceModalOpen}
>
New Note
</Button>
</ListItem>
</ListItem>
</WorkspaceDrawerHeader>
<Divider />
<ListItem disablePadding>
<ListItemButton onClick={handleMemberModalOpen}>
<ListItemButton
onClick={handleNavigateToMember}
selected={currentPage === "member"}
>
<ListItemIcon>
<PeopleIcon />
</ListItemIcon>
<ListItemText primary="Members" />
</ListItemButton>
</ListItem>
<Divider />
<ListItem sx={{ mt: "auto" }}>
<Stack width={1} alignItems="center" justifyContent="flex-end" direction="row">
<ThemeButton />
</Stack>
</ListItem>
<Divider />
<ListItem disablePadding>
<ListItemButton onClick={handleOpenProfilePopover}>
<ListItemAvatar>
<Avatar>{userStore.data?.nickname?.charAt(0)}</Avatar>
</ListItemAvatar>
<ListItemText primary={userStore.data?.nickname} />
<ListItemSecondaryAction>
<MoreVertIcon />
</ListItemSecondaryAction>
</ListItemButton>
<ProfilePopover
open={Boolean(profileAnchorEl)}
anchorEl={profileAnchorEl}
onClose={handleCloseProfilePopover}
/>
</ListItem>
<CreateModal
open={createWorkspaceModalOpen}
title="Note"
onSuccess={handleCreateWorkspace}
onClose={handleCreateWorkspaceModalOpen}
/>
<MemberModal open={memberModalOpen} onClose={handleMemberModalOpen} />
</Drawer>
);
}
Expand Down
35 changes: 0 additions & 35 deletions frontend/src/components/headers/MainHeader.tsx

This file was deleted.

106 changes: 106 additions & 0 deletions frontend/src/components/headers/WorkspaceHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { MouseEventHandler, useState } from "react";
import { Avatar, IconButton, Stack, Toolbar, styled, useTheme } from "@mui/material";
import AppBar, { AppBarProps } from "@mui/material/AppBar";
import { DRAWER_WIDTH } from "../layouts/WorkspaceLayout";
import MenuIcon from "@mui/icons-material/Menu";
import { useSelector } from "react-redux";
import { selectUser } from "../../store/userSlice";
import ProfilePopover from "../popovers/ProfilePopover";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
import CodePairIcon from "../icons/CodePairIcon";
import { useNavigate } from "react-router-dom";
import { selectWorkspace } from "../../store/workspaceSlice";

interface WorkspaceAppBarProps extends AppBarProps {
open?: boolean;
}

const WorkspaceAppBar = styled(AppBar, {
shouldForwardProp: (prop) => prop !== "open",
})<WorkspaceAppBarProps>(({ theme, open }) => ({
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${DRAWER_WIDTH}px)`,
marginLeft: `${DRAWER_WIDTH}px`,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));

interface WorkspaceHeaderProps {
open: boolean;
onDrawerOpen: () => void;
}

function WorkspaceHeader(props: WorkspaceHeaderProps) {
const { open, onDrawerOpen } = props;
const theme = useTheme();
const navigate = useNavigate();
const userStore = useSelector(selectUser);
const workspaceStore = useSelector(selectWorkspace);
const [profileAnchorEl, setProfileAnchorEl] = useState<(EventTarget & Element) | null>(null);

const handleOpenProfilePopover: MouseEventHandler = (event) => {
setProfileAnchorEl(event.currentTarget);
};

const handleCloseProfilePopover = () => {
setProfileAnchorEl(null);
};

const handleToWorkspace = () => {
navigate(`/${workspaceStore.data?.slug}`);
};

return (
<WorkspaceAppBar position="fixed" open={open}>
<Toolbar>
<Stack
width="100%"
direction="row-reverse"
justifyContent="space-between"
alignItems="center"
>
<IconButton onClick={handleOpenProfilePopover}>
<Avatar>{userStore.data?.nickname?.charAt(0)}</Avatar>
</IconButton>
<Stack direction="row">
<IconButton
color="inherit"
aria-label="open drawer"
onClick={onDrawerOpen}
edge="start"
sx={{ mr: 2 }}
>
{open ? (
theme.direction === "ltr" ? (
<KeyboardDoubleArrowLeftIcon />
) : (
<KeyboardDoubleArrowRightIcon />
)
) : (
<MenuIcon />
)}
</IconButton>
<IconButton onClick={handleToWorkspace}>
<CodePairIcon />
</IconButton>
</Stack>
</Stack>
</Toolbar>
<ProfilePopover
open={Boolean(profileAnchorEl)}
anchorEl={profileAnchorEl}
onClose={handleCloseProfilePopover}
/>
</WorkspaceAppBar>
);
}

export default WorkspaceHeader;
2 changes: 0 additions & 2 deletions frontend/src/components/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Stack } from "@mui/material";
import { Outlet } from "react-router-dom";
import MainHeader from "../headers/MainHeader";

function MainLayout() {
return (
<Stack sx={{ flexGrow: 1, height: "100vh" }}>
<MainHeader />
<Outlet />
</Stack>
);
Expand Down
Loading