testmog / src /components /AutomationFeed.tsx
wuhp's picture
Upload 5 files
eca38c2 verified
import React, { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { Terminal, Activity, CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react';
interface GhostLog {
id: string;
timestamp: number;
type: string;
payload: any;
}
export default function AutomationFeed() {
const [logs, setLogs] = useState<GhostLog[]>([]);
const [status, setStatus] = useState<any>({ status: 'idle', message: 'Waiting for feed...' });
const [isLoading, setIsLoading] = useState(false);
const fetchLogs = async () => {
try {
const [logsRes, statusRes] = await Promise.all([
fetch('/api/logs'),
fetch('/api/optimization-status')
]);
const logsData = await logsRes.json();
const statusData = await statusRes.json();
setLogs(logsData);
setStatus(statusData);
} catch (err) {
console.error("Failed to fetch logs:", err);
}
};
useEffect(() => {
const interval = setInterval(fetchLogs, 3000);
return () => clearInterval(interval);
}, []);
const triggerOptimization = () => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
};
const clearLogs = async () => {
try {
await fetch('/api/webhook', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'sys_purge', payload: {} })
});
setLogs([]);
} catch(e) {}
};
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between border-b border-white/5 pb-2">
<div className="flex items-center gap-2">
<Terminal className="w-3 h-3 text-aura-500" />
<span className="text-[10px] font-mono text-white/40 uppercase tracking-widest">Automation Bridge</span>
</div>
<button
onClick={clearLogs}
className="text-[8px] font-mono text-white/20 hover:text-white transition-colors"
>
CLEAR_CACHE
</button>
</div>
{/* Optimization Header */}
<div className="p-3 bg-white/5 border border-white/10 rounded group">
<div className="flex justify-between items-start mb-2">
<p className="text-[9px] font-mono text-white/40 uppercase">Global Pipeline</p>
{status.status === 'perfect' ? (
<CheckCircle2 className="w-3 h-3 text-aura-500" />
) : (
<Activity className="w-3 h-3 text-aura-500 animate-[spin_3s_linear_infinite]" />
)}
</div>
<p className="text-xs font-serif italic text-white mb-2">{status.message}</p>
<div className="flex items-center gap-2">
<div className="flex-1 h-1 bg-white/10 rounded-full overflow-hidden">
<motion.div
initial={false}
animate={{ width: status.status === 'perfect' ? '100%' : '65%' }}
className="h-full bg-aura-500"
/>
</div>
<span className="text-[9px] font-mono text-white/40">
{status.status === 'perfect' ? '10/10' : (status.lastScore ? `${status.lastScore}/10` : 'INIT')}
</span>
</div>
<button
disabled={isLoading}
onClick={triggerOptimization}
className="w-full mt-3 py-1.5 bg-aura-500/10 border border-aura-500/20 text-aura-500 text-[9px] font-mono uppercase tracking-widest hover:bg-aura-500/20 transition-colors flex items-center justify-center gap-2"
>
{isLoading ? (
<RefreshCw className="w-2.5 h-2.5 animate-spin" />
) : (
'Recalibrate Parameters'
)}
</button>
</div>
{/* Real-time Feeds */}
<div className="flex-1 overflow-hidden flex flex-col gap-2">
<p className="text-[9px] font-mono text-white/20 uppercase tracking-tighter">Handshake Terminal</p>
<div className="space-y-1.5 max-h-[140px] overflow-y-auto pr-2 custom-scrollbar">
<AnimatePresence initial={false}>
{logs.length > 0 ? logs.map((log) => (
<motion.div
key={log.id}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="flex gap-2 text-[9px] font-mono border-l border-aura-500/30 pl-2 py-1 bg-white/[0.02] hover:bg-white/[0.05] transition-colors"
>
<span className="text-white/20 whitespace-nowrap">
{new Date(log.timestamp).toLocaleTimeString().split(' ')[0]}
</span>
<span className="text-aura-500">[{log.type.toUpperCase()}]</span>
<span className="text-white/60 truncate">
{log.type === 'report' ? `REPORT_RCVD SC:${log.payload?.statsJson?.overall}` : 'SYNC_SUCCESS'}
</span>
</motion.div>
)) : (
<div className="text-[9px] font-mono text-white/10 italic">Waiting for Tampermonkey exfiltration...</div>
)}
</AnimatePresence>
</div>
</div>
<div className="text-[8px] font-mono text-white/20 leading-tight border-t border-white/5 pt-2">
* Ensure Borepub URL in script points to your development app address + /api/webhook
</div>
</div>
);
}