'use client'; import { useEffect, useState, useContext } from 'react'; import { Button, Textarea, Spinner, addToast } from '@heroui/react'; import CommentItem from './CommentItem'; import { TokenContext } from '@/utils/TokenProvider'; import { fetchComments, createComment, updateComment, deleteComment } from '@/utils/commentControl'; import { logError } from '@/utils/errorHandler'; import type { CommentMessages, CommentType } from '@/types/comment'; type Props = { projectId: string; commentableType: 'RunCase' | 'Run' | 'Case'; commentableId?: number; messages: CommentMessages; }; export default function Comments({ projectId, commentableType, commentableId, messages }: Props) { const context = useContext(TokenContext); const [comments, setComments] = useState([]); const [isLoading, setIsLoading] = useState(false); const [newComment, setNewComment] = useState(''); const [editingId, setEditingId] = useState(null); const [editContent, setEditContent] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { async function loadComments() { if (!commentableType || !commentableId || !context.isSignedIn()) return; setIsLoading(true); try { const data = await fetchComments(context.token.access_token, commentableType, commentableId); setComments(data); } catch (error: unknown) { logError('Error fetching comments', error); } finally { setIsLoading(false); } } loadComments(); }, [commentableType, commentableId, context]); const handleAddComment = async () => { if (!newComment.trim() || !commentableType || !commentableId) return; setIsSubmitting(true); try { const comment = await createComment(context.token.access_token, commentableType, commentableId, newComment); if (!comment) { throw new Error('Failed to create comment'); } const updatedComments = [...comments, comment]; setComments(updatedComments); setNewComment(''); addToast({ title: 'Success', color: 'success', description: messages.commentAdded, }); } catch (error: unknown) { logError('Error adding comment', error); addToast({ title: 'Error', color: 'danger', description: messages.failedToAddComment, }); } finally { setIsSubmitting(false); } }; const handleStartEdit = (id: number, content: string) => { setEditingId(id); setEditContent(content); }; const handleCancelEdit = () => { setEditingId(null); setEditContent(''); }; const handleSaveEdit = async (id: number) => { if (!editContent.trim()) return; setIsSubmitting(true); try { const updated = await updateComment(context.token.access_token, id, editContent); if (!updated) { throw new Error('Failed to update comment'); } setComments(comments.map((c) => (c.id === id ? { ...c, content: editContent } : c))); setEditingId(null); setEditContent(''); addToast({ title: 'Success', color: 'success', description: messages.commentUpdated, }); } catch (error: unknown) { logError('Error updating comment', error); addToast({ title: 'Error', color: 'danger', description: messages.failedToUpdateComment, }); } finally { setIsSubmitting(false); } }; const handleDeleteComment = async (id: number) => { setIsSubmitting(true); try { await deleteComment(context.token.access_token, id); const updatedComments = comments.filter((c) => c.id !== id); setComments(updatedComments); addToast({ title: 'Success', color: 'success', description: messages.commentDeleted, }); } catch (error: unknown) { logError('Error deleting comment', error); addToast({ title: 'Error', color: 'danger', description: messages.failedToDeleteComment, }); } finally { setIsSubmitting(false); } }; if (!commentableType || !commentableId) { return (
{commentableType === 'RunCase' && !commentableId ?

{messages.notIncludedInRun}

:

Unknown state

}
); } if (isLoading) { return (
); } const canComment = projectId && context.isProjectReporter(Number(projectId)); return (
{comments.length === 0 ? (

{messages.noComments}

) : (
{comments.map((comment) => ( handleStartEdit(comment.id, comment.content)} onCancelEdit={handleCancelEdit} onSave={() => handleSaveEdit(comment.id)} onDelete={() => handleDeleteComment(comment.id)} /> ))}
)}