testarcbuilder / components /FixerModal.tsx
wuhp's picture
Update components/FixerModal.tsx
aecda56 verified
import React, { useState, useEffect } from 'react';
import { Wrench, X, Bug, HardHat, CheckCircle2, Play, AlertTriangle } from 'lucide-react';
import { fixArchitectureErrors, AgentStatus } from '../services/geminiService';
import { Node, Edge } from 'reactflow';
import { NodeData } from '../types';
interface FixerModalProps {
isOpen: boolean;
onClose: () => void;
onApply: (nodes: any[], edges: any[], logMsg?: string) => void;
errorMsg: string;
nodes: Node<NodeData>[];
edges: Edge[];
}
const FixerModal: React.FC<FixerModalProps> = ({ isOpen, onClose, onApply, errorMsg, nodes, edges }) => {
const [isFixing, setIsFixing] = useState(false);
const [agentStatus, setAgentStatus] = useState<AgentStatus>('idle');
const [agentMessage, setAgentMessage] = useState('');
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (isOpen) {
setAgentStatus('idle');
setAgentMessage('');
setError(null);
setIsFixing(false);
}
}, [isOpen]);
const handleStartFix = async () => {
setIsFixing(true);
setAgentStatus('debugger');
setAgentMessage('Initializing debugging protocols...');
try {
const result = await fixArchitectureErrors(nodes, edges, errorMsg, (status, msg) => {
setAgentStatus(status);
setAgentMessage(msg);
});
if (result && result.nodes && result.edges) {
// Post-process to ensure compatibility and safe access
const processedNodes = result.nodes.map((n: any) => {
const data = n.data || {};
const type = data.type || n.type || 'Identity'; // Fallback
return {
...n,
type: 'custom',
data: {
...data,
type: type,
label: data.label || n.label || type, // Safe access
params: data.params || {}
}
};
});
const processedEdges = result.edges.map((e: any) => ({
...e, animated: true, style: { stroke: '#94a3b8' }
}));
setTimeout(() => {
onApply(processedNodes, processedEdges, "Auto-Fixer agents applied corrections.");
onClose();
}, 1000);
}
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to fix architecture");
setAgentStatus('error');
setIsFixing(false);
}
};
const renderAgentStep = (step: AgentStatus, icon: React.ReactNode, label: string) => {
const order = ['idle', 'debugger', 'architect', 'patcher', 'complete'];
const stepIdx = order.indexOf(step);
const currentIdx = order.indexOf(agentStatus);
const isCurrent = agentStatus === step;
const isDone = currentIdx > stepIdx && agentStatus !== 'error';
const isPending = currentIdx < stepIdx;
let statusColor = 'text-slate-600';
if (isDone) statusColor = 'text-emerald-400';
if (isCurrent) statusColor = 'text-amber-400 animate-pulse';
if (agentStatus === 'error') statusColor = 'text-red-400';
return (
<div className={`flex items-center gap-3 p-3 rounded-lg border transition-all duration-300 ${isCurrent ? 'bg-slate-800 border-amber-500/30' : 'bg-slate-900 border-slate-800'}`}>
<div className={`${statusColor}`}>
{isDone ? <CheckCircle2 size={24} /> : icon}
</div>
<div className="flex-1">
<div className={`font-semibold text-sm ${isCurrent ? 'text-amber-200' : isDone ? 'text-emerald-200' : 'text-slate-400'}`}>
{label}
</div>
{isCurrent && (
<div className="text-xs text-slate-500 mt-1">{agentMessage}</div>
)}
</div>
{isCurrent && <div className="w-2 h-2 rounded-full bg-amber-400 animate-ping" />}
</div>
);
}
if (!isOpen) return null;
return (
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="bg-slate-950 w-full max-w-lg rounded-xl border border-red-500/30 shadow-2xl flex flex-col overflow-hidden animate-in fade-in zoom-in duration-200">
<div className="flex items-center justify-between px-6 py-4 border-b border-slate-800 bg-gradient-to-r from-slate-900 to-slate-800">
<h2 className="text-lg font-bold text-white flex items-center gap-2">
<Wrench className="text-red-400" size={20} />
Auto-Fixer
</h2>
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors">
<X size={20} />
</button>
</div>
<div className="p-6 space-y-6">
{!isFixing && agentStatus !== 'complete' ? (
<>
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-4 flex gap-3 items-start">
<AlertTriangle className="text-red-400 shrink-0 mt-0.5" size={18} />
<div>
<h3 className="text-sm font-bold text-red-200 mb-1">Detected Issues</h3>
<p className="text-xs text-red-300/80 font-mono leading-relaxed max-h-32 overflow-y-auto">
{errorMsg}
</p>
</div>
</div>
<div className="text-sm text-slate-400">
Deploy AI debugging agents to analyze the graph structure and apply corrections automatically.
</div>
<div className="flex justify-end pt-2">
<button
onClick={handleStartFix}
className="flex items-center gap-2 px-6 py-2.5 rounded-lg font-medium text-white bg-red-600 hover:bg-red-500 transition-all shadow-lg shadow-red-900/20"
>
<Play size={18} fill="currentColor" />
Run Diagnostics & Fix
</button>
</div>
</>
) : (
<div className="space-y-3">
{renderAgentStep('debugger', <Bug size={24} />, "Debugger: Identifying Root Cause")}
{renderAgentStep('architect', <HardHat size={24} />, "Architect: Planning Structural Fix")}
{renderAgentStep('patcher', <Wrench size={24} />, "Patcher: Applying Fixes to Graph")}
{agentStatus === 'complete' && (
<div className="text-center text-emerald-400 font-bold mt-6 animate-pulse">
Fixes Applied Successfully!
</div>
)}
{agentStatus === 'error' && (
<div className="text-center text-red-400 font-bold mt-6">
{error || "An error occurred during fixing."}
</div>
)}
</div>
)}
</div>
</div>
</div>
);
};
export default FixerModal;