import React, { useState } from 'react'; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, } from "@/components/ui/dialog"; import { AlertCircle, Check, FileCode, Loader2, Wrench } from 'lucide-react'; import { toast } from 'sonner'; interface ConflictFile { path: string; content: string; } interface ConflictResolverProps { owner: string; repo: string; queueItemId: string; triggerButton?: React.ReactNode; variant?: "default" | "icon"; } export function ConflictResolver({ owner, repo, queueItemId, triggerButton, variant = "default" }: ConflictResolverProps) { const [isOpen, setIsOpen] = useState(false); const [loading, setLoading] = useState(false); const [conflicts, setConflicts] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [resolutions, setResolutions] = useState<{ path: string, content: string }[]>([]); const [currentContent, setCurrentContent] = useState(""); const fetchConflicts = async () => { setLoading(true); try { const res = await fetch(`/api/repos/${owner}/${repo}/queue/${queueItemId}/conflicts`); if (!res.ok) throw new Error("Failed to check conflicts"); const data = await res.json(); if (data.error) throw new Error(data.error); setConflicts(data); if (data.length > 0) { setCurrentContent(data[0].content); } else { toast.success("No conflicts detected via check."); setIsOpen(false); } } catch (e: any) { toast.error(e.message); setIsOpen(false); } finally { setLoading(false); } }; const handleOpenChange = (open: boolean) => { setIsOpen(open); if (open) { setConflicts([]); setResolutions([]); setCurrentIndex(0); fetchConflicts(); } }; const handleNext = () => { // Save current resolution const currentFile = conflicts[currentIndex]; const newResolutions = [...resolutions]; const existingIndex = newResolutions.findIndex(r => r.path === currentFile.path); if (existingIndex >= 0) { newResolutions[existingIndex] = { path: currentFile.path, content: currentContent }; } else { newResolutions.push({ path: currentFile.path, content: currentContent }); } setResolutions(newResolutions); if (currentIndex < conflicts.length - 1) { setCurrentIndex(currentIndex + 1); setCurrentContent(conflicts[currentIndex + 1].content); } else { // All done, submit submitResolutions(newResolutions); } }; const submitResolutions = async (finalResolutions: { path: string, content: string }[]) => { setLoading(true); try { const res = await fetch(`/api/repos/${owner}/${repo}/queue/${queueItemId}/conflicts`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ resolutions: finalResolutions }) }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Failed to submit resolutions"); } toast.success("Conflicts resolved and merge committed!"); setIsOpen(false); window.location.reload(); // Refresh to see updated queue status } catch (e: any) { toast.error(e.message); } finally { setLoading(false); } }; const currentFile = conflicts[currentIndex]; return ( {triggerButton ? triggerButton : variant === "icon" ? ( ) : ( )} Resolve Conflicts Fix merge conflicts manually. {conflicts.length > 0 && `File ${currentIndex + 1} of ${conflicts.length}`}
{loading && conflicts.length === 0 ? (
Checking for conflicts...
) : conflicts.length > 0 && currentFile ? ( <>
{currentFile.path}