import { useState, useEffect } from "react"; import { CommentItem } from "./CommentItem"; import { Loader2, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; // Assuming this exists import { toast } from "sonner"; interface PRConversationProps { owner: string; repo: string; pullNumber: number; currentUser?: any; } export default function PRConversation({ owner, repo, pullNumber, currentUser }: PRConversationProps) { const [comments, setComments] = useState([]); const [loading, setLoading] = useState(true); const [newComment, setNewComment] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [templates, setTemplates] = useState([]); const [selectedTemplateId, setSelectedTemplateId] = useState(""); const [isReviewMode, setIsReviewMode] = useState(false); const [pendingComments, setPendingComments] = useState([]); const [reviewSummary, setReviewSummary] = useState(""); const [isReviewSubmitting, setIsReviewSubmitting] = useState(false); useEffect(() => { fetchComments(); }, [owner, repo, pullNumber]); useEffect(() => { if (!currentUser) return; fetchTemplates(); }, [owner, repo, currentUser]); const fetchComments = async () => { try { const res = await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/comments`); if (res.ok) { const data = await res.json(); setComments(data.comments || []); } } catch (e) { console.error(e); } finally { setLoading(false); } }; const fetchTemplates = async () => { try { const res = await fetch(`/api/repos/${owner}/${repo}/review-templates`); if (res.ok) { const data = await res.json(); const nextTemplates = data.templates || []; setTemplates(nextTemplates); const defaultTemplate = nextTemplates.find((t: any) => t.isDefault); if (defaultTemplate) { setSelectedTemplateId(defaultTemplate.id); setNewComment((prev) => (prev.trim() ? prev : defaultTemplate.content || "")); } } } catch (e) { console.error(e); } }; const handleSubmit = async () => { if (!newComment.trim()) return; if (isReviewMode) { setPendingComments((prev) => [newComment.trim(), ...prev]); setNewComment(""); toast.success("Added to review"); return; } setIsSubmitting(true); try { const res = await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/comments`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ body: newComment }) }); if (res.ok) { const data = await res.json(); setComments([data.comment, ...comments]); setNewComment(""); toast.success("Comment added"); } else { toast.error("Failed to add comment"); } } catch (e) { toast.error("Error adding comment"); } finally { setIsSubmitting(false); } }; const removePending = (index: number) => { setPendingComments((prev) => prev.filter((_, i) => i !== index)); }; const submitReview = async () => { if (pendingComments.length === 0) return; setIsReviewSubmitting(true); try { const reviewRes = await fetch(`/api/repos/${owner}/${repo}/pulls/${pullNumber}/reviews/batch`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ state: "COMMENTED", body: reviewSummary || "Review submitted", comments: pendingComments.map((body) => ({ body })), }) }); if (!reviewRes.ok) { let message = "Failed to submit review"; try { const payload = await reviewRes.json(); message = payload?.error?.message || payload?.message || message; } catch { // best effort parse only } throw new Error(message); } setPendingComments([]); setReviewSummary(""); setIsReviewMode(false); await fetchComments(); toast.success("Review submitted"); } catch (e: any) { toast.error(e.message || "Failed to submit review"); } finally { setIsReviewSubmitting(false); } }; const handleTemplateSelect = (templateId: string) => { setSelectedTemplateId(templateId); const template = templates.find((t: any) => t.id === templateId); if (template?.content) { setNewComment(template.content); } }; if (loading) { return
; } return (
{/* Comment Form */} {currentUser && (
{currentUser.avatarUrl ? ( {currentUser.username} ) : (
{currentUser.username?.charAt(0).toUpperCase()}
)}