Skip to content

Commit

Permalink
invalidate 로직 리팩토링 & 삭제 확인 모달 기능 추가 (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
jijiseong authored Jul 13, 2024
1 parent 69a5b01 commit c1032ef
Show file tree
Hide file tree
Showing 33 changed files with 515 additions and 284 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
'use client';

import Avatar from '@app/_components/user/Avatar';
import { getArticleDetail } from '@app/_service/article';
import useArticleDetailQuery from '@app/_hooks/apis/boards/useArticleDetailQuery';

interface Props {
articleId: string;
}

async function AuthorSection({ articleId }: Props) {
const {
article: { nickname, profile, introduce },
} = await getArticleDetail(articleId);
function AuthorSection({ articleId }: Props) {
const { data } = useArticleDetailQuery(Number(articleId));

if (!data) return <></>;

const { nickname, profile, introduce } = data.article;

return (
<div className="flex items-center gap-8 ">
<section className="mb-32 flex items-center gap-8">
<Avatar
className="size-20"
rounded
Expand All @@ -24,7 +28,7 @@ async function AuthorSection({ articleId }: Props) {
<span className="text-lg font-bold">{nickname}</span>
<p>{introduce}</p>
</div>
</div>
</section>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import ButtonWithDialogCheck from '@app/_components/common/WithDialogCheck';
import {
useDeleteArticleMutation,
useUpdateArticleMutation,
Expand All @@ -25,7 +26,7 @@ function ButtonSection({ articleId, userId }: Props) {
const { mutate: mutateDeletion } = useDeleteArticleMutation(
Number(articleId),
);
const { mutate: mutateUpdate } = useUpdateArticleMutation(articleId);
const { mutate: mutateUpdate } = useUpdateArticleMutation(Number(articleId));
const isMine = myProfile?.id === userId;
const isAdmin = myProfile?.role === 'admin';

Expand Down Expand Up @@ -58,9 +59,16 @@ function ButtonSection({ articleId, userId }: Props) {
</Button>
)}

<Button size="sm" variant="destructive" onClick={deleteArticle}>
<ButtonWithDialogCheck
title="게시글 삭제"
description="정말 게시글을 삭제하시겠어요?"
size="sm"
variant="destructive"
confirmVariant="destructive"
onClick={deleteArticle}
>
삭제
</Button>
</ButtonWithDialogCheck>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function CommentEditorSection({ articleId }: Props) {

return (
<form
className="flex flex-col gap-4"
className="mb-20 flex flex-col gap-4"
onSubmit={handleSubmit(onValid, inValid)}
>
<Label className="text-lg" htmlFor={commentEditorId}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ButtonWithDialogCheck from '@app/_components/common/WithDialogCheck';
import useCommentDelete from '@app/_hooks/apis/boards/useCommentDelete';
import useCommentUpdate from '@app/_hooks/apis/boards/useCommentUpdate';
import { Button } from '@app/_shadcn/components/ui/button';
Expand Down Expand Up @@ -43,9 +44,16 @@ function Buttons({ articleId, commentId, editable, setEditable }: Props) {
</Button>
)}

<Button size="sm" variant="destructive" onClick={deleteComment}>
<ButtonWithDialogCheck
title="댓글 삭제"
description="정말 댓글을 삭제하시겠어요?"
size="sm"
variant="destructive"
confirmVariant="destructive"
onClick={deleteComment}
>
삭제
</Button>
</ButtonWithDialogCheck>
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FormProvider, useForm } from 'react-hook-form';
import { useComments } from '@app/_hooks/apis/boards';
import { myProfileState } from '@app/_store/permissionAtoms';
import Avatar from '@app/_components/user/Avatar';
import Spinner from '@app/_components/Spinner';
import Buttons from './Buttons';
import Content from './Content';
import Header from './Header';
Expand All @@ -16,8 +17,9 @@ interface Props {
}

function CommentSection({ articleId }: Props) {
const { data: comments } = useComments(Number(articleId));
const { data: comments, isLoading } = useComments(Number(articleId));

if (isLoading) return <Spinner />;
if (!comments) return <></>;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ import { useParams } from 'next/navigation';
import { BoardDetailParams } from '@app/_types/boardTypes';
import { PlateEditor } from '@app/_components/molecules/PlateEditor';
import { TElement } from '@udecode/plate-common';
import useArticleDetailQuery from '@app/_hooks/apis/boards/useArticleDetailQuery';

interface Props {
articleContent: string;
}

function ContentSection({ articleContent }: Props) {
function ContentSection() {
const params = useParams<BoardDetailParams>();
const editable = useRecoilValue(editableStateFamily(params));
const { setValue } = useFormContext();
const { data } = useArticleDetailQuery(Number(params.articleId));

if (!data) return null;

const onValueChange = (blocks: TElement[]) => {
const valueString = JSON.stringify(blocks);
setValue('content', valueString);
};

const initialValue: TElement[] = JSON.parse(articleContent);
const initialValue: TElement[] = JSON.parse(data?.article.content);

return (
<div className="mb-40 mt-4">
Expand Down
34 changes: 20 additions & 14 deletions app/(route)/boards/[boardType]/detail/[articleId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from '@tanstack/react-query';
import { articleDeatilQueryOption } from '@app/_hooks/apis/boards/useArticleDetailQuery';
import { formatDistanceToNow } from 'date-fns';
import { ko } from 'date-fns/locale';
import { getArticleDetail } from '@app/_service/article';
import { Separator } from '@app/_shadcn/components/ui/separator';
import {
ButtonSection,
Expand All @@ -17,19 +22,23 @@ export default async function ArticleDetailPage({
}: {
params: { articleId: string };
}) {
const queryClient = new QueryClient();
const options = articleDeatilQueryOption(Number(articleId));
const {
article: { title, userId, content, createdAt },
article: { title, userId, createdAt },
files,
} = await getArticleDetail(articleId);
} = await queryClient.fetchQuery(options);

return (
<ClientFormProvider>
<div>
<HydrationBoundary state={dehydrate(queryClient)}>
<ClientFormProvider>
<div className="flex items-center justify-between py-2">
<h1 className="w-full break-all text-4xl">{title}</h1>
<ButtonSection articleId={articleId} userId={userId} />
</div>

<Separator className="my-3" />

<div className="flex w-full justify-between">
<span className="text-sm text-muted-foreground">
{formatDistanceToNow(new Date(createdAt), {
Expand All @@ -39,15 +48,12 @@ export default async function ArticleDetailPage({
</span>
{files && files.length > 0 && <FileSection files={files} />}
</div>
<ContentSection articleContent={content} />
<div className="mb-32">
<AuthorSection articleId={articleId} />
</div>
<div className="mb-20">
<CommentEditorSection articleId={articleId} />
</div>

<ContentSection />
<AuthorSection articleId={articleId} />
<CommentEditorSection articleId={articleId} />
<CommentSection articleId={articleId} />
</div>
</ClientFormProvider>
</ClientFormProvider>
</HydrationBoundary>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use client';

import Link from 'next/link';
import { useParams } from 'next/navigation';

import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@app/_shadcn/components/ui/table';
import { BoardListParams } from '@app/_types/boardTypes';
import formateDate from '@app/_utils/formatDate';
import { BookOpen, MessageCircle, File } from 'lucide-react';
import { Tooltip, TooltipContent } from '@app/_shadcn/components/ui/tooltip';
import { TooltipTrigger } from '@app/_shadcn/components/plate-ui/tooltip';
import { Badge } from '@app/_shadcn/components/ui/badge';
import useArticleListQuery from '@app/_hooks/apis/boards/useArticleListQuery';

function BoardBody() {
const params = useParams<BoardListParams>();
const { data } = useArticleListQuery({
boardType: params.boardType,
page: Number(params.page),
category: params.category,
});

if (!data || data.articleList.length === 0)
return <div className="flex-center h-40">게시글이 없어요. 😭</div>;

const { articleList } = data;
return (
<Table aria-label="article table">
<TableHeader>
<TableRow>
<TableHead className="text-center">번호</TableHead>
<TableHead>제목</TableHead>
<TableHead className="text-center">작성자</TableHead>
<TableHead className="hidden text-center lg:table-cell">
날짜
</TableHead>
<TableHead className="hidden text-center lg:table-cell">
조회수
</TableHead>
</TableRow>
</TableHeader>

<TableBody>
{articleList?.map(
({
articleId,
title,
view,
nickname,
createdAt,
hide,
numOfComments,
file,
category,
}) => {
const formattedDate = formateDate(createdAt);
return (
<Link
key={articleId}
href={`/boards/${params.boardType}/detail/${articleId}`}
legacyBehavior
>
<TableRow key={articleId} className="cursor-pointer">
<TableCell className="text-center">{articleId}</TableCell>
<TableCell className="flex items-center">
{category !== 'all' && (
<Badge variant="secondary" className="mr-2 shrink-0">
{category}
</Badge>
)}

<span className="flex-center">
{hide && (
<Tooltip>
<TooltipTrigger>
<BookOpen
className="mr-2 stroke-muted-foreground"
size={15}
/>
</TooltipTrigger>
<TooltipContent>
해당 글은 정회원만 열람 가능해요.
</TooltipContent>
</Tooltip>
)}
</span>
<span className="truncate hover:text-clip">{title}</span>
<div className="ml-auto flex items-center gap-4 text-xs text-muted-foreground">
{numOfComments > 0 && (
<span className="flex items-center gap-1">
<MessageCircle size={14} />
{numOfComments}
</span>
)}
{file && (
<span>
<File size={14} />
</span>
)}
</div>
</TableCell>
<TableCell className="truncate text-center">
{nickname}
</TableCell>
<TableCell className="hidden text-center lg:table-cell">
{formattedDate}
</TableCell>
<TableCell className="hidden text-center lg:table-cell">
{view}
</TableCell>
</TableRow>
</Link>
);
},
)}
</TableBody>
</Table>
);
}

export default BoardBody;
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
'use client';

import { NEW_PATH } from '@app/_constants/urls';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@app/_shadcn/components/ui/select';
import { useRouter } from 'next/navigation';
import { BoardListParams } from '@app/_types/boardTypes';
import { useParams, useRouter } from 'next/navigation';

interface Props {
categories: string[];
}

function CategorySelect({ categories }: Props) {
const { push } = useRouter();
const { boardType, page } = useParams<BoardListParams>();

const handleSelectionChange = (value: string) => {
if (value) push(`?category=${value}`);
if (value)
push(
NEW_PATH.boardList.url({
boardType,
page: Number(page),
category: value,
}),
);
};

return (
Expand Down
Loading

0 comments on commit c1032ef

Please sign in to comment.