Skip to content

Commit 10f78c0

Browse files
authored
Merge pull request #69 from boostcampwm-2024/feature-fe-#17
페이지 query 및 mutation 구현
2 parents c0e0d2e + ea057d1 commit 10f78c0

File tree

12 files changed

+299
-774
lines changed

12 files changed

+299
-774
lines changed

frontend/src/api/page.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,36 @@ export type Page = {
88
content: JSONContent;
99
};
1010

11+
export type CreatePageRequest = {
12+
title: string;
13+
content: JSONContent;
14+
x: number;
15+
y: number;
16+
};
17+
1118
export type PageRequest = {
1219
title: string;
1320
content: JSONContent;
1421
};
1522

23+
export const getPage = async (id: number) => {
24+
const url = `/page/${id}`;
25+
26+
const res = await Get<Page>(url);
27+
return res.data;
28+
};
29+
1630
export const getPages = async () => {
1731
const url = "/page";
1832

1933
const res = await Get<Page[]>(url);
2034
return res.data;
2135
};
2236

23-
export const createPage = async (id: number, pageData: PageRequest) => {
24-
const url = `/page/${id}`;
37+
export const createPage = async (pageData: CreatePageRequest) => {
38+
const url = `/page`;
2539

26-
const res = await Post<null, PageRequest>(url, pageData);
40+
const res = await Post<null, CreatePageRequest>(url, pageData);
2741
return res.data;
2842
};
2943

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,14 @@
11
import usePageStore from "@/store/usePageStore";
22
import Editor from "./editor";
3-
import { JSONContent } from "novel";
4-
5-
interface Page {
6-
title: string;
7-
content?: JSONContent;
8-
}
9-
10-
const pages: Page[] = [
11-
{
12-
title: "Page 1",
13-
content: {
14-
type: "doc",
15-
content: [
16-
{
17-
type: "paragraph",
18-
content: [{ type: "text", text: "This is page 1" }],
19-
},
20-
],
21-
},
22-
},
23-
24-
{
25-
title: "Page 2",
26-
content: {
27-
type: "doc",
28-
content: [
29-
{
30-
type: "paragraph",
31-
content: [{ type: "text", text: "This is page 2" }],
32-
},
33-
],
34-
},
35-
},
36-
37-
{
38-
title: "Page 3",
39-
content: {
40-
type: "doc",
41-
content: [
42-
{
43-
type: "paragraph",
44-
content: [{ type: "text", text: "This is page 3" }],
45-
},
46-
],
47-
},
48-
},
49-
];
50-
51-
const getPageFromID = (id: number) => {
52-
return pages[id];
53-
};
3+
import { usePage } from "@/hooks/usePages";
544

555
export default function EditorView() {
566
const { currentPage } = usePageStore();
57-
if (currentPage === null) {
58-
return null;
59-
}
7+
const { data } = usePage(currentPage);
608

61-
const defaultEditorContent = getPageFromID(currentPage).content;
62-
if (!defaultEditorContent) {
63-
return (
64-
<div>
65-
페이지를 로딩할 수 없었습니다<div className=""></div>
66-
</div>
67-
);
9+
if (!data) {
10+
return <div></div>;
6811
}
6912

70-
return (
71-
<Editor
72-
key={currentPage}
73-
initialValue={defaultEditorContent}
74-
pageId={currentPage}
75-
/>
76-
);
13+
return <Editor key={data.id} initialValue={data.content} pageId={data.id} />;
7714
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { cn } from "@/lib/utils";
2+
3+
interface ButtonProps {
4+
className?: string;
5+
onClick?: () => void;
6+
children?: React.ReactNode;
7+
}
8+
9+
export default function Button({ className, onClick, children }: ButtonProps) {
10+
return (
11+
<button
12+
onClick={onClick}
13+
className={cn(
14+
"rounded-md px-6 py-2 shadow-md transition-colors",
15+
className,
16+
)}
17+
>
18+
{children}
19+
</button>
20+
);
21+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useRef } from "react";
2+
import { X } from "lucide-react";
3+
4+
interface DialogTitleProps {
5+
children: React.ReactNode;
6+
}
7+
8+
function DialogTitle({ children }: DialogTitleProps) {
9+
return <h2 className="text-lg font-semibold">{children}</h2>;
10+
}
11+
12+
interface DialogCloseButtonProps {
13+
onCloseModal: () => void;
14+
}
15+
16+
function DialogCloseButton({ onCloseModal }: DialogCloseButtonProps) {
17+
return (
18+
<button
19+
className="absolute right-4 top-4 text-neutral-400 transition-colors hover:text-neutral-700"
20+
onClick={onCloseModal}
21+
>
22+
<X width={16} height={16} />
23+
</button>
24+
);
25+
}
26+
27+
interface DialogMainProps {
28+
isOpen: boolean;
29+
onCloseModal: () => void;
30+
children: React.ReactNode;
31+
}
32+
33+
function DialogMain({ isOpen, onCloseModal, children }: DialogMainProps) {
34+
const dialogRef = useRef<HTMLDialogElement>(null);
35+
36+
const showModal = () => {
37+
dialogRef.current?.showModal();
38+
};
39+
40+
const closeModal = () => {
41+
dialogRef.current?.close();
42+
};
43+
44+
if (isOpen) {
45+
showModal();
46+
} else {
47+
closeModal();
48+
}
49+
50+
return (
51+
<dialog
52+
className="rounded-xl"
53+
open={isOpen}
54+
ref={dialogRef}
55+
onClick={() => {
56+
onCloseModal();
57+
closeModal();
58+
}}
59+
>
60+
<div
61+
className="flex flex-col gap-4 px-12 py-10"
62+
onClick={(e) => e.stopPropagation()}
63+
>
64+
{children}
65+
</div>
66+
</dialog>
67+
);
68+
}
69+
70+
export const Dialog = Object.assign(DialogMain, {
71+
Title: DialogTitle,
72+
CloseButton: DialogCloseButton,
73+
});

0 commit comments

Comments
 (0)