v0.2.7.0
This commit is contained in:
parent
a0f6b821ce
commit
411f25b1b7
3 changed files with 134 additions and 129 deletions
|
|
@ -821,12 +821,18 @@ const Agents: React.FC = () => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* System info — screenshot left, stats right, top processes below */}
|
||||
{/* System info — CSS Grid auto-placement, screenshot anchors top-left */}
|
||||
<div className="card">
|
||||
<h2 className="text-lg font-medium text-gray-900 mb-4">System Information</h2>
|
||||
|
||||
<div className="flex gap-6">
|
||||
{/* Screenshot — left, fixed width */}
|
||||
<div
|
||||
className="grid gap-4"
|
||||
style={{
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))',
|
||||
gridAutoRows: 'min-content',
|
||||
}}
|
||||
>
|
||||
{/* Screenshot — spans 2 rows, anchors top-left */}
|
||||
{(() => {
|
||||
const integrations = readIntegrations(selectedAgent.metadata);
|
||||
const sunshine = integrations.sunshine;
|
||||
|
|
@ -839,7 +845,7 @@ const Agents: React.FC = () => {
|
|||
const canClick = !isCapturing && !isPolling;
|
||||
|
||||
return (
|
||||
<div className="shrink-0 w-[260px]">
|
||||
<div style={{ gridRow: 'span 2', gridColumn: '1' }}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative aspect-video w-full overflow-hidden rounded border',
|
||||
|
|
@ -897,140 +903,139 @@ const Agents: React.FC = () => {
|
|||
? <span className="text-red-500">Failed</span>
|
||||
: null}
|
||||
</div>
|
||||
{/* Platform info — under screenshot */}
|
||||
{(() => {
|
||||
const osInfo = parseOSInfo(selectedAgent);
|
||||
return (
|
||||
<div className="mt-2 space-y-1">
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Platform</p>
|
||||
<p className="text-sm font-medium text-gray-900">{osInfo.platform}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Distribution</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{osInfo.distribution}
|
||||
{osInfo.version && <span className="text-xs text-gray-500 ml-1">({osInfo.version})</span>}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Architecture</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{selectedAgent.os_architecture || selectedAgent.architecture}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* All stats — right column, stacked flush top */}
|
||||
<div className="flex-1 space-y-3 min-w-0">
|
||||
{(() => {
|
||||
const meta = getSystemMetadata(selectedAgent);
|
||||
return (
|
||||
<>
|
||||
{/* Platform metadata */}
|
||||
{(() => {
|
||||
const osInfo = parseOSInfo(selectedAgent);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Platform</p>
|
||||
<p className="text-sm font-medium text-gray-900">{osInfo.platform}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Distribution</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{osInfo.distribution}
|
||||
{osInfo.version && <span className="text-xs text-gray-500 ml-1">({osInfo.version})</span>}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Architecture</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{selectedAgent.os_architecture || selectedAgent.architecture}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Hardware metrics */}
|
||||
{(() => {
|
||||
const meta = getSystemMetadata(selectedAgent);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<Cpu className="h-3 w-3" /> CPU
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.cpuModel}</p>
|
||||
<p className="text-xs text-gray-500">{meta.cpuCores} cores</p>
|
||||
</div>
|
||||
{meta.memoryTotal > 0 && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<Cpu className="h-3 w-3" /> CPU
|
||||
<MemoryStick className="h-3 w-3" /> Memory
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.cpuModel}</p>
|
||||
<p className="text-xs text-gray-500">{meta.cpuCores} cores</p>
|
||||
<p className="text-sm font-medium text-gray-900">{formatBytes(meta.memoryTotal)}</p>
|
||||
</div>
|
||||
{meta.memoryTotal > 0 && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<MemoryStick className="h-3 w-3" /> Memory
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{formatBytes(meta.memoryTotal)}</p>
|
||||
)}
|
||||
{meta.diskTotal > 0 && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<HardDrive className="h-3 w-3" /> Disk ({meta.diskMount})
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{formatBytes(meta.diskUsed)} / {formatBytes(meta.diskTotal)}
|
||||
</p>
|
||||
<div className="w-full bg-gray-200 rounded-full h-1.5 mt-1">
|
||||
<div
|
||||
className="bg-blue-600 h-1.5 rounded-full"
|
||||
style={{ width: `${Math.round((meta.diskUsed / meta.diskTotal) * 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{meta.diskTotal > 0 && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<HardDrive className="h-3 w-3" /> Disk ({meta.diskMount})
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{formatBytes(meta.diskUsed)} / {formatBytes(meta.diskTotal)}
|
||||
</p>
|
||||
<div className="w-full bg-gray-200 rounded-full h-1.5 mt-1">
|
||||
<div
|
||||
className="bg-blue-600 h-1.5 rounded-full"
|
||||
style={{ width: `${Math.round((meta.diskUsed / meta.diskTotal) * 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">{Math.round((meta.diskUsed / meta.diskTotal) * 100)}% used</p>
|
||||
</div>
|
||||
)}
|
||||
{meta.processes !== 'Unknown' && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<GitBranch className="h-3 w-3" /> Running Processes
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.processes}</p>
|
||||
</div>
|
||||
)}
|
||||
{meta.uptime !== 'Unknown' && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" /> Uptime
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.uptime}</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<p className="text-xs text-gray-500">{Math.round((meta.diskUsed / meta.diskTotal) * 100)}% used</p>
|
||||
</div>
|
||||
)}
|
||||
{meta.processes !== 'Unknown' && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<GitBranch className="h-3 w-3" /> Running Processes
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.processes}</p>
|
||||
</div>
|
||||
)}
|
||||
{meta.uptime !== 'Unknown' && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" /> Uptime
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{meta.uptime}</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Top Processes — grid item, auto-placed */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-xs font-medium text-gray-900 flex items-center gap-1">
|
||||
<Activity className="h-3 w-3" /> Top Processes
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigate(`/updates?agent=${selectedAgent.id}`)}
|
||||
className="text-[10px] text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
See More →
|
||||
</button>
|
||||
</div>
|
||||
{(() => {
|
||||
const meta = getSystemMetadata(selectedAgent);
|
||||
const topProcesses = selectedAgent.metadata?.top_processes;
|
||||
if (topProcesses && Array.isArray(topProcesses) && topProcesses.length > 0) {
|
||||
return (
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="text-gray-500 border-b border-gray-100">
|
||||
<th className="text-left py-1 font-medium">Name</th>
|
||||
<th className="text-right py-1 font-medium">PID</th>
|
||||
<th className="text-right py-1 font-medium">CPU%</th>
|
||||
<th className="text-right py-1 font-medium">Mem%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{topProcesses.slice(0, 5).map((proc: any, i: number) => (
|
||||
<tr key={proc.pid || i} className="border-b border-gray-50 last:border-0">
|
||||
<td className="py-1 text-gray-900 font-medium truncate max-w-[160px]">{proc.name}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.pid}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.cpu != null ? `${proc.cpu.toFixed(1)}%` : '—'}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.mem != null ? `${proc.mem.toFixed(1)}%` : '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className="text-[10px] text-gray-400 italic">
|
||||
Process details not reported. Count: {meta.processes}
|
||||
</p>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Top Processes */}
|
||||
<div className="pt-3 border-t border-gray-200">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-xs font-medium text-gray-900 flex items-center gap-1">
|
||||
<Activity className="h-3 w-3" /> Top Processes
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigate(`/updates?agent=${selectedAgent.id}`)}
|
||||
className="text-[10px] text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
See More →
|
||||
</button>
|
||||
</div>
|
||||
{(() => {
|
||||
const meta = getSystemMetadata(selectedAgent);
|
||||
const topProcesses = selectedAgent.metadata?.top_processes;
|
||||
if (topProcesses && Array.isArray(topProcesses) && topProcesses.length > 0) {
|
||||
return (
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="text-gray-500 border-b border-gray-100">
|
||||
<th className="text-left py-1 font-medium">Name</th>
|
||||
<th className="text-right py-1 font-medium">PID</th>
|
||||
<th className="text-right py-1 font-medium">CPU%</th>
|
||||
<th className="text-right py-1 font-medium">Mem%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{topProcesses.slice(0, 5).map((proc: any, i: number) => (
|
||||
<tr key={proc.pid || i} className="border-b border-gray-50 last:border-0">
|
||||
<td className="py-1 text-gray-900 font-medium truncate max-w-[160px]">{proc.name}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.pid}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.cpu != null ? `${proc.cpu.toFixed(1)}%` : '—'}</td>
|
||||
<td className="py-1 text-right text-gray-600">{proc.mem != null ? `${proc.mem.toFixed(1)}%` : '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className="text-[10px] text-gray-400 italic">
|
||||
Process details not reported. Count: {meta.processes}
|
||||
</p>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue