feat: 댓글 수정, 삭제 기능
This commit is contained in:
@@ -2,11 +2,13 @@ import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import {
|
||||
commentFormSchema,
|
||||
commentUpdateFormSchema,
|
||||
type Comment,
|
||||
type CommentService,
|
||||
type CommentList,
|
||||
type CommentParams,
|
||||
type CommentFormData,
|
||||
type CommentUpdateFormData,
|
||||
} from "../model/comment-interface";
|
||||
|
||||
export class CommentApiService implements CommentService {
|
||||
@@ -26,6 +28,32 @@ export class CommentApiService implements CommentService {
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async updateComment(
|
||||
bbsId: number,
|
||||
pstNo: number,
|
||||
cmntNo: number,
|
||||
data: CommentUpdateFormData,
|
||||
): Promise<Comment> {
|
||||
const validatedData = commentUpdateFormSchema.parse(data);
|
||||
const response = await client.put<Comment>(
|
||||
`${ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS}/${bbsId}/${pstNo}/${cmntNo}`,
|
||||
validatedData,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async deleteComment(
|
||||
bbsId: number,
|
||||
pstNo: number,
|
||||
cmntNo: number,
|
||||
cmntPswd: string,
|
||||
): Promise<Comment> {
|
||||
const response = await client.delete<Comment>(
|
||||
`${ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS}/${bbsId}/${pstNo}/${cmntNo}?cmntPswd=${cmntPswd}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const CommentApi = new CommentApiService();
|
||||
|
||||
@@ -6,4 +6,5 @@ export {
|
||||
type CommentList,
|
||||
type CommentParams,
|
||||
type CommentFormData,
|
||||
type CommentUpdateFormData,
|
||||
} from "./model/comment-interface";
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface Comment {
|
||||
rgtr: string;
|
||||
mdfcnDt: string;
|
||||
mdfr: string;
|
||||
delYn: string;
|
||||
}
|
||||
|
||||
export const commentFormSchema = z.object({
|
||||
@@ -39,16 +40,16 @@ export type CommentUpdateFormData = z.infer<typeof commentUpdateFormSchema>;
|
||||
export interface CommentService {
|
||||
getComments(params: CommentParams): Promise<CommentList>;
|
||||
createComment(data: CommentFormData): Promise<Comment>;
|
||||
// updateComment(
|
||||
// bbsId: number,
|
||||
// pstNo: number,
|
||||
// cmntNo: number,
|
||||
// data: CommentUpdateFormData,
|
||||
// ): Promise<Comment>;
|
||||
// deleteComment(
|
||||
// bbsId: number,
|
||||
// pstNo: number,
|
||||
// cmntNo: number,
|
||||
// cmntPswd: string,
|
||||
// ): Promise<void>;
|
||||
updateComment(
|
||||
bbsId: number,
|
||||
pstNo: number,
|
||||
cmntNo: number,
|
||||
data: CommentUpdateFormData,
|
||||
): Promise<Comment>;
|
||||
deleteComment(
|
||||
bbsId: number,
|
||||
pstNo: number,
|
||||
cmntNo: number,
|
||||
cmntPswd: string,
|
||||
): Promise<Comment>;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Button, Input, Textarea } from "@common/components";
|
||||
import {
|
||||
CommentApi,
|
||||
commentFormSchema,
|
||||
type CommentFormData,
|
||||
type CommentUpdateFormData,
|
||||
} from "@/entities/comment";
|
||||
import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form";
|
||||
import { useAlertStore, useUserStore } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore, usePrompt, useUserStore } from "@/shared/lib";
|
||||
import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form";
|
||||
|
||||
function PostComment({ pstNo }: { pstNo: number }) {
|
||||
function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) {
|
||||
const { prompt } = usePrompt();
|
||||
const { confirm } = useConfirm();
|
||||
const queryClient = useQueryClient();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const { userInfo } = useUserStore((state) => state);
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [editingCmntNo, setEditingCmntNo] = useState<number | null>(null);
|
||||
const [editValue, setEditValue] = useState<string>("");
|
||||
|
||||
const { data: comments } = useQuery({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS, pstNo],
|
||||
@@ -26,7 +33,7 @@ function PostComment({ pstNo }: { pstNo: number }) {
|
||||
const { mutate: createComment } = useMutation({
|
||||
mutationFn: (data: CommentFormData) => CommentApi.createComment(data),
|
||||
onSuccess: () => {
|
||||
alert("댓글이 등록되었습니다.");
|
||||
alert("등록되었습니다.");
|
||||
form.setValue("cmntCn", "");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS],
|
||||
@@ -34,6 +41,35 @@ function PostComment({ pstNo }: { pstNo: number }) {
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: updateComment } = useMutation({
|
||||
mutationFn: ({
|
||||
cmntNo,
|
||||
data,
|
||||
}: {
|
||||
cmntNo: number;
|
||||
data: CommentUpdateFormData;
|
||||
}) => CommentApi.updateComment(bbsId, pstNo, cmntNo, data),
|
||||
onSuccess: () => {
|
||||
alert("수정되었습니다.");
|
||||
setEditingCmntNo(null);
|
||||
setEditValue("");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: deleteComment } = useMutation({
|
||||
mutationFn: ({ cmntNo, cmntPswd }: { cmntNo: number; cmntPswd: string }) =>
|
||||
CommentApi.deleteComment(bbsId, pstNo, cmntNo, cmntPswd),
|
||||
onSuccess: () => {
|
||||
alert("삭제되었습니다.");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<CommentFormData>({
|
||||
resolver: zodResolver(commentFormSchema),
|
||||
values: {
|
||||
@@ -48,6 +84,35 @@ function PostComment({ pstNo }: { pstNo: number }) {
|
||||
createComment(data);
|
||||
};
|
||||
|
||||
const getCommentPassword = async () => {
|
||||
let cmntPswd = "";
|
||||
if (userInfo?.userUnqId) {
|
||||
cmntPswd = userInfo?.userUnqId;
|
||||
} else {
|
||||
cmntPswd = await prompt("비밀번호를 입력해주세요.");
|
||||
}
|
||||
return cmntPswd;
|
||||
};
|
||||
|
||||
const handleUpdateComment = async (cmntNo: number, wrtrNm: string) => {
|
||||
if (!(await confirm("수정하시겠습니까?"))) return;
|
||||
const cmntPswd = await getCommentPassword();
|
||||
updateComment({
|
||||
cmntNo,
|
||||
data: {
|
||||
cmntCn: editValue,
|
||||
wrtrNm,
|
||||
cmntPswd,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteComment = async (cmntNo: number) => {
|
||||
if (!(await confirm("삭제하시겠습니까?"))) return;
|
||||
const cmntPswd = await getCommentPassword();
|
||||
deleteComment({ cmntNo, cmntPswd });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card card-border !p-8">
|
||||
<Form {...form}>
|
||||
@@ -113,32 +178,77 @@ function PostComment({ pstNo }: { pstNo: number }) {
|
||||
</form>
|
||||
</Form>
|
||||
<div className="mt-8 flex flex-col">
|
||||
{comments?.content.map((comment) => (
|
||||
<div
|
||||
className="flex justify-between gap-2 border-t border-gray-200 py-8"
|
||||
key={comment.cmntNo}
|
||||
>
|
||||
<div className="flex gap-10">
|
||||
<div className="flex w-[10rem] shrink-0 flex-col">
|
||||
<span className="text-[1.4rem] font-bold">
|
||||
{comment.wrtrNm}
|
||||
</span>
|
||||
<span className="text-[1.2rem] text-gray-500">
|
||||
{dayjs(comment.regDt).format("YYYY.MM.DD")}
|
||||
</span>
|
||||
{comments?.content.map((comment) => {
|
||||
if (comment.delYn === "Y") return null;
|
||||
return (
|
||||
<div
|
||||
className="flex justify-between gap-2 border-t border-gray-200 py-8"
|
||||
key={comment.cmntNo}
|
||||
>
|
||||
<div className="flex w-full gap-10">
|
||||
<div className="flex w-[10rem] shrink-0 flex-col">
|
||||
<span className="text-[1.4rem] font-bold">
|
||||
{comment.wrtrNm}
|
||||
</span>
|
||||
<span className="text-[1.2rem] text-gray-500">
|
||||
{dayjs(comment.regDt).format("YYYY.MM.DD")}
|
||||
</span>
|
||||
</div>
|
||||
{editingCmntNo === comment.cmntNo ? (
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<Textarea
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
maxLength={1000}
|
||||
className="h-[10rem] bg-background p-4 !text-[1.3rem] !leading-6"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
handleUpdateComment(comment.cmntNo, comment.wrtrNm)
|
||||
}
|
||||
>
|
||||
저장
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
color="gray"
|
||||
onClick={() => {
|
||||
setEditingCmntNo(null);
|
||||
setEditValue("");
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="break-all text-[1.4rem]">{comment.cmntCn}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 whitespace-nowrap">
|
||||
<Button
|
||||
size="icon"
|
||||
color="white"
|
||||
onClick={() => {
|
||||
setEditingCmntNo(comment.cmntNo);
|
||||
setEditValue(comment.cmntCn);
|
||||
}}
|
||||
>
|
||||
<i className="diveicon di-pencil" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
color="white"
|
||||
onClick={() => handleDeleteComment(comment.cmntNo)}
|
||||
>
|
||||
<i className="diveicon di-trash" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="break-all text-[1.4rem]">{comment.cmntCn}</p>
|
||||
</div>
|
||||
<div className="flex gap-2 whitespace-nowrap">
|
||||
<Button size="icon" color="white">
|
||||
<i className="diveicon di-pencil" />
|
||||
</Button>
|
||||
<Button size="icon" color="white">
|
||||
<i className="diveicon di-trash" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -131,7 +131,7 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) {
|
||||
</div>
|
||||
</div>
|
||||
</PostAction>
|
||||
<PostComment pstNo={parseInt(pstNo)} />
|
||||
<PostComment bbsId={parseInt(bbsId)} pstNo={parseInt(pstNo)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user