diff --git a/src/components/common/post/detail/PostDetailActions.tsx b/src/components/common/post/detail/PostDetailActions.tsx new file mode 100644 index 0000000..d796fd7 --- /dev/null +++ b/src/components/common/post/detail/PostDetailActions.tsx @@ -0,0 +1,73 @@ +import { useCallback } from "react"; +import { usePostDelete } from "@/hooks/usePostDelete"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, + Button, +} from "@/components/ui"; + +import type { Post } from "@/types/post.type"; +import type { User } from "@/types/user.type"; + +interface Props { + post: Post; + user: User; +} + +export default function PostDetailActions({ post, user }: Props) { + const { handleDelete } = usePostDelete(); + + const handleDeleteClick = useCallback(() => { + handleDelete(post.id); + }, [post.id]); + + return ( +
+ {post?.author === user?.id && ( +
+ + + + + + + + + 해당 모집 게시글을 삭제하시겠습니까? + + + 삭제하시면 영구적으로 삭제되어 복구할 수 없습니다. + + + + 닫기 + + 삭제 + + + + +
+ )} +
+ ); +} diff --git a/src/components/common/post/detail/PostDetailContent.tsx b/src/components/common/post/detail/PostDetailContent.tsx new file mode 100644 index 0000000..925ba7e --- /dev/null +++ b/src/components/common/post/detail/PostDetailContent.tsx @@ -0,0 +1,18 @@ +import { Editor } from "@/components/write"; +import type { Post } from "@/types/post.type"; + +interface Props { + post: Post; +} + +export default function PostDetailContent({ post }: Props) { + return ( +
+
+
+ {post?.content && } +
+
+
+ ); +} diff --git a/src/components/common/post/detail/PostDetailHeader.tsx b/src/components/common/post/detail/PostDetailHeader.tsx new file mode 100644 index 0000000..67bc84c --- /dev/null +++ b/src/components/common/post/detail/PostDetailHeader.tsx @@ -0,0 +1,26 @@ +import type { Post } from "@/types/post.type"; +import { Separator } from "@/components/ui"; +import dayjs from "dayjs"; + +interface Props { + post: Post; +} + +export default function PostDetailHeader({ post }: Props) { + return ( +
+
+ # {post?.category} +

+ {post?.title} +

+ + + + + {dayjs(post?.created_at).format("YYYY. MM. DD")} + +
+
+ ); +} diff --git a/src/components/common/post/detail/PostDetailMeta.tsx b/src/components/common/post/detail/PostDetailMeta.tsx new file mode 100644 index 0000000..b68e09f --- /dev/null +++ b/src/components/common/post/detail/PostDetailMeta.tsx @@ -0,0 +1,103 @@ +import type { Post } from "@/types/post.type"; +import { Badge } from "@/components/ui"; + +interface Props { + post: Post; +} + +export default function PostDetailMeta({ post }: Props) { + return ( +
+
+
+ {/* 모집 인원 */} +
+ + 모집 인원 + + + {post?.members} + +
+ + {/* 진행 방식 */} +
+ + 진행 방식 + + + {post?.progress_method} + +
+ + {/* 예상 기간 */} +
+ + 예상 기간 + + + {post?.duration} + +
+ + {/* 연락 수단 */} +
+ + 연락 수단 + + + + {post?.contact} + + +
+
+ + {/* 모집 분야 */} +
+
+ + 모집 분야 + +
+ {post?.position?.map((pos: string) => ( + + {pos} + + ))} +
+
+ + {/* 기술 스택 */} +
+ + 기술 스택 + +
+ {post?.tech_stack?.map((tech: string) => ( + {tech} + ))} +
+
+
+
+
+ ); +} diff --git a/src/hooks/usePostDelete.ts b/src/hooks/usePostDelete.ts new file mode 100644 index 0000000..d438a76 --- /dev/null +++ b/src/hooks/usePostDelete.ts @@ -0,0 +1,30 @@ +import supabase from "@/lib/supabase"; +import { useCallback } from "react"; +import { useNavigate } from "react-router"; +import { toast } from "sonner"; + +export const usePostDelete = () => { + const navigate = useNavigate(); + + const handleDelete = useCallback(async (id: number) => { + if (!id) { + toast.error("게시글 ID를 찾을 수 없습니다."); + return; + } + try { + const { error } = await supabase.from("post").delete().eq("id", id); + + if (error) { + toast.error(error.message); + return; + } + + toast.success("글을 삭제하였습니다."); + navigate("/"); + } catch (error) { + console.log(error); + throw error; + } + }, []); + return { handleDelete }; +}; diff --git a/src/pages/post/[id]/detail.tsx b/src/pages/post/[id]/detail.tsx index bbc288d..1a9ef57 100644 --- a/src/pages/post/[id]/detail.tsx +++ b/src/pages/post/[id]/detail.tsx @@ -1,33 +1,19 @@ -import supabase from "@/lib/supabase"; - +import { useEffect } from "react"; +import { useParams } from "react-router"; import { useAuthStore } from "@/stores"; import { usePostDetail } from "@/hooks/usePostDetail"; -import { useEffect } from "react"; -import { useNavigate, useParams } from "react-router"; -import { Editor } from "@/components/write"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, - Button, - Separator, - Spinner, -} from "@/components/ui"; +import PostDetailHeader from "@/components/common/post/detail/PostDetailHeader"; +import PostDetailActions from "@/components/common/post/detail/PostDetailActions"; +import PostDetailMeta from "@/components/common/post/detail/PostDetailMeta"; +import PostDetailContent from "@/components/common/post/detail/PostDetailContent"; + +import { Separator, Spinner } from "@/components/ui"; import { toast } from "sonner"; -import { Badge } from "@/components/ui"; -import dayjs from "dayjs"; export default function PostDetail() { const { id } = useParams(); const user = useAuthStore((state) => state.user); - const navigate = useNavigate(); const { post, isLoading, isError } = usePostDetail(id); @@ -37,203 +23,31 @@ export default function PostDetail() { } }, [isError]); - if (!post && !isLoading) { + if (isLoading) { return (
- 게시글이 존재하지 않습니다. +
); } - const handleDelete = async () => { - try { - const { error } = await supabase.from("post").delete().eq("id", id); - - if (error) { - toast.error(error.message); - return; - } - toast.success("글을 삭제하였습니다."); - navigate("/"); - } catch (error) { - console.log(error); - throw error; - } - }; + if (!post) { + return ( +
+ 게시글이 존재하지 않습니다. +
+ ); + } return ( <> - {isLoading ? ( -
- -
- ) : ( - <> - {/* 수정 및 삭제 버튼 */} -
- {post?.author === user?.id && ( -
- - - - - - - - - 해당 모집 게시글을 삭제하시겠습니까? - - - 삭제하시면 영구적으로 삭제되어 복구할 수 없습니다. - - - - 닫기 - - 삭제 - - - - -
- )} -
- -
-
- # {post?.category} -

- {post?.title} -

- - - - - {dayjs(post?.created_at).format("YYYY. MM. DD")} - -
-
- -
-
-
- {/* 모집 인원 */} -
- - 모집 인원 - - - {post?.members} - -
- - {/* 진행 방식 */} -
- - 진행 방식 - - - {post?.progress_method} - -
- - {/* 예상 기간 */} -
- - 예상 기간 - - - {post?.duration} - -
- - {/* 연락 수단 */} -
- - 연락 수단 - - - - {post?.contact} - - -
-
- - {/* 모집 분야 */} -
-
- - 모집 분야 - -
- {post?.position?.map((pos: string) => ( - - {pos} - - ))} -
-
- - {/* 기술 스택 */} -
- - 기술 스택 - -
- {post?.tech_stack?.map((tech: string) => ( - {tech} - ))} -
-
-
-
-
+ {user && } + + - + -
-
-
- {post?.content && } -
-
-
- - )} + ); }