Skip to content

Commit 490f4cd

Browse files
committed
Add settings page and simple styling for mobile (#565)
1 parent c82773d commit 490f4cd

File tree

6 files changed

+202
-65
lines changed

6 files changed

+202
-65
lines changed

frontend/src/components/drawers/WorkspaceDrawer.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
22
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
33
import PeopleIcon from "@mui/icons-material/People";
4+
import SettingsIcon from "@mui/icons-material/Settings";
45
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
56
import {
67
Box,
@@ -46,6 +47,12 @@ function WorkspaceDrawer(props: WorkspaceDrawerProps) {
4647
selected: currentPage === "member",
4748
moveTo: `/${params.workspaceSlug}/member`,
4849
},
50+
{
51+
title: "Settings",
52+
IconComponent: SettingsIcon,
53+
selected: currentPage === "settings",
54+
moveTo: `/${params.workspaceSlug}/settings`,
55+
},
4956
];
5057
}, [currentPage, params.workspaceSlug]);
5158

frontend/src/components/workspace/TableTab.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ export default function TableContent({ documents }: { documents: Document[] }) {
3939
<Table stickyHeader>
4040
<TableHead>
4141
<TableRow>
42-
<TableCell sx={{ width: "18%" }}>Title</TableCell>
43-
<TableCell sx={{ width: "18%" }}>Tag</TableCell>
44-
<TableCell sx={{ width: "18%" }}>User</TableCell>
45-
<TableCell sx={{ width: "18%" }}>Date</TableCell>
46-
<TableCell sx={{ width: "18%" }}>Last Edit</TableCell>
47-
<TableCell sx={{ width: "10%" }}></TableCell>
42+
<TableCell sx={{ width: "55%" }}>Title</TableCell>
43+
<TableCell sx={{ width: "15%" }}>Online Users</TableCell>
44+
<TableCell sx={{ width: "10%" }}>Tags</TableCell>
45+
<TableCell sx={{ width: "15%" }}>Created At</TableCell>
46+
<TableCell sx={{ width: "5%" }}></TableCell>
4847
</TableRow>
4948
</TableHead>
5049
<TableBody
@@ -72,7 +71,6 @@ export default function TableContent({ documents }: { documents: Document[] }) {
7271
</Typography>
7372
</Link>
7473
</TableCell>
75-
<TableCell>-</TableCell>
7674
<TableCell>
7775
<AvatarGroup
7876
max={isSmallScreen ? 2 : 4}
@@ -94,12 +92,8 @@ export default function TableContent({ documents }: { documents: Document[] }) {
9492
))}
9593
</AvatarGroup>
9694
</TableCell>
97-
<TableCell sx={{ color: "primary.main" }}>
98-
{moment(doc.createdAt).format("D MMM YYYY")}
99-
</TableCell>
100-
<TableCell sx={{ color: "text.secondary" }}>
101-
{moment(doc.updatedAt).fromNow()}
102-
</TableCell>
95+
<TableCell></TableCell>
96+
<TableCell>{moment(doc.createdAt).format("D MMM YYYY")}</TableCell>
10397
<TableCell sx={{ py: 0 }} align="right">
10498
{/* TODO(yeonthusiast): When the document deletion is implemented, uncomment the following code */}
10599
{/* <IconButton color="inherit" onClick={() => {}}>

frontend/src/pages/workspace/Index.tsx

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
useCreateDocumentMutation,
44
useGetWorkspaceDocumentListQuery,
55
} from "../../hooks/api/workspaceDocument";
6-
import { useDeleteWorkSpaceMutation, useGetWorkspaceQuery } from "../../hooks/api/workspace";
6+
import { useGetWorkspaceQuery } from "../../hooks/api/workspace";
77
import {
88
Backdrop,
99
Box,
@@ -25,8 +25,6 @@ import CreateModal from "../../components/modals/CreateModal";
2525
import AddIcon from "@mui/icons-material/Add";
2626
import BoardTab from "../../components/workspace/BoardTab";
2727
import TableTab from "../../components/workspace/TableTab";
28-
import DeleteIcon from "@mui/icons-material/Delete";
29-
import DeleteModal from "../../components/modals/DeleteModal";
3028

3129
const TABS = ["BOARD", "TABLE"] as const;
3230
type TabType = (typeof TABS)[number];
@@ -52,9 +50,6 @@ function WorkspaceIndex() {
5250
const { mutateAsync: createDocument } = useCreateDocumentMutation(workspace?.id || "");
5351
const [createDocumentModalOpen, setCreateDocumentModalOpen] = useState(false);
5452

55-
const { mutateAsync: deleteWorkspace } = useDeleteWorkSpaceMutation(workspace?.id);
56-
const [deleteWorkspaceModalOpen, setDeleteWorkspaceModalOpen] = useState(false);
57-
5853
const documentList = useMemo(() => {
5954
return (
6055
documentPageList?.pages.reduce((prev, page) => {
@@ -85,16 +80,6 @@ function WorkspaceIndex() {
8580
setSearch(e.target.value);
8681
};
8782

88-
const handleDeleteWorkspaceModalOpen = () => {
89-
setDeleteWorkspaceModalOpen((prev) => !prev);
90-
};
91-
92-
const handleDeleteWorkspace = async () => {
93-
await deleteWorkspace();
94-
setDeleteWorkspaceModalOpen(false);
95-
navigate("/", { replace: true });
96-
};
97-
9883
return (
9984
<Stack position="relative" pb={6}>
10085
<Paper
@@ -104,24 +89,35 @@ function WorkspaceIndex() {
10489
top: 64,
10590
left: 0,
10691
width: "100%",
107-
pb: 4,
92+
pb: { xs: 2, sm: 4 },
10893
zIndex: 3,
10994
}}
11095
>
111-
<Stack direction="row" justifyContent="space-between" alignItems="center" pt={6}>
112-
<Typography variant="h5" fontWeight="bold">
113-
{workspace?.title}{" "}
96+
<Stack
97+
direction={{ xs: "column", sm: "row" }}
98+
justifyContent="space-between"
99+
alignItems={{ xs: "flex-start", sm: "center" }}
100+
gap={2}
101+
pt={{ xs: 2, sm: 6 }}
102+
>
103+
<Typography
104+
variant="h6"
105+
fontWeight="bold"
106+
sx={{ fontSize: { xs: "1rem", sm: "1.25rem" } }}
107+
>
108+
Documents{" "}
114109
<Typography component="span" variant="inherit" color="primary">
115110
{documentPageList?.pages[0].totalLength}
116111
</Typography>
117112
</Typography>
118-
<Stack direction="row" alignItems="center" gap={2}>
113+
<Stack direction="row" alignItems="center" gap={2} flexWrap="wrap">
119114
<TextField
120-
placeholder="Search notes..."
115+
placeholder="Search documents..."
121116
variant="outlined"
122117
value={search}
123118
onChange={handleSearchChange}
124119
size="small"
120+
sx={{ minWidth: { xs: "100%", sm: 200 } }}
125121
slotProps={{
126122
input: {
127123
startAdornment: (
@@ -136,31 +132,25 @@ function WorkspaceIndex() {
136132
variant="contained"
137133
startIcon={<AddIcon />}
138134
onClick={handleCreateDocumentModalOpen}
135+
sx={{ minWidth: { xs: "100%", sm: "auto" } }}
139136
>
140-
New Note
137+
New Document
141138
</Button>
142-
{/* NOTE(kokodak): Delete workspace button should be visible after
143-
the following requirements are met:
144-
- Members should NOT see the delete button.
145-
- Even Owners should go through an extra confirmation step before delete.
146-
Once these requirements are met, we can safely remove the MODE condition below.
147-
For more details, see PR #556: https://github.com/yorkie-team/codepair/pull/556
148-
*/}
149-
{import.meta.env.MODE !== "production" && (
150-
<Button
151-
variant="contained"
152-
color="error"
153-
startIcon={<DeleteIcon />}
154-
onClick={handleDeleteWorkspaceModalOpen}
155-
>
156-
Delete
157-
</Button>
158-
)}
159139
</Stack>
160140
</Stack>
161141
</Paper>
162-
<Box sx={{ borderBottom: 1, borderColor: "divider" }} mb={4}>
163-
<Tabs value={currentTab} onChange={handleTabChange}>
142+
<Box sx={{ borderBottom: 1, borderColor: "divider" }} mb={{ xs: 2, sm: 4 }}>
143+
<Tabs
144+
value={currentTab}
145+
onChange={handleTabChange}
146+
sx={{
147+
minHeight: { xs: 40, sm: 48 },
148+
"& .MuiTab-root": {
149+
minHeight: { xs: 40, sm: 48 },
150+
py: { xs: 1, sm: 1.5 },
151+
},
152+
}}
153+
>
164154
{TABS.map((tab) => (
165155
<Tab key={tab} label={tab} value={tab} />
166156
))}
@@ -187,12 +177,6 @@ function WorkspaceIndex() {
187177
onSuccess={handleCreateWorkspace}
188178
onClose={handleCreateDocumentModalOpen}
189179
/>
190-
<DeleteModal
191-
open={deleteWorkspaceModalOpen}
192-
title={workspace?.title}
193-
onSuccess={handleDeleteWorkspace}
194-
onClose={handleDeleteWorkspaceModalOpen}
195-
/>
196180
</Stack>
197181
);
198182
}

frontend/src/pages/workspace/member/Index.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,20 @@ function MemberIndex() {
4747

4848
return (
4949
<Container maxWidth="lg">
50-
<Stack gap={4} py={6}>
51-
<Stack direction="row" justifyContent="space-between" alignItems="center" px={2}>
52-
<Typography variant="h5" fontWeight="bold">
53-
{workspace?.title}{" "}
50+
<Stack gap={{ xs: 2, sm: 4 }} py={{ xs: 2, sm: 6 }}>
51+
<Stack
52+
direction={{ xs: "column", sm: "row" }}
53+
justifyContent="space-between"
54+
alignItems={{ xs: "flex-start", sm: "center" }}
55+
gap={2}
56+
px={2}
57+
>
58+
<Typography
59+
variant="h6"
60+
fontWeight="bold"
61+
sx={{ fontSize: { xs: "1rem", sm: "1.25rem" } }}
62+
>
63+
Members{" "}
5464
<Typography component="span" variant="inherit" color="primary">
5565
{workspaceUserPageList?.pages[0].totalLength}
5666
</Typography>
@@ -59,6 +69,7 @@ function MemberIndex() {
5969
variant="contained"
6070
startIcon={<AddIcon />}
6171
onClick={handleMemberModalOpen}
72+
sx={{ minWidth: { xs: "100%", sm: "auto" } }}
6273
>
6374
Add Members
6475
</Button>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import DeleteIcon from "@mui/icons-material/Delete";
2+
import {
3+
Box,
4+
Button,
5+
Container,
6+
Divider,
7+
Paper,
8+
Stack,
9+
TextField,
10+
Typography,
11+
} from "@mui/material";
12+
import { useState } from "react";
13+
import { useNavigate, useParams } from "react-router-dom";
14+
import DeleteModal from "../../../components/modals/DeleteModal";
15+
import { useDeleteWorkSpaceMutation, useGetWorkspaceQuery } from "../../../hooks/api/workspace";
16+
17+
function SettingsIndex() {
18+
const params = useParams();
19+
const navigate = useNavigate();
20+
const { data: workspace } = useGetWorkspaceQuery(params.workspaceSlug);
21+
const { mutateAsync: deleteWorkspace } = useDeleteWorkSpaceMutation(workspace?.id);
22+
const [deleteWorkspaceModalOpen, setDeleteWorkspaceModalOpen] = useState(false);
23+
24+
const handleDeleteWorkspaceModalOpen = () => {
25+
setDeleteWorkspaceModalOpen((prev) => !prev);
26+
};
27+
28+
const handleDeleteWorkspace = async () => {
29+
try {
30+
await deleteWorkspace();
31+
setDeleteWorkspaceModalOpen(false);
32+
navigate("/", { replace: true });
33+
} catch (error) {
34+
// Keep modal open to let user retry
35+
console.error("Failed to delete workspace:", error);
36+
}
37+
};
38+
39+
return (
40+
<Container maxWidth="lg">
41+
<Stack gap={{ xs: 3, sm: 6 }} py={{ xs: 2, sm: 6 }}>
42+
<Typography
43+
variant="h6"
44+
fontWeight="bold"
45+
sx={{ fontSize: { xs: "1rem", sm: "1.25rem" } }}
46+
>
47+
Settings
48+
</Typography>
49+
50+
{/* Workspace Information Section */}
51+
<Paper sx={{ p: 3 }}>
52+
<Stack gap={3}>
53+
<Typography variant="h6" fontWeight="medium">
54+
General
55+
</Typography>
56+
<Stack gap={2}>
57+
<Box>
58+
<Typography variant="subtitle2" color="text.secondary" mb={1}>
59+
Workspace Name
60+
</Typography>
61+
<TextField
62+
fullWidth
63+
value={workspace?.title || ""}
64+
disabled
65+
size="small"
66+
/>
67+
</Box>
68+
<Box>
69+
<Typography variant="subtitle2" color="text.secondary" mb={1}>
70+
Workspace Slug
71+
</Typography>
72+
<TextField
73+
fullWidth
74+
value={workspace?.slug || ""}
75+
disabled
76+
size="small"
77+
/>
78+
</Box>
79+
</Stack>
80+
</Stack>
81+
</Paper>
82+
83+
{/* Danger Zone Section */}
84+
<Paper sx={{ p: 3, borderColor: "error.main" }}>
85+
<Stack gap={3}>
86+
<Typography variant="h6" fontWeight="medium" color="error">
87+
Danger Zone
88+
</Typography>
89+
<Divider />
90+
<Stack
91+
direction={{ xs: "column", sm: "row" }}
92+
justifyContent="space-between"
93+
alignItems={{ xs: "flex-start", sm: "center" }}
94+
gap={2}
95+
>
96+
<Box>
97+
<Typography variant="subtitle1" fontWeight="medium">
98+
Delete this workspace
99+
</Typography>
100+
<Typography variant="body2" color="text.secondary">
101+
Once you delete a workspace, there is no going back. Please be
102+
certain.
103+
</Typography>
104+
</Box>
105+
{/* NOTE(kokodak): Delete workspace button should be visible after
106+
the following requirements are met:
107+
- Members should NOT see the delete button.
108+
- Even Owners should go through an extra confirmation step before delete.
109+
Once these requirements are met, we can safely remove the MODE condition below.
110+
For more details, see PR #556: https://github.com/yorkie-team/codepair/pull/556
111+
*/}
112+
{import.meta.env.MODE !== "production" && (
113+
<Button
114+
variant="contained"
115+
color="error"
116+
startIcon={<DeleteIcon />}
117+
onClick={handleDeleteWorkspaceModalOpen}
118+
>
119+
Delete Workspace
120+
</Button>
121+
)}
122+
</Stack>
123+
</Stack>
124+
</Paper>
125+
</Stack>
126+
<DeleteModal
127+
open={deleteWorkspaceModalOpen}
128+
title={workspace?.title}
129+
onSuccess={handleDeleteWorkspace}
130+
onClose={handleDeleteWorkspaceModalOpen}
131+
/>
132+
</Container>
133+
);
134+
}
135+
136+
export default SettingsIndex;

frontend/src/routes.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import DocumentIndex from "./pages/workspace/document/Index";
1515
import DocumentShareIndex from "./pages/workspace/document/share/Index";
1616
import JoinIndex from "./pages/workspace/join/Index";
1717
import MemberIndex from "./pages/workspace/member/Index";
18+
import SettingsIndex from "./pages/workspace/settings/Index";
1819

1920
interface CodePairRoute {
2021
path: string;
@@ -64,6 +65,10 @@ const codePairRoutes: Array<CodePairRoute> = [
6465
path: "member",
6566
element: <MemberIndex />,
6667
},
68+
{
69+
path: "settings",
70+
element: <SettingsIndex />,
71+
},
6772
],
6873
},
6974
{

0 commit comments

Comments
 (0)