Skip to content

Commit 8c21c30

Browse files
committed
Merge branch 'develop' into feature-shared-#211
2 parents c2b743f + 2d9cca6 commit 8c21c30

File tree

8 files changed

+155
-90
lines changed

8 files changed

+155
-90
lines changed

README.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
![Sprint 33](https://github.com/user-attachments/assets/2b23184d-90ed-458d-9dc4-dab9579c1e48)
22

3-
4-
> 배포 링크: http://octodocs.s3-website.kr.object.ncloudstorage.com/
5-
>
3+
🪝 [**배포 링크**](http://211.188.48.107:3000/)
64

75
<br>
86

@@ -21,21 +19,18 @@
2119

2220
> “Notion을 쓰고 있는데, 문서끼리 관계 표현이 너무 어려워요…”
2321
>
24-
2522
> “Obsidian으로 노트 정리를 잘 하고 있는데, 공유하기가 너무 불편해요…”
26-
>
2723
28-
이런 고민, 이제 OctoDocs로 해결해보세요!!
24+
이런 고민, 이제 **OctoDocs** 해결해보세요!!
2925

30-
- OctoDocs는
31-
- **실시간 협업**이 지원되는 **연결형 지식관리 도구**입니다.
32-
- **실시간 동시편집****마크다운** 형식 문서편집을 지원합니다.
26+
- **실시간 협업**이 지원되는 **연결형 지식관리 도구**입니다.
27+
- **실시간 동시편집****마크다운** 형식 문서편집을 지원합니다.
3328

34-
## 😎 핵심 기능
29+
## 📢 핵심 기능
3530

3631
![image](https://github.com/user-attachments/assets/4c0010db-d4a3-455f-ab26-03e04c85e46e)
3732

38-
## 🚀시스템 아키텍처
33+
## 🛠️ 시스템 아키텍처
3934

4035
v.241115
4136

@@ -45,6 +40,6 @@ v.241115
4540
## 🧸 팀원 소개
4641
| [J032_김동준](https://github.com/djk01281) | [J075_김현준](https://github.com/Tolerblanc) | [J097_민서진](https://github.com/summersummerwhy) | [J162_유성민](https://github.com/ezcolin2) | [J248_진예원](https://github.com/yewonJin) |
4742
|:----------------------------------------:|:------------------------------------------:|:------------------------------------------------:|:----------------------------------------:|:----------------------------------------:|
48-
| <img width="204" alt="스크린샷 2024-10-29 오후 11 40 13" src="https://github.com/user-attachments/assets/96e153b5-7254-4354-b17f-7436eb2a5734"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 04" src="https://github.com/user-attachments/assets/e093f852-a6ea-4937-b0ce-b89276bd7135"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 55" src="https://github.com/user-attachments/assets/0f638ba9-a1ad-47b8-a874-957c0119384c"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 00" src="https://github.com/user-attachments/assets/1d77b650-70f1-4dee-9489-dc0122b7c9ff"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 40 31" src="https://github.com/user-attachments/assets/db99b6b2-ae06-4758-8687-17ebb860a52b"> |
49-
| **INFJ** | **INFJ** | **INTP** | **INFP** | **ISTJ** |
50-
| **`FE`** | **`BE`** | **`BE`** | **`BE`** | **`FE`** |
43+
| <img width="204" alt="스크린샷 2024-10-29 오후 4" src="https://github.com/user-attachments/assets/71a5a38e-f60c-4f60-97e3-30d7a73a3c77"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 04" src="https://github.com/user-attachments/assets/e093f852-a6ea-4937-b0ce-b89276bd7135"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 55" src="https://github.com/user-attachments/assets/0f638ba9-a1ad-47b8-a874-957c0119384c"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 41 00" src="https://github.com/user-attachments/assets/1d77b650-70f1-4dee-9489-dc0122b7c9ff"> | <img width="204" alt="스크린샷 2024-10-29 오후 11 40 31" src="https://github.com/user-attachments/assets/db99b6b2-ae06-4758-8687-17ebb860a52b"> |
44+
| **INFJ** | **INFJ** | **INTP** | **INFP** | **ISTJ** |
45+
| **`FE`** | **`BE`** | **`BE`** | **`BE`** | **`FE`** |

apps/frontend/src/components/canvas/NoteNode.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Handle, NodeProps, Position, type Node } from "@xyflow/react";
2+
import { FileText } from "lucide-react";
23

34
import ActiveUser from "../commons/activeUser";
45

56
import usePageStore from "@/store/usePageStore";
67
import useUserStore from "@/store/useUserStore";
78
import Emoji from "../commons/emoji";
9+
import { useEffect, useState } from "react";
810

911
export type NoteNodeData = { title: string; id: number; emoji: string };
1012
export type NoteNodeType = Node<NoteNodeData, "note">;
@@ -13,6 +15,12 @@ export function NoteNode({ data }: NodeProps<NoteNodeType>) {
1315
const { setCurrentPage } = usePageStore();
1416
const { users } = useUserStore();
1517

18+
const [activeUsers, setActiveUsers] = useState(users);
19+
20+
useEffect(() => {
21+
setActiveUsers(users);
22+
}, [users]);
23+
1624
const handleNodeClick = () => {
1725
const id = data.id;
1826
if (id === undefined || id === null) {
@@ -24,7 +32,7 @@ export function NoteNode({ data }: NodeProps<NoteNodeType>) {
2432

2533
return (
2634
<div
27-
className="rounded-md border-[1px] border-black bg-neutral-100 p-2"
35+
className="h-24 w-48 rounded-lg border-[1px] border-[#eaeaea] bg-white p-3 shadow-sm"
2836
onClick={handleNodeClick}
2937
>
3038
<Handle
@@ -51,16 +59,26 @@ export function NoteNode({ data }: NodeProps<NoteNodeType>) {
5159
position={Position.Bottom}
5260
isConnectable={true}
5361
/>
54-
<div className="flex gap-1">
55-
<Emoji emoji={data.emoji} />
56-
<div>{data.title}</div>
62+
<div className="flex h-full w-full flex-col justify-between">
63+
<div className="flex w-full min-w-0 flex-row items-center justify-start gap-1">
64+
{data.emoji ? (
65+
<Emoji emoji={data.emoji} />
66+
) : (
67+
<FileText
68+
className="h-4 w-4 flex-shrink-0"
69+
strokeWidth="1.5px"
70+
color="#91918e"
71+
/>
72+
)}
73+
<div className="w-full truncate">{data.title}</div>
74+
</div>
75+
<ActiveUser
76+
className="self-end"
77+
users={activeUsers.filter(
78+
(user) => user.currentPageId === data.id.toString(),
79+
)}
80+
/>
5781
</div>
58-
<ActiveUser
59-
className="justify-end"
60-
users={users.filter(
61-
(user) => user.currentPageId === data.id.toString(),
62-
)}
63-
/>
6482
</div>
6583
);
6684
}

apps/frontend/src/components/commons/activeUser/index.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,35 @@ interface ActiveUserProps {
77
}
88

99
export default function ActiveUser({ users, className }: ActiveUserProps) {
10+
const maxVisibleUsers = 10;
11+
const hasMoreUsers = users.length > maxVisibleUsers;
12+
const visibleUsers = users.slice(0, maxVisibleUsers);
13+
1014
return (
11-
<div className={cn("flex gap-2", className)}>
12-
{users.map((user) => (
15+
<div className={cn("flex items-center", className)}>
16+
{visibleUsers.map((user, index) => (
1317
<div
1418
style={{ backgroundColor: user.color }}
15-
className={cn("group relative h-5 w-5 rounded-full")}
19+
className={cn(
20+
"group relative h-5 w-5 rounded-full",
21+
index !== 0 && "-ml-2",
22+
"hover:z-10",
23+
)}
1624
key={user.clientId}
1725
>
1826
<div
1927
style={{ backgroundColor: user.color }}
20-
className="absolute left-2 z-10 hidden px-2 text-sm group-hover:flex"
28+
className="absolute left-5 top-0 z-20 hidden max-w-28 truncate rounded-md px-2 py-1 text-sm text-white group-hover:block"
2129
>
2230
{user.clientId}
2331
</div>
2432
</div>
2533
))}
34+
{hasMoreUsers && (
35+
<div className="relative -ml-2 flex h-5 w-5 items-center justify-center rounded-full bg-gray-200 text-xs font-medium text-gray-600">
36+
+{users.length - maxVisibleUsers}
37+
</div>
38+
)}
2639
</div>
2740
);
2841
}
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import { cn } from "@/lib/utils";
2+
import { FileText } from "lucide-react";
23
import { Tailwindest } from "tailwindest";
34

45
interface EmojiProps {
5-
emoji: string;
6+
emoji: string | null;
67
width?: Tailwindest["width"];
78
height?: Tailwindest["height"];
89
fontSize?: Tailwindest["fontSize"];
910
}
1011

1112
export default function Emoji({ emoji, width, height, fontSize }: EmojiProps) {
13+
if (!emoji)
14+
return (
15+
<FileText
16+
className={cn("", width, height)}
17+
strokeWidth="1.5px"
18+
color="#91918e"
19+
/>
20+
);
21+
1222
return <div className={cn("", width, height, fontSize)}>{emoji}</div>;
1323
}

apps/frontend/src/components/editor/EditorActionPanel.tsx

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,41 @@ export default function EditorActionPanel({
2020
usePageStore();
2121

2222
return (
23-
<div
24-
className={cn(
25-
"mx-auto mb-2 flex items-center justify-between gap-2 p-4",
26-
isMaximized && "max-w-[900px] px-0",
27-
)}
28-
>
29-
<div className="flex items-center gap-1">
30-
<button
31-
onClick={togglePanel}
32-
className={cn(
33-
"z-50 flex h-6 w-6 !cursor-pointer items-center justify-center rounded-md hover:bg-neutral-200",
34-
!isPanelOpen &&
35-
"absolute -left-8 top-0 h-8 w-8 rounded-l border-b border-l border-t border-[#e0e6ee] bg-[#f5f5f5]",
36-
)}
37-
>
38-
{isPanelOpen ? (
39-
<PanelRightClose className="h-4 w-4" />
40-
) : (
41-
<PanelLeftClose className="h-4 w-4" />
42-
)}
43-
</button>
44-
<button
45-
onClick={toggleMaximized}
46-
className="flex h-6 w-6 !cursor-pointer items-center justify-center rounded-md rounded-l border-b border-l border-t border-none hover:bg-neutral-200"
47-
>
48-
{isMaximized ? (
49-
<Minimize2 className="h-4 w-4 rotate-90" />
50-
) : (
51-
<Maximize2 className="h-4 w-4 rotate-90" />
52-
)}
53-
</button>
23+
<div className="w-full px-4 py-2">
24+
<div
25+
className={cn(
26+
"flex w-full items-center justify-between",
27+
isMaximized && "mx-auto max-w-[900px]",
28+
)}
29+
>
30+
<div className="flex items-center gap-1">
31+
<button
32+
onClick={togglePanel}
33+
className={cn(
34+
"z-50 flex h-6 w-6 !cursor-pointer items-center justify-center rounded-md hover:bg-neutral-200",
35+
!isPanelOpen &&
36+
"absolute -left-8 top-0 h-8 w-8 rounded-l border-b border-l border-t border-[#e0e6ee] bg-[#f5f5f5]",
37+
)}
38+
>
39+
{isPanelOpen ? (
40+
<PanelRightClose className="h-4 w-4" />
41+
) : (
42+
<PanelLeftClose className="h-4 w-4" />
43+
)}
44+
</button>
45+
<button
46+
onClick={toggleMaximized}
47+
className="flex h-6 w-6 !cursor-pointer items-center justify-center rounded-md hover:bg-neutral-200"
48+
>
49+
{isMaximized ? (
50+
<Minimize2 className="h-4 w-4 rotate-90" />
51+
) : (
52+
<Maximize2 className="h-4 w-4 rotate-90" />
53+
)}
54+
</button>
55+
</div>
56+
<SaveStatus saveStatus={saveStatus} />
5457
</div>
55-
<SaveStatus saveStatus={saveStatus} />
5658
</div>
5759
);
5860
}

apps/frontend/src/components/editor/EditorTitle.tsx

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Emoji from "@/components/commons/emoji";
77
import useYDocStore from "@/store/useYDocStore";
88
import { useYText } from "@/hooks/useYText";
99
import { useOptimisticUpdatePage } from "@/hooks/usePages";
10+
import { cn } from "@/lib/utils";
1011

1112
interface EditorTitleProps {
1213
currentPage: number;
@@ -44,11 +45,11 @@ export default function EditorTitle({
4445
});
4546
};
4647

47-
const handleEmojiClick = (emoji: Emoji) => {
48-
setYEmoji(emoji.native);
48+
const handleEmojiClick = ({ native }: Emoji) => {
49+
setYEmoji(native);
4950

5051
optimisticUpdatePageMutation.mutate({
51-
pageData: { title, content: pageContent, emoji: emoji.native },
52+
pageData: { title, content: pageContent, emoji: native },
5253
});
5354

5455
setIsEmojiPickerOpen(false);
@@ -64,21 +65,46 @@ export default function EditorTitle({
6465
setIsEmojiPickerOpen(false);
6566
};
6667

68+
const handleRemoveIconClick = (e: React.MouseEvent) => {
69+
e.stopPropagation();
70+
71+
setYEmoji("");
72+
73+
optimisticUpdatePageMutation.mutate({
74+
pageData: { title, content: pageContent, emoji: "" },
75+
});
76+
77+
setIsEmojiPickerOpen(false);
78+
};
79+
6780
return (
6881
<div className="flex flex-col gap-3">
69-
<div className="relative">
70-
<button onClick={handleTitleEmojiClick}>
71-
<Emoji emoji={emoji} width="w-10" height="h-10" fontSize="text-6xl" />
72-
</button>
73-
<div
74-
className={`top-18 absolute z-50 ${isEmojiPickerOpen ? "block" : "hidden"}`}
75-
>
76-
<Picker
77-
theme="light"
78-
previewPosition="none"
79-
onEmojiSelect={handleEmojiClick}
80-
onClickOutside={handleEmojiOutsideClick}
81-
/>
82+
<div className="group">
83+
<div className={cn("relative duration-200")}>
84+
<button onClick={handleTitleEmojiClick}>
85+
<Emoji
86+
emoji={emoji}
87+
width="w-16"
88+
height="h-16"
89+
fontSize="text-6xl"
90+
/>
91+
</button>
92+
<div
93+
className={`top-18 absolute z-50 ${isEmojiPickerOpen ? "block" : "hidden"}`}
94+
>
95+
<button
96+
onClick={handleRemoveIconClick}
97+
className="absolute -top-8 right-0 z-10 rounded-md bg-neutral-50 p-1 px-2 text-sm hover:bg-neutral-200"
98+
>
99+
Remove
100+
</button>
101+
<Picker
102+
theme="light"
103+
previewPosition="none"
104+
onEmojiSelect={handleEmojiClick}
105+
onClickOutside={handleEmojiOutsideClick}
106+
/>
107+
</div>
82108
</div>
83109
</div>
84110
<input

apps/frontend/src/components/layout/EditorLayout.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,21 @@ const EditorLayout = ({ children, saveStatus }: EditorLayoutProps) => {
1313
return (
1414
<div
1515
className={cn(
16-
"absolute right-4 top-4 h-[720px] w-[520px] rounded-lg border bg-white shadow-lg transition-transform duration-100 ease-in-out",
16+
"absolute right-4 top-4 flex h-[720px] w-[520px] flex-col rounded-lg border bg-white shadow-lg transition-transform duration-100 ease-in-out",
1717
isPanelOpen ? "transform-none" : "translate-x-full",
1818
isMaximized ? "right-0 top-0 h-screen w-screen" : "",
1919
)}
2020
>
2121
<EditorActionPanel saveStatus={saveStatus} />
22-
<div
23-
className={cn(
24-
"flex h-full flex-col gap-4 overflow-auto px-12 py-4",
25-
isMaximized && "mx-auto mt-8 box-content w-[800px] px-0",
26-
)}
27-
>
28-
{children}
29-
</div>
22+
{isMaximized ? (
23+
<div className="flex-1 overflow-auto">
24+
<div className="mx-auto w-[800px] py-4">{children}</div>
25+
</div>
26+
) : (
27+
<div className="flex flex-1 flex-col gap-4 overflow-auto px-12 py-4">
28+
{children}
29+
</div>
30+
)}
3031
</div>
3132
);
3233
};

apps/frontend/src/components/sidebar/NoteList.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,18 @@ export default function NoteList({ className }: NoteListProps) {
3636
<Button
3737
onClick={() => handleNoteClick(id)}
3838
key={id}
39-
className="group flex flex-row justify-between gap-1 rounded-sm px-3 py-1 hover:bg-neutral-100"
39+
className="group flex flex-row items-center justify-between gap-1 rounded-sm px-3 py-1 hover:bg-neutral-100"
4040
>
41-
{emoji && <Emoji emoji={emoji} />}
42-
<div className="w-full truncate text-start">{title}</div>
41+
<Emoji emoji={emoji} width="w-5" height="h-5" />
42+
<div className="flex-1 truncate text-start">{title}</div>
4343
<span
4444
className="hidden text-neutral-400 transition-colors group-hover:block hover:text-red-500"
4545
onClick={(e) => {
4646
e.stopPropagation();
4747
openModal(id);
4848
}}
4949
>
50-
<Trash2 width={20} height={20} />
50+
<Trash2 width={18} height={18} />
5151
</span>
5252
</Button>
5353
))}

0 commit comments

Comments
 (0)