summaryrefslogtreecommitdiff
path: root/components/qna/comment-section.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
commit2acf5f8966a40c1c9a97680c8dc263ee3f1ad3d1 (patch)
treef406b5c86f563347c7fd088a85fd1a82284dc5ff /components/qna/comment-section.tsx
parent6a9ca20deddcdcbe8495cf5a73ec7ea5f53f9b55 (diff)
(대표님/최겸) 20250702 변경사항 업데이트
Diffstat (limited to 'components/qna/comment-section.tsx')
-rw-r--r--components/qna/comment-section.tsx166
1 files changed, 166 insertions, 0 deletions
diff --git a/components/qna/comment-section.tsx b/components/qna/comment-section.tsx
new file mode 100644
index 00000000..2ea358e2
--- /dev/null
+++ b/components/qna/comment-section.tsx
@@ -0,0 +1,166 @@
+import * as React from "react";
+import { useSession } from "next-auth/react";
+import { Button } from "@/components/ui/button";
+import { Textarea } from "@/components/ui/textarea";
+import { Badge } from "@/components/ui/badge";
+import { format } from "date-fns";
+import { Comment } from "@/lib/qna/types";
+import { Trash2, Pencil, Check, X } from "lucide-react";
+
+interface CommentSectionProps {
+ answerId: string;
+ comments: Comment[];
+ onAddComment: (content: string) => Promise<void>;
+ onDeleteComment: (commentId: string) => Promise<void>;
+ onUpdateComment?: (commentId: string, content: string) => Promise<void>;
+}
+
+export function CommentSection({ answerId, comments, onAddComment, onDeleteComment, onUpdateComment }: CommentSectionProps) {
+ const { data: session } = useSession();
+ const [content, setContent] = React.useState("");
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
+ const [editingId, setEditingId] = React.useState<string | null>(null);
+ const [editContent, setEditContent] = React.useState("");
+
+ const handleSubmit = async () => {
+ if (!content.trim() || !session?.user?.name) return;
+ setIsSubmitting(true);
+ try {
+ await onAddComment(content);
+ setContent("");
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ const handleEditStart = (comment: Comment) => {
+ setEditingId(comment.id);
+ setEditContent(comment.content);
+ };
+
+ const handleEditCancel = () => {
+ setEditingId(null);
+ setEditContent("");
+ };
+
+ const handleEditSave = async (commentId: string) => {
+ if (!editContent.trim() || !onUpdateComment) return;
+ try {
+ await onUpdateComment(commentId, editContent);
+ setEditingId(null);
+ } catch (error) {
+ console.error("댓글 수정 실패:", error);
+ }
+ };
+
+ return (
+ <div className="space-y-4 mt-4">
+ <div className="flex items-center gap-2">
+ <h3 className="text-sm font-medium">댓글</h3>
+ {comments.length > 0 && (
+ <Badge variant="secondary" className="text-xs">
+ {comments.length}
+ </Badge>
+ )}
+ </div>
+
+ {/* 댓글 목록 */}
+ <div className="space-y-3">
+ {comments.map((comment) => (
+ <div key={comment.id} className="flex items-start justify-between text-sm bg-muted/50 rounded-md p-2">
+ <div className="flex-1 space-y-1">
+ <div className="flex items-center gap-2">
+ <span className="font-medium">{comment.author}</span>
+ <span className="text-xs text-muted-foreground">
+ {format(new Date(comment.createdAt), "yyyy.MM.dd HH:mm")}
+ </span>
+ </div>
+ {editingId === comment.id ? (
+ <Textarea
+ value={editContent}
+ onChange={(e) => setEditContent(e.target.value)}
+ className="min-h-[60px] text-sm"
+ maxLength={250}
+ />
+ ) : (
+ <p className="text-sm">{comment.content}</p>
+ )}
+ </div>
+ {session?.user?.name === comment.author && (
+ <div className="flex gap-2">
+ {editingId === comment.id ? (
+ <>
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-6 w-6 text-green-600 hover:text-green-700"
+ onClick={() => handleEditSave(comment.id)}
+ >
+ <Check className="h-4 w-4" />
+ </Button>
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-6 w-6 text-muted-foreground"
+ onClick={handleEditCancel}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </>
+ ) : (
+ <>
+ <Button
+ variant="ghost"
+ size="icon"
+ onClick={() => handleEditStart(comment)}
+ className="h-6 w-6 text-muted-foreground hover:text-blue-500 transition-colors"
+ >
+ <Pencil className="h-3.5 w-3.5" />
+ </Button>
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-6 w-6 text-muted-foreground hover:text-destructive transition-colors"
+ onClick={() => onDeleteComment(comment.id)}
+ >
+ <Trash2 className="h-3.5 w-3.5" />
+ </Button>
+ </>
+ )}
+ </div>
+ )}
+ </div>
+ ))}
+ </div>
+
+ {/* 댓글 입력 */}
+ {session?.user && (
+ <div className="flex gap-2">
+ <Textarea
+ value={content}
+ onChange={(e) => {
+ if (e.target.value.length <= 250) {
+ setContent(e.target.value);
+ }
+ }}
+ placeholder="댓글을 입력하세요 (250자 이내)"
+ className="min-h-[80px] flex-1"
+ maxLength={250}
+ />
+ <Button
+ onClick={handleSubmit}
+ disabled={isSubmitting || !content.trim()}
+ className="self-start"
+ >
+ {isSubmitting ? "저장 중..." : "등록"}
+ </Button>
+ </div>
+ )}
+
+ {/* 글자 수 표시 */}
+ <div className="text-xs text-muted-foreground text-right">
+ {content.length}/250
+ </div>
+ </div>
+ );
+} \ No newline at end of file