From 5d4ffaa61252e0d62ae07512bb33e28250e3f417 Mon Sep 17 00:00:00 2001 From: devleejb Date: Thu, 20 Jun 2024 21:59:05 +0900 Subject: [PATCH 01/11] Remove login page --- .../src/components/headers/MainHeader.tsx | 35 ----------- .../src/components/layouts/MainLayout.tsx | 2 - frontend/src/pages/Index.tsx | 57 +++++++++++++++++- frontend/src/pages/login/Index.tsx | 60 ------------------- frontend/src/routes.tsx | 5 -- 5 files changed, 55 insertions(+), 104 deletions(-) delete mode 100644 frontend/src/components/headers/MainHeader.tsx delete mode 100644 frontend/src/pages/login/Index.tsx diff --git a/frontend/src/components/headers/MainHeader.tsx b/frontend/src/components/headers/MainHeader.tsx deleted file mode 100644 index 3e864568..00000000 --- a/frontend/src/components/headers/MainHeader.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { AppBar, Button, Stack, Toolbar } from "@mui/material"; - -import ThemeButton from "../common/ThemeButton"; -import CodePairIcon from "../icons/CodePairIcon"; -import { useNavigate } from "react-router-dom"; -function MainHeader() { - const navigate = useNavigate(); - - const handleMoveToLogin = () => { - navigate("/login"); - }; - - return ( - - - - - - - - - - - - ); -} - -export default MainHeader; diff --git a/frontend/src/components/layouts/MainLayout.tsx b/frontend/src/components/layouts/MainLayout.tsx index d147f6b6..bb19ac17 100644 --- a/frontend/src/components/layouts/MainLayout.tsx +++ b/frontend/src/components/layouts/MainLayout.tsx @@ -1,11 +1,9 @@ import { Stack } from "@mui/material"; import { Outlet } from "react-router-dom"; -import MainHeader from "../headers/MainHeader"; function MainLayout() { return ( - ); diff --git a/frontend/src/pages/Index.tsx b/frontend/src/pages/Index.tsx index a46cf09b..f03d6270 100644 --- a/frontend/src/pages/Index.tsx +++ b/frontend/src/pages/Index.tsx @@ -1,7 +1,60 @@ -import { Box } from "@mui/material"; +import { Box, Container, Divider, Grid, Paper, Stack, Typography } from "@mui/material"; +import CodePairIcon from "../components/icons/CodePairIcon"; +import { GithubLoginButton } from "react-social-login-buttons"; + +const socialLoginList = [ + { + SocailLoginComponent: GithubLoginButton, + provider: "github", + }, +]; function Index() { - return ; + const handleLogin = (provider: string) => { + window.location.href = `${import.meta.env.VITE_API_ADDR}/auth/login/${provider}`; + }; + + return ( + + + + + + + + Login + + + Real-time markdown editor for interviews, meetings and more... + + + + + + + + + + Login with + + + + + + + {socialLoginList.map(({ SocailLoginComponent, provider }) => ( + handleLogin(provider)} + /> + ))} + + + + + + ); } export default Index; diff --git a/frontend/src/pages/login/Index.tsx b/frontend/src/pages/login/Index.tsx deleted file mode 100644 index b7d4eed9..00000000 --- a/frontend/src/pages/login/Index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Box, Container, Divider, Grid, Paper, Stack, Typography } from "@mui/material"; -import CodePairIcon from "../../components/icons/CodePairIcon"; -import { GithubLoginButton } from "react-social-login-buttons"; - -const socialLoginList = [ - { - SocailLoginComponent: GithubLoginButton, - provider: "github", - }, -]; - -function LoginIndex() { - const handleLogin = (provider: string) => { - window.location.href = `${import.meta.env.VITE_API_ADDR}/auth/login/${provider}`; - }; - - return ( - - - - - - - - Login - - - Real-time markdown editor for interviews, meetings and more... - - - - - - - - - - Login with - - - - - - - {socialLoginList.map(({ SocailLoginComponent, provider }) => ( - handleLogin(provider)} - /> - ))} - - - - - - ); -} - -export default LoginIndex; diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index e256ebf7..d7b15272 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -1,6 +1,5 @@ import DocumentIndex from "./pages/workspace/document/Index"; import MainLayout from "./components/layouts/MainLayout"; -import LoginIndex from "./pages/login/Index"; import CallbackIndex from "./pages/auth/callback/Index"; import WorkspaceLayout from "./components/layouts/WorkspaceLayout"; import GuestRoute from "./components/common/GuestRoute"; @@ -40,10 +39,6 @@ const codePairRoutes: Array = [ path: "", element: , }, - { - path: "login", - element: , - }, ], }, { From b22fd358b2e7e6b3b0b282f3efe9cedb7831d661 Mon Sep 17 00:00:00 2001 From: devleejb Date: Thu, 20 Jun 2024 22:09:15 +0900 Subject: [PATCH 02/11] Change WorkspaceLayout --- .../components/layouts/WorkspaceLayout.tsx | 123 +++++++++++++++++- frontend/src/pages/workspace/Index.tsx | 4 +- 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/layouts/WorkspaceLayout.tsx b/frontend/src/components/layouts/WorkspaceLayout.tsx index c77152a5..705c1ba8 100644 --- a/frontend/src/components/layouts/WorkspaceLayout.tsx +++ b/frontend/src/components/layouts/WorkspaceLayout.tsx @@ -1,7 +1,128 @@ +import * as React from "react"; +import { styled, useTheme } from "@mui/material/styles"; +import Box from "@mui/material/Box"; +import Drawer from "@mui/material/Drawer"; +import CssBaseline from "@mui/material/CssBaseline"; +import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; +import Toolbar from "@mui/material/Toolbar"; +import List from "@mui/material/List"; +import Typography from "@mui/material/Typography"; +import Divider from "@mui/material/Divider"; +import IconButton from "@mui/material/IconButton"; +import MenuIcon from "@mui/icons-material/Menu"; +import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import ListItem from "@mui/material/ListItem"; +import ListItemButton from "@mui/material/ListItemButton"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import ListItemText from "@mui/material/ListItemText"; +import InboxIcon from "@mui/icons-material/MoveToInbox"; +import MailIcon from "@mui/icons-material/Mail"; import { Outlet } from "react-router-dom"; +const DRAWER_WIDTH = 282; + +const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{ + open?: boolean; +}>(({ theme, open }) => ({ + flexGrow: 1, + padding: theme.spacing(3), + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + marginLeft: `-${DRAWER_WIDTH}px`, + ...(open && { + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + marginLeft: 0, + }), +})); + +interface WorkspaceAppBarProps extends MuiAppBarProps { + open?: boolean; +} + +const WorkspaceAppBar = styled(MuiAppBar, { + shouldForwardProp: (prop) => prop !== "open", +})(({ 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, + }), + }), +})); + +const WorkspaceDrawerHeader = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + justifyContent: "flex-end", +})); + function WorkspaceLayout() { - return ; + const theme = useTheme(); + const [open, setOpen] = React.useState(false); + + const handleDrawerOpen = () => { + setOpen((prev) => !prev); + }; + + return ( + + + + + + + + + Persistent drawer + + + + + + + {theme.direction === "ltr" ? : } + + + +
+ + +
+
+ ); } export default WorkspaceLayout; diff --git a/frontend/src/pages/workspace/Index.tsx b/frontend/src/pages/workspace/Index.tsx index 5ad24737..3e68061b 100644 --- a/frontend/src/pages/workspace/Index.tsx +++ b/frontend/src/pages/workspace/Index.tsx @@ -26,7 +26,7 @@ function WorkspaceIndex() { return ( - + {/* - + */} ); } From df7b0f524cf7d5b98cd92cf685ae1f02bbeba914 Mon Sep 17 00:00:00 2001 From: devleejb Date: Thu, 20 Jun 2024 22:09:51 +0900 Subject: [PATCH 03/11] Remove unused import statement --- frontend/src/components/layouts/WorkspaceLayout.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/frontend/src/components/layouts/WorkspaceLayout.tsx b/frontend/src/components/layouts/WorkspaceLayout.tsx index 705c1ba8..ad7f00c2 100644 --- a/frontend/src/components/layouts/WorkspaceLayout.tsx +++ b/frontend/src/components/layouts/WorkspaceLayout.tsx @@ -2,22 +2,13 @@ import * as React from "react"; import { styled, useTheme } from "@mui/material/styles"; import Box from "@mui/material/Box"; import Drawer from "@mui/material/Drawer"; -import CssBaseline from "@mui/material/CssBaseline"; import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; -import List from "@mui/material/List"; import Typography from "@mui/material/Typography"; -import Divider from "@mui/material/Divider"; import IconButton from "@mui/material/IconButton"; import MenuIcon from "@mui/icons-material/Menu"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import InboxIcon from "@mui/icons-material/MoveToInbox"; -import MailIcon from "@mui/icons-material/Mail"; import { Outlet } from "react-router-dom"; const DRAWER_WIDTH = 282; @@ -81,7 +72,6 @@ function WorkspaceLayout() { return ( - Date: Thu, 20 Jun 2024 22:22:07 +0900 Subject: [PATCH 04/11] Implement WorkspaceHeader --- .../components/headers/WorkspaceHeader.tsx | 81 +++++++++++++++++++ .../components/layouts/WorkspaceLayout.tsx | 45 +---------- .../components/popovers/ProfilePopover.tsx | 4 +- 3 files changed, 86 insertions(+), 44 deletions(-) create mode 100644 frontend/src/components/headers/WorkspaceHeader.tsx diff --git a/frontend/src/components/headers/WorkspaceHeader.tsx b/frontend/src/components/headers/WorkspaceHeader.tsx new file mode 100644 index 00000000..a6a8a34a --- /dev/null +++ b/frontend/src/components/headers/WorkspaceHeader.tsx @@ -0,0 +1,81 @@ +import { Avatar, IconButton, Stack, Toolbar, styled } 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 { MouseEventHandler, useState } from "react"; + +interface WorkspaceAppBarProps extends AppBarProps { + open?: boolean; +} + +const WorkspaceAppBar = styled(AppBar, { + shouldForwardProp: (prop) => prop !== "open", +})(({ 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 userStore = useSelector(selectUser); + const [profileAnchorEl, setProfileAnchorEl] = useState<(EventTarget & Element) | null>(null); + + const handleOpenProfilePopover: MouseEventHandler = (event) => { + setProfileAnchorEl(event.currentTarget); + }; + + const handleCloseProfilePopover = () => { + setProfileAnchorEl(null); + }; + + return ( + + + + + {userStore.data?.nickname?.charAt(0)} + + + + + + + + + ); +} + +export default WorkspaceHeader; diff --git a/frontend/src/components/layouts/WorkspaceLayout.tsx b/frontend/src/components/layouts/WorkspaceLayout.tsx index ad7f00c2..59579956 100644 --- a/frontend/src/components/layouts/WorkspaceLayout.tsx +++ b/frontend/src/components/layouts/WorkspaceLayout.tsx @@ -2,16 +2,13 @@ import * as React from "react"; import { styled, useTheme } from "@mui/material/styles"; import Box from "@mui/material/Box"; import Drawer from "@mui/material/Drawer"; -import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; -import Toolbar from "@mui/material/Toolbar"; -import Typography from "@mui/material/Typography"; import IconButton from "@mui/material/IconButton"; -import MenuIcon from "@mui/icons-material/Menu"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { Outlet } from "react-router-dom"; +import WorkspaceHeader from "../headers/WorkspaceHeader"; -const DRAWER_WIDTH = 282; +export const DRAWER_WIDTH = 282; const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{ open?: boolean; @@ -32,27 +29,6 @@ const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{ }), })); -interface WorkspaceAppBarProps extends MuiAppBarProps { - open?: boolean; -} - -const WorkspaceAppBar = styled(MuiAppBar, { - shouldForwardProp: (prop) => prop !== "open", -})(({ 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, - }), - }), -})); - const WorkspaceDrawerHeader = styled("div")(({ theme }) => ({ display: "flex", alignItems: "center", @@ -72,22 +48,7 @@ function WorkspaceLayout() { return ( - - - - - - - Persistent drawer - - - + Date: Thu, 20 Jun 2024 22:34:38 +0900 Subject: [PATCH 05/11] Implement WorkspaceDrawer --- .../components/drawers/WorkspaceDrawer.tsx | 104 ++++++------------ .../components/headers/WorkspaceHeader.tsx | 19 +++- .../components/layouts/WorkspaceLayout.tsx | 30 +---- 3 files changed, 54 insertions(+), 99 deletions(-) diff --git a/frontend/src/components/drawers/WorkspaceDrawer.tsx b/frontend/src/components/drawers/WorkspaceDrawer.tsx index 799108f6..6bc06cbc 100644 --- a/frontend/src/components/drawers/WorkspaceDrawer.tsx +++ b/frontend/src/components/drawers/WorkspaceDrawer.tsx @@ -1,22 +1,16 @@ 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 KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; @@ -24,35 +18,28 @@ 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"; -const DRAWER_WIDTH = 240; +interface WorkspaceDrawerProps { + open: boolean; +} -function WorkspaceDrawer() { +function WorkspaceDrawer(props: WorkspaceDrawerProps) { + const { open } = props; 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 handleOpenWorkspacePopover: MouseEventHandler = (event) => { setWorkspaceListAnchorEl(event.currentTarget); }; @@ -85,36 +72,38 @@ function WorkspaceDrawer() { boxSizing: "border-box", }, }} - variant="permanent" + variant="persistent" anchor="left" - open + open={open} > - - - + + + + + + {workspaceListAnchorEl ? ( + + ) : ( + + )} + + + + - - - {workspaceListAnchorEl ? ( - - ) : ( - - )} - - - - - + + - - @@ -126,12 +93,6 @@ function WorkspaceDrawer(props: WorkspaceDrawerProps) { - ); diff --git a/frontend/src/pages/workspace/Index.tsx b/frontend/src/pages/workspace/Index.tsx index 3e68061b..5d152e25 100644 --- a/frontend/src/pages/workspace/Index.tsx +++ b/frontend/src/pages/workspace/Index.tsx @@ -1,21 +1,28 @@ -import { useParams } from "react-router-dom"; -import WorkspaceDrawer from "../../components/drawers/WorkspaceDrawer"; -import { useGetWorkspaceDocumentListQuery } from "../../hooks/api/workspaceDocument"; +import { useNavigate, useParams } from "react-router-dom"; +import { + useCreateDocumentMutation, + useGetWorkspaceDocumentListQuery, +} from "../../hooks/api/workspaceDocument"; import { useGetWorkspaceQuery } from "../../hooks/api/workspace"; -import { Box, CircularProgress, Grid, Stack } from "@mui/material"; +import { Box, Button, CircularProgress, Grid, Stack, Typography } from "@mui/material"; import DocumentCard from "../../components/cards/DocumentCard"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { Document } from "../../hooks/api/types/document.d"; import InfiniteScroll from "react-infinite-scroller"; +import CreateModal from "../../components/modals/CreateModal"; +import AddIcon from "@mui/icons-material/Add"; function WorkspaceIndex() { const params = useParams(); + const navigate = useNavigate(); const { data: workspace } = useGetWorkspaceQuery(params.workspaceSlug); const { data: documentPageList, fetchNextPage, hasNextPage, } = useGetWorkspaceDocumentListQuery(workspace?.id); + const { mutateAsync: createDocument } = useCreateDocumentMutation(workspace?.id || ""); + const [createDocumentModalOpen, setCreateDocumentModalOpen] = useState(false); const documentList = useMemo(() => { return ( documentPageList?.pages.reduce((prev, page) => { @@ -24,42 +31,71 @@ function WorkspaceIndex() { ); }, [documentPageList?.pages]); + const handleCreateDocumentModalOpen = () => { + setCreateDocumentModalOpen((prev) => !prev); + }; + + const handleCreateWorkspace = async (data: { title: string }) => { + const document = await createDocument(data); + + navigate(document.id); + }; + return ( - - {/* - - fetchNextPage()} - hasMore={hasNextPage} - loader={ - - - - } - useWindow={false} + + + + {workspace?.title}{" "} + + {documentPageList?.pages[0].documents.length} + + + + + fetchNextPage()} + hasMore={hasNextPage} + loader={ + + + + } + useWindow={false} + > + + + {documentList.map((document) => ( + + + + ))} + + + + ); } From 08b10160d56e79c331e524a898602a8622e1915b Mon Sep 17 00:00:00 2001 From: devleejb Date: Fri, 21 Jun 2024 00:50:46 +0900 Subject: [PATCH 07/11] Implement member page --- .../components/drawers/WorkspaceDrawer.tsx | 20 +-- .../src/components/modals/MemberModal.tsx | 92 +++++--------- frontend/src/pages/workspace/member/Index.tsx | 118 ++++++++++++++++++ frontend/src/routes.tsx | 5 + 4 files changed, 164 insertions(+), 71 deletions(-) create mode 100644 frontend/src/pages/workspace/member/Index.tsx diff --git a/frontend/src/components/drawers/WorkspaceDrawer.tsx b/frontend/src/components/drawers/WorkspaceDrawer.tsx index f9875635..924f44be 100644 --- a/frontend/src/components/drawers/WorkspaceDrawer.tsx +++ b/frontend/src/components/drawers/WorkspaceDrawer.tsx @@ -9,14 +9,14 @@ import { ListItemText, } from "@mui/material"; import { useSelector } from "react-redux"; -import { MouseEventHandler, useState } from "react"; +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 PeopleIcon from "@mui/icons-material/People"; -import MemberModal from "../modals/MemberModal"; import { selectWorkspace } from "../../store/workspaceSlice"; import { DRAWER_WIDTH, WorkspaceDrawerHeader } from "../layouts/WorkspaceLayout"; +import { useNavigate, useParams } from "react-router-dom"; interface WorkspaceDrawerProps { open: boolean; @@ -24,11 +24,15 @@ interface WorkspaceDrawerProps { function WorkspaceDrawer(props: WorkspaceDrawerProps) { const { open } = props; + const params = useParams(); + const navigate = useNavigate(); const workspaceStore = useSelector(selectWorkspace); const [workspaceListAnchorEl, setWorkspaceListAnchorEl] = useState< (EventTarget & Element) | null >(null); - const [memberModalOpen, setMemberModalOpen] = useState(false); + const currentPage = useMemo(() => { + return window.location.href.split("/")[4] ?? "main"; + }, []); const handleOpenWorkspacePopover: MouseEventHandler = (event) => { setWorkspaceListAnchorEl(event.currentTarget); @@ -38,8 +42,8 @@ function WorkspaceDrawer(props: WorkspaceDrawerProps) { setWorkspaceListAnchorEl(null); }; - const handleMemberModalOpen = () => { - setMemberModalOpen((prev) => !prev); + const handleNavigateToMember = () => { + navigate(`/${params.workspaceSlug}/member`); }; return ( @@ -86,14 +90,16 @@ function WorkspaceDrawer(props: WorkspaceDrawerProps) { - + - ); } diff --git a/frontend/src/components/modals/MemberModal.tsx b/frontend/src/components/modals/MemberModal.tsx index 7c9fd867..c793096c 100644 --- a/frontend/src/components/modals/MemberModal.tsx +++ b/frontend/src/components/modals/MemberModal.tsx @@ -1,8 +1,6 @@ import { - Avatar, Box, Button, - CircularProgress, FormControl, IconButton, Modal, @@ -12,11 +10,8 @@ import { Typography, } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; -import { useGetWorkspaceUserListQuery } from "../../hooks/api/workspaceUser"; import { useCreateWorkspaceInvitationTokenMutation } from "../../hooks/api/workspace"; -import { useMemo, useState } from "react"; -import { User } from "../../hooks/api/types/user"; -import InfiniteScroll from "react-infinite-scroller"; +import { useState } from "react"; import { FormContainer, SelectElement } from "react-hook-form-mui"; import { invitationExpiredStringList } from "../../utils/expire"; import moment, { unitOfTime } from "moment"; @@ -34,20 +29,8 @@ interface MemeberModalProps { function MemeberModal(props: MemeberModalProps) { const { open, onClose } = props; const workspaceStore = useSelector(selectWorkspace); - const { - data: workspaceUserPageList, - fetchNextPage, - hasNextPage, - } = useGetWorkspaceUserListQuery(workspaceStore.data?.id); const { mutateAsync: createWorkspaceInvitationToken } = useCreateWorkspaceInvitationTokenMutation(workspaceStore.data?.id || ""); - const userList = useMemo(() => { - return ( - workspaceUserPageList?.pages.reduce((prev, page) => { - return prev.concat(page.workspaceUsers); - }, [] as Array) ?? [] - ); - }, [workspaceUserPageList?.pages]); const { enqueueSnackbar } = useSnackbar(); const [invitationUrl, setInvitationUrl] = useState(null); @@ -86,7 +69,7 @@ function MemeberModal(props: MemeberModalProps) { left: "50%", transform: "translate(-50%, -50%)", p: 4, - width: 400, + width: 538, }} > - Members + + + Add Members + + Generate and share the link. + - Invite Link - + + + + + fetchNextPage()} + hasMore={hasNextPage} + loader={ + + + + } + useWindow={false} + style={{ + width: "100%", + }} + > + + + + Name + Role + + + + + {userList.map((row) => ( + + + {row.nickname} + + - + - + + ))} + +
+
+
+
+ + + ); +} + +export default MemberIndex; diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index d7b15272..c3106430 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -10,6 +10,7 @@ import JoinIndex from "./pages/workspace/join/Index"; import Index from "./pages/Index"; import DocumentLayout from "./components/layouts/DocumentLayout"; import DocumentShareIndex from "./pages/workspace/document/share/Index"; +import MemberIndex from "./pages/workspace/member/Index"; interface CodePairRoute { path: string; @@ -50,6 +51,10 @@ const codePairRoutes: Array = [ path: "", element: , }, + { + path: "member", + element: , + }, ], }, { From 597dc912d7cea895a0ea5df73a67669da528ca91 Mon Sep 17 00:00:00 2001 From: devleejb Date: Fri, 21 Jun 2024 01:10:13 +0900 Subject: [PATCH 08/11] Change drawer option to permanent store --- .../components/drawers/WorkspaceDrawer.tsx | 7 +- .../components/layouts/WorkspaceLayout.tsx | 14 ++-- frontend/src/pages/workspace/Index.tsx | 64 +++++++++---------- frontend/src/store/configSlice.ts | 7 +- 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/frontend/src/components/drawers/WorkspaceDrawer.tsx b/frontend/src/components/drawers/WorkspaceDrawer.tsx index 924f44be..3b98cb25 100644 --- a/frontend/src/components/drawers/WorkspaceDrawer.tsx +++ b/frontend/src/components/drawers/WorkspaceDrawer.tsx @@ -16,7 +16,7 @@ import WorkspaceListPopover from "../popovers/WorkspaceListPopover"; import PeopleIcon from "@mui/icons-material/People"; import { selectWorkspace } from "../../store/workspaceSlice"; import { DRAWER_WIDTH, WorkspaceDrawerHeader } from "../layouts/WorkspaceLayout"; -import { useNavigate, useParams } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; interface WorkspaceDrawerProps { open: boolean; @@ -24,6 +24,7 @@ interface WorkspaceDrawerProps { function WorkspaceDrawer(props: WorkspaceDrawerProps) { const { open } = props; + const location = useLocation(); const params = useParams(); const navigate = useNavigate(); const workspaceStore = useSelector(selectWorkspace); @@ -31,8 +32,8 @@ function WorkspaceDrawer(props: WorkspaceDrawerProps) { (EventTarget & Element) | null >(null); const currentPage = useMemo(() => { - return window.location.href.split("/")[4] ?? "main"; - }, []); + return location.pathname.split("/")[2] ?? "main"; + }, [location.pathname]); const handleOpenWorkspacePopover: MouseEventHandler = (event) => { setWorkspaceListAnchorEl(event.currentTarget); diff --git a/frontend/src/components/layouts/WorkspaceLayout.tsx b/frontend/src/components/layouts/WorkspaceLayout.tsx index 1a0512c0..e1abac7d 100644 --- a/frontend/src/components/layouts/WorkspaceLayout.tsx +++ b/frontend/src/components/layouts/WorkspaceLayout.tsx @@ -1,9 +1,10 @@ -import * as React from "react"; import { styled } from "@mui/material/styles"; import Box from "@mui/material/Box"; import { Outlet } from "react-router-dom"; import WorkspaceHeader from "../headers/WorkspaceHeader"; import WorkspaceDrawer from "../drawers/WorkspaceDrawer"; +import { useDispatch, useSelector } from "react-redux"; +import { selectConfig, setDrawerOpen } from "../../store/configSlice"; export const DRAWER_WIDTH = 282; @@ -36,17 +37,18 @@ export const WorkspaceDrawerHeader = styled("div")(({ theme }) => ({ })); function WorkspaceLayout() { - const [open, setOpen] = React.useState(false); + const { drawerOpen } = useSelector(selectConfig); + const dispatch = useDispatch(); const handleDrawerOpen = () => { - setOpen((prev) => !prev); + dispatch(setDrawerOpen(!drawerOpen)); }; return ( - - -
+ + +
diff --git a/frontend/src/pages/workspace/Index.tsx b/frontend/src/pages/workspace/Index.tsx index 5d152e25..4a8ea6ee 100644 --- a/frontend/src/pages/workspace/Index.tsx +++ b/frontend/src/pages/workspace/Index.tsx @@ -42,14 +42,7 @@ function WorkspaceIndex() { }; return ( - + {workspace?.title}{" "} @@ -65,31 +58,38 @@ function WorkspaceIndex() { New Note - fetchNextPage()} - hasMore={hasNextPage} - loader={ - - - - } - useWindow={false} + - - - {documentList.map((document) => ( - - - - ))} - - - + fetchNextPage()} + hasMore={hasNextPage} + loader={ + + + + } + useWindow={false} + > + + + {documentList.map((document) => ( + + + + ))} + + + + ) => { state.theme = action.payload; }, + setDrawerOpen: (state, action: PayloadAction) => { + state.drawerOpen = action.payload; + }, }, }); -export const { setTheme } = configSlice.actions; +export const { setTheme, setDrawerOpen } = configSlice.actions; export const selectConfig = (state: RootState) => state.config; From 57e12dee925ab66abb05401267fd428ef02d5423 Mon Sep 17 00:00:00 2001 From: devleejb Date: Fri, 21 Jun 2024 14:13:46 +0900 Subject: [PATCH 09/11] Fix typo --- frontend/src/components/modals/MemberModal.tsx | 6 +++--- frontend/src/pages/workspace/member/Index.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/modals/MemberModal.tsx b/frontend/src/components/modals/MemberModal.tsx index c793096c..b2ba79c4 100644 --- a/frontend/src/components/modals/MemberModal.tsx +++ b/frontend/src/components/modals/MemberModal.tsx @@ -21,12 +21,12 @@ import { useSnackbar } from "notistack"; import { useSelector } from "react-redux"; import { selectWorkspace } from "../../store/workspaceSlice"; -interface MemeberModalProps { +interface MemberModalProps { open: boolean; onClose: () => void; } -function MemeberModal(props: MemeberModalProps) { +function MemberModal(props: MemberModalProps) { const { open, onClose } = props; const workspaceStore = useSelector(selectWorkspace); const { mutateAsync: createWorkspaceInvitationToken } = @@ -142,4 +142,4 @@ function MemeberModal(props: MemeberModalProps) { ); } -export default MemeberModal; +export default MemberModal; diff --git a/frontend/src/pages/workspace/member/Index.tsx b/frontend/src/pages/workspace/member/Index.tsx index 0f1f0020..a636f00b 100644 --- a/frontend/src/pages/workspace/member/Index.tsx +++ b/frontend/src/pages/workspace/member/Index.tsx @@ -20,7 +20,7 @@ import { useGetWorkspaceUserListQuery } from "../../../hooks/api/workspaceUser"; import { useMemo, useState } from "react"; import AddIcon from "@mui/icons-material/Add"; import { User } from "../../../hooks/api/types/user"; -import MemeberModal from "../../../components/modals/MemberModal"; +import MemberModal from "../../../components/modals/MemberModal"; function MemberIndex() { const params = useParams(); @@ -30,7 +30,7 @@ function MemberIndex() { fetchNextPage, hasNextPage, } = useGetWorkspaceUserListQuery(workspace?.id); - const [memeberModalOpen, setMemeberModalOpen] = useState(false); + const [memberModalOpen, setMemberModalOpen] = useState(false); const userList = useMemo(() => { return ( workspaceUserPageList?.pages.reduce((prev, page) => { @@ -40,7 +40,7 @@ function MemberIndex() { }, [workspaceUserPageList?.pages]); const handleMemberModalOpen = () => { - setMemeberModalOpen((prev) => !prev); + setMemberModalOpen((prev) => !prev); }; return ( @@ -110,7 +110,7 @@ function MemberIndex() { - + ); } From e12392e11166f9082ea0c73f3269019c2554ec46 Mon Sep 17 00:00:00 2001 From: devleejb Date: Fri, 21 Jun 2024 21:29:19 +0900 Subject: [PATCH 10/11] Add home button to `WorkspaceHeader` --- .../components/headers/WorkspaceHeader.tsx | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/headers/WorkspaceHeader.tsx b/frontend/src/components/headers/WorkspaceHeader.tsx index 1b4c5e2b..1f421a88 100644 --- a/frontend/src/components/headers/WorkspaceHeader.tsx +++ b/frontend/src/components/headers/WorkspaceHeader.tsx @@ -8,6 +8,9 @@ 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; @@ -38,7 +41,9 @@ interface WorkspaceHeaderProps { 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) => { @@ -49,6 +54,10 @@ function WorkspaceHeader(props: WorkspaceHeaderProps) { setProfileAnchorEl(null); }; + const handleToWorkspace = () => { + navigate(`/${workspaceStore.data?.slug}`); + }; + return ( @@ -61,23 +70,28 @@ function WorkspaceHeader(props: WorkspaceHeaderProps) { {userStore.data?.nickname?.charAt(0)} - - {open ? ( - theme.direction === "ltr" ? ( - + + + {open ? ( + theme.direction === "ltr" ? ( + + ) : ( + + ) ) : ( - - ) - ) : ( - - )} - + + )} + + + + + Date: Fri, 21 Jun 2024 21:29:40 +0900 Subject: [PATCH 11/11] Add theme change button in `ProfilePopover` --- .../src/components/popovers/ProfilePopover.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/src/components/popovers/ProfilePopover.tsx b/frontend/src/components/popovers/ProfilePopover.tsx index e2a93e07..13c756fa 100644 --- a/frontend/src/components/popovers/ProfilePopover.tsx +++ b/frontend/src/components/popovers/ProfilePopover.tsx @@ -10,15 +10,24 @@ import LogoutIcon from "@mui/icons-material/Logout"; import { useDispatch } from "react-redux"; import { setAccessToken } from "../../store/authSlice"; import { setUserData } from "../../store/userSlice"; +import DarkModeIcon from "@mui/icons-material/DarkMode"; +import LightModeIcon from "@mui/icons-material/LightMode"; +import { useCurrentTheme } from "../../hooks/useCurrentTheme"; +import { setTheme } from "../../store/configSlice"; function ProfilePopover(props: PopoverProps) { const dispatch = useDispatch(); + const themeMode = useCurrentTheme(); const handleLogout = () => { dispatch(setAccessToken(null)); dispatch(setUserData(null)); }; + const handleChangeTheme = () => { + dispatch(setTheme(themeMode == "light" ? "dark" : "light")); + }; + return ( + + + {themeMode === "light" ? : } + + Appearance +