260 lines
8.5 KiB
TypeScript
260 lines
8.5 KiB
TypeScript
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 { ENDPOINT } from "@/shared/config";
|
|
import { useAlertStore, usePrompt, useUserStore } from "@/shared/lib";
|
|
import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form";
|
|
|
|
function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) {
|
|
const { prompt } = usePrompt();
|
|
const { confirm } = useConfirm();
|
|
const queryClient = useQueryClient();
|
|
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],
|
|
queryFn: () => CommentApi.getComments({ pstNo }),
|
|
enabled: !!pstNo,
|
|
});
|
|
|
|
const { mutate: createComment } = useMutation({
|
|
mutationFn: (data: CommentFormData) => CommentApi.createComment(data),
|
|
onSuccess: () => {
|
|
alert("등록되었습니다.");
|
|
form.setValue("cmntCn", "");
|
|
queryClient.invalidateQueries({
|
|
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS],
|
|
});
|
|
},
|
|
});
|
|
|
|
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: {
|
|
pstNo,
|
|
cmntCn: "",
|
|
wrtrNm: userInfo?.userName ?? "",
|
|
cmntPswd: userInfo?.userUnqId ?? "",
|
|
},
|
|
});
|
|
|
|
const onSubmit = (data: CommentFormData) => {
|
|
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}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
<FormField
|
|
control={form.control}
|
|
name="cmntCn"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Textarea
|
|
maxLength={1000}
|
|
className="h-[10rem] bg-background p-4 !text-[1.3rem] !leading-6 text-foreground"
|
|
placeholder="댓글을 입력해주세요."
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<div className="mt-4 flex items-center justify-between">
|
|
<div className="flex gap-4">
|
|
{!userInfo?.userName && (
|
|
<FormField
|
|
control={form.control}
|
|
name="wrtrNm"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
value={field.value ?? ""}
|
|
className="w-[16rem] bg-background !text-[1.3rem] text-foreground"
|
|
placeholder="작성자"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
)}
|
|
{!userInfo?.userUnqId && (
|
|
<FormField
|
|
control={form.control}
|
|
name="cmntPswd"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
type="password"
|
|
value={field.value ?? ""}
|
|
className="w-[12rem] bg-background !text-[1.3rem] text-foreground"
|
|
placeholder="비밀번호"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
)}
|
|
</div>
|
|
<Button type="submit">등록</Button>
|
|
</div>
|
|
</form>
|
|
</Form>
|
|
<div className="mt-8 flex flex-col">
|
|
{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 text-label">
|
|
{comment.wrtrNm}
|
|
</span>
|
|
<span className="text-[1.2rem] text-label">
|
|
{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 text-foreground"
|
|
/>
|
|
<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] text-foreground">
|
|
{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>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export { PostComment };
|