diff --git a/gitmon-client/src/app/[id]/[repo]/[slug]/page.tsx b/gitmon-client/src/app/[id]/[repo]/[slug]/page.tsx index 4dc6a9b..9a0f479 100644 --- a/gitmon-client/src/app/[id]/[repo]/[slug]/page.tsx +++ b/gitmon-client/src/app/[id]/[repo]/[slug]/page.tsx @@ -3,12 +3,13 @@ import Link from 'next/link' import { ArrowLeft, Calendar, MessageSquare } from 'lucide-react' import { formatDate, replaceId } from '@lib/utils' import { Separator } from '@components/ui/separator' -import { CommentSection } from './CommentSection' +import { CommentSection } from '@components/Post/CommentSection' import { fetchPost } from '@lib/github' import MarkdownRenderer from '@components/MarkdownRenderer' import { ShareButton } from '@components/ShareButton' import { LikeButton } from '@components/LikeButton' import { cookies } from 'next/headers' +import { PostButtons } from '@components/Post/PostButtons' export default async function BlogPost({ params, @@ -70,17 +71,20 @@ export default async function BlogPost({

{post.title}

-
-
- - -
-
- - {'3'} comments +
+
+
+ + +
+
+ + {'3'} comments +
+
diff --git a/gitmon-client/src/app/[id]/[repo]/[slug]/update/UpdatePost.tsx b/gitmon-client/src/app/[id]/[repo]/[slug]/update/UpdatePost.tsx new file mode 100644 index 0000000..96bf796 --- /dev/null +++ b/gitmon-client/src/app/[id]/[repo]/[slug]/update/UpdatePost.tsx @@ -0,0 +1,108 @@ +'use client' + +import { useState } from 'react' + +import { useMutation } from '@tanstack/react-query' +import { useParams, useRouter } from 'next/navigation' +import { toast } from 'sonner' + +import { PostForm } from '@components/Post' +import matter from 'gray-matter' +import { titleToSlug } from '@lib/utils' +import { Post, PostMeta } from '@lib/types' + +interface UpdatePostProps { + token: string | null + post: Omit +} + +export default function UpdatePost({ token, post }: UpdatePostProps) { + const router = useRouter() + const { slug } = useParams() + const [user, setUser] = useState<{ id: string; repo: string }>() + + console.log(slug) + + const { mutate } = useMutation({ + // 해당 부분을 업데이트치는 API 호출로 변경해야 함 + mutationFn: async ({ title, blob }: { title: string; blob: Blob }) => { + const body = new FormData() + const fileName = titleToSlug(title) + body.append('title', fileName) + body.append('content', blob, fileName) + body.append('id', slug as string) + + const response = await fetch(`${process.env.NEXT_PUBLIC_API_DOMAIN}/api/v1/posting`, { + method: 'PUT', + headers: { + Authorization: `Bearer ${token}`, + }, + body, + }) + + if (!response.ok) { + toast('게시글 저장에 실패했습니다.') + throw new Error('게시글 저장 실패') + } + return response + }, + onSuccess: async (res, {}) => { + const { data } = await res.json() + + toast('게시글이 저장되었습니다.') + router.push(`/${user?.id}/${user?.repo}/${data.id}`) + }, + }) + + const handleSavePost = async ({ title, content }: { title: string; content: string }) => { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_DOMAIN}/api/v1/member`, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }) + + if (!res.ok) { + toast('사용자 정보를 가져오는 데 실패했습니다.') + return + } + + const { data } = await res.json() + setUser({ id: data.githubUsername, repo: data.repoName }) + + if (!data.githubUsername || !data.repoName) { + toast('레포지토리 정보가 없습니다. 먼저 레포지토리를 설정해주세요.') + return + } + + if (!title) { + toast('게시글 제목을 입력해주세요.') + return + } + + const metadata: PostMeta = { + title: title.trim(), + slug: titleToSlug(title), + repo: data.repoName, + excerpt: content.slice(0, 100), + coverImage: '', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + pinned: false, + tags: [], + author: data.githubUsername, + } + + const markdown = matter.stringify(content, metadata) + const blob = new Blob([markdown], { type: 'text/markdown' }) + + mutate({ title, blob }) + } + + return ( +
+ +
+ ) +} diff --git a/gitmon-client/src/app/[id]/[repo]/[slug]/update/page.tsx b/gitmon-client/src/app/[id]/[repo]/[slug]/update/page.tsx new file mode 100644 index 0000000..8553c42 --- /dev/null +++ b/gitmon-client/src/app/[id]/[repo]/[slug]/update/page.tsx @@ -0,0 +1,42 @@ +import Link from 'next/link' +import { cookies } from 'next/headers' + +import UpdatePost from './UpdatePost' +import { replaceId } from '@lib/utils' +import { fetchPost } from '@lib/github' + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string; id: string; repo: string }> +}) { + const { id, repo, slug } = await params + const token = (await cookies()).get('github_token')?.value ?? null + + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_DOMAIN}/api/v1/posting/github/${replaceId(id)}/${slug}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + }, + ) + + const { data } = await res.json() + + if (!data?.githubDownloadUrl) { + return ( +
+ 해당 포스트를 찾을 수 없습니다.
+ + 다른 포스트 보기 + +
+ ) + } + const post = await fetchPost(data?.githubDownloadUrl) + + return +} diff --git a/gitmon-client/src/app/[id]/[repo]/[slug]/CommentSection.tsx b/gitmon-client/src/components/Post/CommentSection.tsx similarity index 100% rename from gitmon-client/src/app/[id]/[repo]/[slug]/CommentSection.tsx rename to gitmon-client/src/components/Post/CommentSection.tsx diff --git a/gitmon-client/src/components/Post/PostButtons.tsx b/gitmon-client/src/components/Post/PostButtons.tsx new file mode 100644 index 0000000..09efab3 --- /dev/null +++ b/gitmon-client/src/components/Post/PostButtons.tsx @@ -0,0 +1,36 @@ +'use client' + +import { Button } from '@components/ui' +import { useRouter } from 'next/navigation' +import React from 'react' + +interface PostButtonsProps { + id: string + repo: string + slug: string +} + +export const PostButtons = ({ id, repo, slug }: PostButtonsProps) => { + const router = useRouter() + + const handleUpdate = () => { + router.push(`/@${id}/${repo}/${slug}/update`) + } + + const handleDelete = () => { + const confirm = window.confirm('정말 삭제하시겠습니까?') + if (confirm) { + console.log('delete') + } + } + return ( +
+ + +
+ ) +} diff --git a/gitmon-client/src/components/Post/PostForm.tsx b/gitmon-client/src/components/Post/PostForm.tsx index e2e3d69..758477b 100644 --- a/gitmon-client/src/components/Post/PostForm.tsx +++ b/gitmon-client/src/components/Post/PostForm.tsx @@ -26,15 +26,17 @@ import { } from 'lucide-react' import Link from 'next/link' import { useParams } from 'next/navigation' +import { Post } from '@lib/types' interface PostFormProps { onPostSaved: (post: { title: string; content: string }) => void + post?: Omit } -export function PostForm({ onPostSaved }: PostFormProps) { +export function PostForm({ onPostSaved, post }: PostFormProps) { const params = useParams() - const [title, setTitle] = useState('') - const [content, setContent] = useState('') + const [title, setTitle] = useState(post?.title || '') + const [content, setContent] = useState(post?.content || '') const [showImageUploader, setShowImageUploader] = useState(false) const textareaRef = useRef(null)