Fix #2: Auslastungsmonitor in Agent-Tab + Monitoring nach CPU/RAM sortiert

This commit is contained in:
2026-06-04 17:00:41 +02:00
parent 67059b551d
commit d584e65226
2 changed files with 125 additions and 13 deletions

View File

@@ -241,7 +241,7 @@ const AssignModal = ({ asset, onClose, onAssigned }) => {
useEffect(() => {
authFetch(`${API}/users`)
.then(r => r.json())
.then(data => setUsers(Array.isArray(data) ? data : (data.users || [])))
.then(data => setUsers(data.data || data.users || (Array.isArray(data) ? data : [])))
.catch(() => {});
}, []);
@@ -449,7 +449,7 @@ const AssetsPage = () => {
if (!detail) return;
if (!window.confirm('Zuweisung wirklich aufheben?')) return;
try {
const res = await authFetch(`${API}/assets/${detail.id}/unassign`, { method: 'POST' });
const res = await authFetch(`${API}/assets/${detail.id}/unassign`, { method: 'POST', body: JSON.stringify({ new_status: 'verfuegbar' }) });
if (!res.ok) throw new Error(await res.text());
toast.success('Zuweisung aufgehoben');
loadAssets();
@@ -1024,6 +1024,72 @@ const AssetsPage = () => {
) : null)
) : <div style={{ color: '#64748b', fontSize: 13 }}>Kein Agent verbunden</div>}
</div>
{/* Auslastung */}
<div className="ap-card">
<div className="ap-card-title">📊 Auslastung</div>
{agent ? (() => {
const cpuPct = agent.cpu_usage_percent ?? null;
const ramUsed = agent.ram_used_gb ?? null;
const ramTotal = agent.ram_total_gb ?? null;
const ramPct = ramTotal > 0 ? Math.round((ramUsed / ramTotal) * 100) : null;
const diskTotal = agent.disk_total_gb ?? null;
const diskFree = agent.disk_free_gb ?? null;
const diskUsed = diskTotal != null && diskFree != null ? diskTotal - diskFree : null;
const diskPct = diskTotal > 0 ? Math.round((diskUsed / diskTotal) * 100) : null;
const uptimeH = agent.uptime_hours ?? null;
const fmtUptime = (h) => {
if (h == null) return '—';
if (h < 24) return `${Math.floor(h)} Std.`;
return `${Math.floor(h / 24)} Tag(e)`;
};
const barColor = (p) => p > 80 ? '#ef4444' : p > 60 ? '#f59e0b' : '#34d399';
const Bar = ({ pct: p }) => (
<div style={{ background: 'rgba(255,255,255,0.08)', borderRadius: 4, height: 6, marginTop: 4 }}>
<div style={{ background: barColor(p), width: `${Math.min(100, p)}%`, height: 6, borderRadius: 4, transition: 'width 0.3s' }} />
</div>
);
return (
<>
{cpuPct != null && (
<div className="ap-info-row" style={{ flexDirection: 'column', alignItems: 'stretch', gap: 2 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span className="ap-info-label">CPU-Auslastung</span>
<span className="ap-info-value" style={{ color: barColor(cpuPct) }}>{cpuPct.toFixed(1)} %</span>
</div>
<Bar pct={cpuPct} />
</div>
)}
{ramUsed != null && ramTotal != null && (
<div className="ap-info-row" style={{ flexDirection: 'column', alignItems: 'stretch', gap: 2 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span className="ap-info-label">RAM</span>
<span className="ap-info-value" style={{ color: barColor(ramPct) }}>{ramUsed.toFixed(1)} / {ramTotal.toFixed(0)} GB</span>
</div>
<Bar pct={ramPct} />
</div>
)}
{diskUsed != null && diskTotal != null && (
<div className="ap-info-row" style={{ flexDirection: 'column', alignItems: 'stretch', gap: 2 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span className="ap-info-label">Disk</span>
<span className="ap-info-value" style={{ color: barColor(diskPct) }}>{diskUsed.toFixed(0)} / {diskTotal.toFixed(0)} GB</span>
</div>
<Bar pct={diskPct} />
</div>
)}
{uptimeH != null && (
<div className="ap-info-row">
<span className="ap-info-label">Laufzeit</span>
<span className="ap-info-value">{fmtUptime(uptimeH)}</span>
</div>
)}
{cpuPct == null && ramUsed == null && diskUsed == null && uptimeH == null && (
<div style={{ color: '#64748b', fontSize: 13 }}>Keine Auslastungsdaten</div>
)}
</>
);
})() : <div style={{ color: '#64748b', fontSize: 13 }}>Kein Agent verbunden</div>}
</div>
</div>
)}
@@ -1031,14 +1097,27 @@ const AssetsPage = () => {
{activeTab === 'dokumente' && (
<div className="ap-card">
<div className="ap-card-title">📁 Dokumente &amp; Aktionen</div>
<div
className="ap-doc-item"
onClick={() => window.open(`${API}/assets/${detail.id}/label`, '_blank')}
>
<div className="ap-doc-icon">🏷</div>
<div>
<div className="ap-doc-name">Asset-Label drucken</div>
<div className="ap-doc-meta">QR-Code Label als PDF öffnen</div>
<div className="ap-doc-item" style={{cursor:'default', flexDirection:'column', alignItems:'flex-start', gap:10}}>
<div style={{display:'flex', alignItems:'center', gap:10}}>
<div className="ap-doc-icon">🏷</div>
<div>
<div className="ap-doc-name">Asset-Label drucken</div>
<div className="ap-doc-meta">Format wählen und als PDF öffnen</div>
</div>
</div>
<div style={{display:'flex', gap:8, flexWrap:'wrap', paddingLeft:4}}>
{[
{ key: 'small', label: 'Klein (50×25mm)' },
{ key: 'medium', label: 'Mittel (80×40mm)' },
{ key: 'large', label: 'Groß (A5)' },
{ key: 'ptouch12', label: 'P-Touch 700 (12mm)' },
].map(f => (
<button key={f.key}
className="ap-btn ap-btn-sm"
style={{fontSize:11, padding:'4px 10px', background:'rgba(13,148,136,0.12)', border:'1px solid rgba(13,148,136,0.3)', color:'#0d9488', borderRadius:6, cursor:'pointer'}}
onClick={() => openPdf(`${API}/assets/${detail.id}/label?size=${f.key}`)}
>{f.label}</button>
))}
</div>
</div>
{detail.status === 'zugewiesen' && (