| 'use client'; |
| import React, { useState, useEffect } from 'react'; |
| import { api } from '../../services/api'; |
|
|
| export default function AgentMonitor() { |
| const [activeBuilds, setActiveBuilds] = useState([]); |
| const [loading, setLoading] = useState(false); |
|
|
| const triggerNewBuild = async (target) => { |
| setLoading(true); |
| try { |
| const res = await api.startBuild(target); |
| if (res && res.build_id) { |
| setActiveBuilds((prev) => [ |
| { build_id: res.build_id, target, status: 'building', timestamp: new Date().toLocaleTimeString() }, |
| ...prev |
| ]); |
| pollStatus(res.build_id); |
| } |
| } catch (err) { |
| console.error('Failed to trigger build:', err); |
| } finally { |
| setLoading(false); |
| } |
| }; |
|
|
| const pollStatus = (buildId) => { |
| const interval = setInterval(async () => { |
| try { |
| const res = await api.getBuildStatus(buildId); |
| if (res && res.status) { |
| setActiveBuilds((prev) => |
| prev.map((item) => (item.build_id === buildId ? { ...item, status: res.status, error: res.error } : item)) |
| ); |
| if (res.status === 'success' || res.status === 'failed') { |
| clearInterval(interval); |
| } |
| } |
| } catch (err) { |
| clearInterval(interval); |
| } |
| }, 2000); |
| }; |
|
|
| return ( |
| <div className="p-6 bg-slate-950 text-slate-200 h-full overflow-y-auto space-y-6"> |
| <div className="border-b border-slate-800 pb-4"> |
| <h2 className="text-lg font-bold text-white">Agent Task Engine & Build Operations</h2> |
| <p className="text-xs text-slate-400 mt-1"> |
| Trigger production build processes and monitor autonomous agent DAG executions. |
| </p> |
| </div> |
| |
| {/* Action Controls */} |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| <button |
| onClick={() => triggerNewBuild('static')} |
| disabled={loading} |
| className="p-4 bg-slate-900 border border-slate-800 hover:border-emerald-500/50 rounded-lg text-left transition-all group" |
| > |
| <div className="text-emerald-400 font-bold text-sm group-hover:translate-x-1 transition-transform"> |
| π Build Web Static Bundle β |
| </div> |
| <div className="text-xs text-slate-400 mt-1">Triggers automated Vite/React/Next site production build.</div> |
| </button> |
| |
| <button |
| onClick={() => triggerNewBuild('docker')} |
| disabled={loading} |
| className="p-4 bg-slate-900 border border-slate-800 hover:border-blue-500/50 rounded-lg text-left transition-all group" |
| > |
| <div className="text-blue-400 font-bold text-sm group-hover:translate-x-1 transition-transform"> |
| π³ Build Docker Container β |
| </div> |
| <div className="text-xs text-slate-400 mt-1">Compiles application image via host Docker engine.</div> |
| </button> |
| |
| <button |
| onClick={() => triggerNewBuild('apk')} |
| disabled={loading} |
| className="p-4 bg-slate-900 border border-slate-800 hover:border-purple-500/50 rounded-lg text-left transition-all group" |
| > |
| <div className="text-purple-400 font-bold text-sm group-hover:translate-x-1 transition-transform"> |
| π± Package Android APK β |
| </div> |
| <div className="text-xs text-slate-400 mt-1">Compiles mobile bundle into installable APK binary.</div> |
| </button> |
| </div> |
| |
| {/* Execution Queue Table */} |
| <div className="bg-slate-900 border border-slate-800 rounded-lg overflow-hidden"> |
| <div className="px-4 py-3 border-b border-slate-800 font-semibold text-xs text-slate-400 uppercase tracking-wider"> |
| Active Build & Execution Pipeline Jobs |
| </div> |
| <div className="divide-y divide-slate-800/60 text-xs"> |
| {activeBuilds.length === 0 ? ( |
| <div className="p-4 text-slate-500 italic text-center">No build jobs initiated in this session.</div> |
| ) : ( |
| activeBuilds.map((job) => ( |
| <div key={job.build_id} className="p-4 flex items-center justify-between"> |
| <div> |
| <div className="font-mono text-slate-200 font-medium">{job.build_id}</div> |
| <div className="text-[11px] text-slate-500">Target: {job.target} | Queued: {job.timestamp}</div> |
| {job.error && <div className="text-rose-400 text-[11px] mt-1">{job.error}</div>} |
| </div> |
| <div> |
| <span |
| className={`px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider ${ |
| job.status === 'success' |
| ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' |
| : job.status === 'failed' |
| ? 'bg-rose-500/10 text-rose-400 border border-rose-500/20' |
| : 'bg-amber-500/10 text-amber-400 border border-amber-500/20 animate-pulse' |
| }`} |
| > |
| {job.status} |
| </span> |
| </div> |
| </div> |
| )) |
| )} |
| </div> |
| </div> |
| </div> |
| ); |
| } |
|
|