Files
g3/crates/g3-console/web/src/pages/Detail.jsx
2025-11-07 09:27:13 +11:00

168 lines
5.7 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import StatusBadge from '../components/StatusBadge'
import ChatView from '../components/ChatView'
import GitStatus from '../components/GitStatus'
import ProgressBar from '../components/ProgressBar'
function Detail() {
const { id } = useParams()
const navigate = useNavigate()
const [instance, setInstance] = useState(null)
const [logs, setLogs] = useState({ messages: [], tool_calls: [] })
const [loading, setLoading] = useState(true)
const fetchInstance = async () => {
try {
const response = await fetch(`/api/instances/${id}`)
if (response.ok) {
const data = await response.json()
setInstance(data)
}
} catch (error) {
console.error('Failed to fetch instance:', error)
}
}
const fetchLogs = async () => {
try {
const response = await fetch(`/api/instances/${id}/logs`)
if (response.ok) {
const data = await response.json()
setLogs(data)
}
} catch (error) {
console.error('Failed to fetch logs:', error)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchInstance()
fetchLogs()
const interval = setInterval(() => {
fetchInstance()
fetchLogs()
}, 5000)
return () => clearInterval(interval)
}, [id])
if (loading || !instance) {
return (
<div className="flex justify-center items-center h-64">
<div className="text-gray-600 dark:text-gray-400">Loading instance details...</div>
</div>
)
}
return (
<div>
<button
onClick={() => navigate('/')}
className="mb-4 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
>
Back to instances
</button>
{/* Summary Section */}
<div className="hero-card p-6 mb-6">
<div className="flex justify-between items-start mb-4">
<div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
Instance {instance.instance.id}
</h2>
<div className="flex items-center gap-2">
<StatusBadge status={instance.instance.status} />
<span className="text-sm text-gray-600 dark:text-gray-400">
{instance.instance.instance_type === 'ensemble' ? 'Coach + Player' : 'Single Agent'}
</span>
</div>
</div>
</div>
<ProgressBar
instanceType={instance.instance.instance_type}
durationSecs={instance.stats.duration_secs}
/>
<div className="grid grid-cols-3 gap-4 mt-4">
<div>
<div className="text-sm text-gray-600 dark:text-gray-400">Tokens</div>
<div className="text-2xl font-bold text-gray-900 dark:text-white">
{instance.stats.total_tokens.toLocaleString()}
</div>
</div>
<div>
<div className="text-sm text-gray-600 dark:text-gray-400">Tool Calls</div>
<div className="text-2xl font-bold text-gray-900 dark:text-white">
{instance.stats.tool_calls}
</div>
</div>
<div>
<div className="text-sm text-gray-600 dark:text-gray-400">Errors</div>
<div className="text-2xl font-bold text-gray-900 dark:text-white">
{instance.stats.errors}
</div>
</div>
</div>
<div className="mt-4 text-sm text-gray-600 dark:text-gray-400">
<div><strong>Workspace:</strong> {instance.instance.workspace}</div>
<div><strong>Provider:</strong> {instance.instance.provider || 'N/A'}</div>
<div><strong>Model:</strong> {instance.instance.model || 'N/A'}</div>
<div><strong>Started:</strong> {new Date(instance.instance.start_time).toLocaleString()}</div>
</div>
</div>
{/* Project Context Section */}
<div className="hero-card p-6 mb-6">
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">Project Context</h3>
{/* Project Files */}
<div className="space-y-4">
{instance.project_files.requirements && (
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">requirements.md</h4>
<pre className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap">
{instance.project_files.requirements}
</pre>
</div>
)}
{instance.project_files.readme && (
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">README.md</h4>
<pre className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap">
{instance.project_files.readme}
</pre>
</div>
)}
{instance.project_files.agents && (
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">AGENTS.md</h4>
<pre className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap">
{instance.project_files.agents}
</pre>
</div>
)}
</div>
{/* Git Status */}
{instance.git_status && (
<div className="mt-6">
<GitStatus status={instance.git_status} />
</div>
)}
</div>
{/* Chat View Section */}
<div className="hero-card p-6">
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">Chat History</h3>
<ChatView messages={logs.messages} toolCalls={logs.tool_calls} />
</div>
</div>
)
}
export default Detail