import { useState, useCallback } from "react"; import { exportTaskPatch, pushTaskBranch, createTaskPR, type ExportPatchResponse } from "../../lib/api"; interface GitActionsPanelProps { taskId: string; isLocalOnly: boolean; taskStatus: string; } interface ToastMessage { type: "success" | "error" | "info"; message: string; } export function GitActionsPanel({ taskId, isLocalOnly, taskStatus, }: GitActionsPanelProps) { const [isExporting, setIsExporting] = useState(false); const [isPushing, setIsPushing] = useState(false); const [isCreatingPR, setIsCreatingPR] = useState(false); const [toast, setToast] = useState(null); const [exportedPatch, setExportedPatch] = useState(null); // Only show for completed tasks if (taskStatus !== "done") return null; const showToast = (type: ToastMessage["type"], message: string) => { setToast({ type, message }); setTimeout(() => setToast(null), 5000); }; const handleExportPatch = useCallback(async () => { setIsExporting(true); try { const result = await exportTaskPatch(taskId); setExportedPatch(result); showToast("success", `Patch generated: ${result.fileName}`); } catch (e) { showToast("error", e instanceof Error ? e.message : "Failed to generate patch"); } finally { setIsExporting(false); } }, [taskId]); const handlePushBranch = useCallback(async () => { if (isLocalOnly) { showToast("error", "Push disabled in local-only mode"); return; } setIsPushing(true); try { const result = await pushTaskBranch(taskId); showToast("success", `Branch pushed: ${result.branchName}`); } catch (e) { showToast("error", e instanceof Error ? e.message : "Failed to push branch"); } finally { setIsPushing(false); } }, [taskId, isLocalOnly]); const handleCreatePR = useCallback(async () => { if (isLocalOnly) { showToast("error", "PR creation disabled in local-only mode"); return; } setIsCreatingPR(true); try { const result = await createTaskPR(taskId); if (result.prUrl) { showToast("success", `PR created: ${result.prUrl}`); // Open PR in new tab window.open(result.prUrl, "_blank", "noopener,noreferrer"); } else { showToast("success", "PR creation initiated"); } } catch (e) { showToast("error", e instanceof Error ? e.message : "Failed to create PR"); } finally { setIsCreatingPR(false); } }, [taskId, isLocalOnly]); return (
{/* Section Header */}
Git Actions
{/* Local-only mode alert */} {isLocalOnly && (
Contract is in local-only mode. Push/PR actions disabled.
)} {/* Toast notification */} {toast && (
{toast.message}
)} {/* Action buttons */}
{/* Export result info */} {exportedPatch && (
Exported Patch
File: {exportedPatch.fileName}
{exportedPatch.filePath && (
Path: {exportedPatch.filePath}
)} {exportedPatch.patchSize && (
Size: {formatBytes(exportedPatch.patchSize)}
)}
)}
); } function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }