feat: 게시글 평가 댓글 기능 추가

This commit is contained in:
2025-06-27 18:23:38 +09:00
parent 2b9028492b
commit b8190c3550
3 changed files with 127 additions and 65 deletions
@@ -26,7 +26,7 @@ export const commentFormSchema = z.object({
cmntCn: z.string(),
wrtrNm: z.string().nullish(),
cmntPswd: z.string(),
cmntEvl: z.number().nullish(),
cmntEvl: z.string().nullish(),
});
export const commentUpdateFormSchema = commentFormSchema.omit({
+5 -1
View File
@@ -311,7 +311,11 @@ function BbsForm({ form, bbsId, ...props }: BbsFormProps) {
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="cmntUseY" />
<Label htmlFor="cmntUseY">사용</Label>
<Label htmlFor="cmntUseY">일반댓글</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="C" id="cmntUseC" />
<Label htmlFor="cmntUseC">평가댓글</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="cmntUseN" />
+121 -63
View File
@@ -4,7 +4,14 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Button, Input, Textarea } from "@common/components";
import {
Button,
Input,
Label,
RadioGroup,
RadioGroupItem,
Textarea,
} from "@common/components";
import {
CommentApi,
commentFormSchema,
@@ -14,6 +21,7 @@ import {
import { ENDPOINT } from "@/shared/config";
import {
useAlertStore,
useMenuStore,
useMutationWrapper,
usePrompt,
useUserStore,
@@ -24,10 +32,11 @@ function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) {
const { prompt } = usePrompt();
const { confirm } = useConfirm();
const queryClient = useQueryClient();
const bbsInfo = useMenuStore((state) => state.bbsInfo);
const userInfo = useUserStore((state) => state.userInfo);
const alert = useAlertStore((state) => state.setMessage);
const [editValue, setEditValue] = useState("");
const [editingCmntNo, setEditingCmntNo] = useState<number | null>(null);
const [editValue, setEditValue] = useState<string>("");
const { data: comments } = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_COMMENTS, pstNo],
@@ -123,6 +132,46 @@ function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) {
<div className="card card-border !p-8">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{bbsInfo?.cmntUseYn === "C" && (
<FormField
control={form.control}
name="cmntEvl"
render={({ field }) => (
<FormItem className="mb-4">
<FormControl>
<RadioGroup
name="cmntEvl"
value={field.value?.toString() ?? ""}
onValueChange={field.onChange}
>
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<RadioGroupItem value="5" id="cmntEvl5" />
<Label htmlFor="cmntEvl5">매우만족 ★★★★★</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="4" id="cmntEvl4" />
<Label htmlFor="cmntEvl4">만족 ★★★★</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="3" id="cmntEvl3" />
<Label htmlFor="cmntEvl3">보통 ★★★</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="2" id="cmntEvl2" />
<Label htmlFor="cmntEvl2">불만족 ★★</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="1" id="cmntEvl1" />
<Label htmlFor="cmntEvl1">매우불만족 ★</Label>
</div>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="cmntCn"
@@ -188,71 +237,80 @@ function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) {
if (comment.delYn === "Y") return null;
return (
<div
className="flex justify-between gap-2 border-t border-gray-200 py-8"
key={comment.cmntNo}
className="flex flex-col border-t border-gray-200 py-8"
>
<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"
/>
<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>
{Array.from({ length: Number(comment.cmntEvl) }).map(
(_, index) => (
<span key={index}>★</span>
),
)}
</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 className="flex justify-between">
<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"
/>
<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>
);