Skip to content

Commit

Permalink
refactor: Document Page 컴포넌트 분할
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee-Dongwook committed Oct 19, 2024
1 parent c6a3c30 commit eb4da7d
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 109 deletions.
112 changes: 3 additions & 109 deletions client/src/app/document/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,111 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import DocumentPage from "@/template/DocumentPage";

import { useState, useEffect } from "react";
import socket from "@/lib/socket";
import dynamic from "next/dynamic";
import API from "@/lib/api";
import useDebounce from "@/hook/useDebounce";

import "react-quill/dist/quill.snow.css";

const QuillNoSSRWrapper = dynamic(() => import("react-quill"), {
ssr: false,
loading: () => <p>Loading...</p>,
});

export default function DocumentPage({ params }: { params: { id: string } }) {
const { id } = params;
const [content, setContent] = useState<string>("");
const [versions, setVersions] = useState<any[]>([]);
const debouncedContent = useDebounce(content, 500);

const fetchDocument = async () => {
try {
const { data } = await API.get(`/api/document/${id}`);
setContent(data.content);
} catch (error) {
console.error("Failed to fetch document:", error);
}
};

const fetchVersionsOfDocument = async () => {
try {
const { data } = await API.get(`/api/documents/${id}/versions`);
setVersions(data);
} catch (error) {
console.error("Failed to fetch versions:", error);
}
};

const restoreVersionOfDocument = async (index: number) => {
try {
await API.put(`/api/documents/${id}/restore`, { versionIndex: index });
const { data } = await API.get(`/api/documents/${id}`);
setContent(data.content);
} catch (error) {
console.error("Failed to restore version:", error);
}
};

const handleChangeContent = (value: string) => {
setContent(value);
socket.emit("edit-document", id, value);
};

useEffect(() => {
if (!id) return;

fetchDocument();
fetchVersionsOfDocument();

socket.connect();
socket.emit("join-document", id);

socket.on("receive-edit", (updatedContent: string) => {
setContent(updatedContent);
});

return () => {
socket.disconnect();
};
}, [id]);

useEffect(() => {
if (debouncedContent) {
API.put(`/api/document/${id}`, { content: debouncedContent }).catch(
(error) => console.error("Failed to save document", error)
);
}
}, [debouncedContent]);

return (
<div className="p-4">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold">Document ID: {id}</h1>
<div className="mt-4">
<QuillNoSSRWrapper
theme="snow"
value={content}
onChange={handleChangeContent}
className="bg-white p-2"
/>
</div>
<div className="mt-4">
<h2 className="text-lg font-bold">Version History</h2>
{versions.map((version, index) => (
<div key={index} className="mt-2">
<button
className="bg-gray-200 p-1 rounded hover:bg-gray-300 "
onClick={() => restoreVersionOfDocument(index)}
>
Restore Version {index + 1}
</button>
</div>
))}
</div>
</div>
</div>
);
export default function Page(props) {
return <DocumentPage {...props} />;
}
30 changes: 30 additions & 0 deletions client/src/components/DocumentEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import dynamic from "next/dynamic";
import "react-quill/dist/quill.snow.css";

const QuillNoSSRWrapper = dynamic(() => import("react-quill"), {
ssr: false,
loading: () => <p>Loading...</p>,
});

interface DocumentEditorProps {
content: string;
onChange: (value: string) => void;
}

export default function DocumentEditor({
content,
onChange,
}: DocumentEditorProps) {
return (
<div className="mt-4">
<QuillNoSSRWrapper
theme="snow"
value={content}
onChange={onChange}
className="bg-white p-2"
/>
</div>
);
}
27 changes: 27 additions & 0 deletions client/src/components/VersionHistory/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

interface VersionHistoryProps {
versions: any[];
onRestore: (index: number) => void;
}

export default function VersionHistory({
versions,
onRestore,
}: VersionHistoryProps) {
return (
<div className="mt-4">
<h2 className="text-lg font-bold">Version History</h2>
{versions.map((version, index) => (
<div key={index} className="mt-2">
<button
className="bg-gray-200 p-1 rounded hover:bg-gray-300"
onClick={() => onRestore(index)}
>
Restore Version {index + 1}
</button>
</div>
))}
</div>
);
}
97 changes: 97 additions & 0 deletions client/src/template/DocumentPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";

import { useState, useEffect } from "react";

import DocumentEditor from "@/components/DocumentEditor";
import VersionHistory from "@/components/VersionHistory";
import useDebounce from "@/hook/useDebounce";
import socket from "@/lib/socket";
import API from "@/lib/api";

interface DocumentPageProps {
params: {
id: string;
};
}

export default function DocumentPage({ params }: DocumentPageProps) {
const { id } = params;

const [content, setContent] = useState<string>("");
const [versions, setVersions] = useState<any[]>([]);
const debouncedContent = useDebounce(content, 500);

const fetchDocument = async () => {
try {
const { data } = await API.get(`/api/document/${id}`);
setContent(data.content);
} catch (error) {
console.error("Failed to fetch document: ", error);
}
};

const fetchVersionsOfDocument = async () => {
try {
const { data } = await API.get(`/api/documents/${id}/versions`);
setVersions(data);
} catch (error) {
console.error("Failed to fetch versions: ", error);
}
};

const restoreVersionOfDocument = async (index: number) => {
try {
await API.put(`/api/documents/${id}/restore`, { versionIndex: index });
const { data } = await API.get(`/api/documents/${id}`);
setContent(data.content);
} catch (error) {
console.error("Failed to restore version:", error);
}
};

const handleChangeContent = (value: string) => {
setContent(value);
socket.emit("edit-document", id, value);
};

useEffect(() => {
if (!id) return;

fetchDocument();
fetchVersionsOfDocument();

socket.connect();
socket.emit("join-document", id);

socket.on("receive-edit", (updatedContent: string) => {
setContent(updatedContent);
});

return () => {
socket.disconnect();
};
}, [id]);

useEffect(() => {
if (debouncedContent) {
API.put(`/api/document/${id}`, { content: debouncedContent }).catch(
(error) => console.error("Failed to save document", error)
);
}
}, [debouncedContent]);

return (
<div className="p-4">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold">Document ID: {id}</h1>
</div>
<DocumentEditor content={content} onChange={handleChangeContent} />
<VersionHistory
versions={versions}
onRestore={restoreVersionOfDocument}
/>
</div>
);
}
1 change: 1 addition & 0 deletions client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"noImplicitAny": false,
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
Expand Down

0 comments on commit eb4da7d

Please sign in to comment.