import { useState, useEffect, useCallback } from "react"; import type { PatchSummary } from "../../lib/api"; import { listTaskPatches } from "../../lib/api"; interface PatchesListPanelProps { taskId: string; contractId: string; } /** Format a date for display */ function formatDate(dateStr: string): string { const date = new Date(dateStr); return date.toLocaleDateString("en-US", { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); } /** Copy text to clipboard and show feedback */ async function copyToClipboard(text: string): Promise { try { await navigator.clipboard.writeText(text); return true; } catch { // Fallback for older browsers const textArea = document.createElement("textarea"); textArea.value = text; textArea.style.position = "fixed"; textArea.style.left = "-999999px"; textArea.style.top = "-999999px"; document.body.appendChild(textArea); textArea.select(); try { document.execCommand("copy"); return true; } catch { return false; } finally { document.body.removeChild(textArea); } } } export function PatchesListPanel({ taskId, contractId }: PatchesListPanelProps) { const [patches, setPatches] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [copiedPatchId, setCopiedPatchId] = useState(null); const [expandedPatchId, setExpandedPatchId] = useState(null); const fetchPatches = useCallback(async () => { try { setLoading(true); setError(null); const patchList = await listTaskPatches(taskId, contractId); setPatches(patchList); } catch (e) { console.error("Failed to fetch patches:", e); setError(e instanceof Error ? e.message : "Failed to fetch patches"); } finally { setLoading(false); } }, [taskId, contractId]); useEffect(() => { fetchPatches(); }, [fetchPatches]); const handleCopyApplyCommand = useCallback(async (patch: PatchSummary) => { // Generate the apply command for this patch const command = `makima patch apply ${patch.id}`; const success = await copyToClipboard(command); if (success) { setCopiedPatchId(patch.id); setTimeout(() => setCopiedPatchId(null), 2000); } }, []); const handleViewPatch = useCallback((patchId: string) => { setExpandedPatchId(expandedPatchId === patchId ? null : patchId); }, [expandedPatchId]); if (loading) { return (
Exported Patches
Loading patches...
); } if (error) { return (
Exported Patches
{error}
); } if (patches.length === 0) { return (
Exported Patches
No patches exported yet
); } return (
{/* Header */}
Exported Patches
{patches.length} patch{patches.length !== 1 ? "es" : ""}
{/* Patch list */}
{patches.map((patch) => (
{/* Patch header */}
{patch.name}
{formatDate(patch.createdAt)} {patch.filesCount} file{patch.filesCount !== 1 ? "s" : ""} +{patch.linesAdded} -{patch.linesRemoved}
{/* Action buttons */}
{/* Expanded patch content */} {expandedPatchId === patch.id && patch.description && (
                  {patch.description}
                
)} {/* Expanded - show files if available */} {expandedPatchId === patch.id && patch.files && patch.files.length > 0 && (
Changed files:
{patch.files.map((file, idx) => (
{file}
))}
)}
))}
); }