0.10.2-11 Layout
This commit is contained in:
@@ -2113,165 +2113,6 @@ export default function DashboardPage({
|
||||
<div className="page-grid">
|
||||
<Toast ref={toastRef} />
|
||||
|
||||
{monitoringState.enabled ? (
|
||||
<Card title="Hardware Monitoring">
|
||||
<div className="hardware-monitor-head">
|
||||
<Tag value={`Letztes Update: ${formatUpdatedAt(monitoringState.updatedAt)}`} severity="warning" />
|
||||
</div>
|
||||
|
||||
{monitoringState.error ? (
|
||||
<small className="error-text">{monitoringState.error}</small>
|
||||
) : null}
|
||||
|
||||
{!monitoringState.enabled ? (
|
||||
<p>Monitoring ist deaktiviert. Aktivierung in den Settings unter Kategorie "Monitoring".</p>
|
||||
) : !monitoringSample ? (
|
||||
<p>Monitoring ist aktiv. Erste Messwerte werden gesammelt ...</p>
|
||||
) : (
|
||||
<div className="hardware-monitor-grid">
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>CPU</h4>
|
||||
<div className="hardware-cpu-summary">
|
||||
<div className="hardware-cpu-chip" title="CPU Gesamtauslastung">
|
||||
<i className="pi pi-chart-line" />
|
||||
<span>{formatPercent(cpuMetrics?.overallUsagePercent)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="CPU Gesamttemperatur">
|
||||
<i className="pi pi-bolt" />
|
||||
<span>{formatTemperature(cpuMetrics?.overallTemperatureC)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-load-group">
|
||||
<div className="hardware-cpu-chip" title="CPU Load Average">
|
||||
<i className="pi pi-chart-bar" />
|
||||
<span>{Array.isArray(cpuMetrics?.loadAverage) ? cpuMetrics.loadAverage.join(' / ') : '-'}</span>
|
||||
</div>
|
||||
{cpuPerCoreMetrics.length > 0 ? (
|
||||
<button
|
||||
type="button"
|
||||
className="hardware-cpu-core-toggle-btn"
|
||||
onClick={() => setCpuCoresExpanded((prev) => !prev)}
|
||||
aria-label={cpuCoresExpanded ? 'CPU-Kerne ausblenden' : 'CPU-Kerne einblenden'}
|
||||
aria-expanded={cpuCoresExpanded}
|
||||
>
|
||||
<i className={`pi ${cpuCoresExpanded ? 'pi-angle-up' : 'pi-angle-down'}`} />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{cpuPerCoreMetrics.length === 0 ? (
|
||||
<small>Pro-Core-Daten sind noch nicht verfuegbar.</small>
|
||||
) : null}
|
||||
{cpuPerCoreMetrics.length > 0 && cpuCoresExpanded ? (
|
||||
<div className="hardware-core-grid compact">
|
||||
{cpuPerCoreMetrics.map((core) => (
|
||||
<div key={`core-${core.index}`} className="hardware-core-item compact">
|
||||
<div className="hardware-core-title">C{core.index}</div>
|
||||
<div className="hardware-core-metric" title="Auslastung">
|
||||
<i className="pi pi-chart-line" />
|
||||
<small>{formatPercent(core.usagePercent)}</small>
|
||||
</div>
|
||||
<div className="hardware-core-metric" title="Temperatur">
|
||||
<i className="pi pi-bolt" />
|
||||
<small>{formatTemperature(core.temperatureC)}</small>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>RAM</h4>
|
||||
<div className="hardware-cpu-summary">
|
||||
<div className="hardware-cpu-chip" title="RAM Auslastung">
|
||||
<i className="pi pi-chart-pie" />
|
||||
<span>{formatPercent(memoryMetrics?.usagePercent)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Belegt">
|
||||
<i className="pi pi-arrow-up" />
|
||||
<span>{formatBytes(memoryMetrics?.usedBytes)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Frei">
|
||||
<i className="pi pi-arrow-down" />
|
||||
<span>{formatBytes(memoryMetrics?.freeBytes)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Gesamt">
|
||||
<i className="pi pi-database" />
|
||||
<span>{formatBytes(memoryMetrics?.totalBytes)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>GPU</h4>
|
||||
{!gpuMetrics?.available ? (
|
||||
<small>{gpuMetrics?.message || 'Keine GPU-Metriken verfuegbar.'}</small>
|
||||
) : (
|
||||
<div className="hardware-gpu-list">
|
||||
{gpuDevices.map((gpu, index) => (
|
||||
<div key={`gpu-${gpu?.index ?? index}`} className="hardware-gpu-item">
|
||||
<strong>
|
||||
GPU {gpu?.index ?? index}
|
||||
{gpu?.name ? ` | ${gpu.name}` : ''}
|
||||
</strong>
|
||||
<small>Load: {formatPercent(gpu?.utilizationPercent)}</small>
|
||||
<small>Mem-Load: {formatPercent(gpu?.memoryUtilizationPercent)}</small>
|
||||
<small>Temp: {formatTemperature(gpu?.temperatureC)}</small>
|
||||
<small>VRAM: {formatBytes(gpu?.memoryUsedBytes)} / {formatBytes(gpu?.memoryTotalBytes)}</small>
|
||||
<small>Power: {Number.isFinite(Number(gpu?.powerDrawW)) ? `${gpu.powerDrawW} W` : 'n/a'} / {Number.isFinite(Number(gpu?.powerLimitW)) ? `${gpu.powerLimitW} W` : 'n/a'}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>Freier Speicher</h4>
|
||||
<div className="hardware-storage-list">
|
||||
{storageGroups.map((group) => {
|
||||
const rep = group.representative;
|
||||
const tone = getStorageUsageTone(rep?.usagePercent);
|
||||
const usagePercent = Number(rep?.usagePercent);
|
||||
const barValue = Number.isFinite(usagePercent)
|
||||
? Math.max(0, Math.min(100, usagePercent))
|
||||
: 0;
|
||||
const hasError = group.entries.every((e) => e?.error);
|
||||
const groupKey = group.mountPoint || group.entries.map((e) => e?.key).join('-');
|
||||
const labels = group.entries.map((e) => e?.label || e?.key || 'Pfad').join(' · ');
|
||||
return (
|
||||
<div
|
||||
key={`storage-group-${groupKey}`}
|
||||
className={`hardware-storage-item compact${hasError ? ' has-error' : ''}`}
|
||||
>
|
||||
<small className="hardware-storage-group-label">{labels}</small>
|
||||
{hasError ? (
|
||||
<small className="error-text">{rep?.error}</small>
|
||||
) : (
|
||||
<>
|
||||
<div className="hardware-storage-bar-row">
|
||||
<div className={`hardware-storage-bar tone-${tone}`}>
|
||||
<ProgressBar value={barValue} showValue={false} />
|
||||
</div>
|
||||
<span className={`hardware-storage-percent tone-${tone}`}>
|
||||
{formatPercent(rep?.usagePercent)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hardware-storage-summary">
|
||||
<small>Frei: {formatBytes(rep?.freeBytes)}</small>
|
||||
<small>Gesamt: {formatBytes(rep?.totalBytes)}</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
<div className="dashboard-3col-grid">
|
||||
<div className="dashboard-col dashboard-col-left">
|
||||
<Card title="Audiobook Upload">
|
||||
@@ -2390,6 +2231,48 @@ export default function DashboardPage({
|
||||
<p>Aktuell keine Disk erkannt.</p>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="Freier Speicher">
|
||||
<div className="hardware-storage-list">
|
||||
{storageGroups.map((group) => {
|
||||
const rep = group.representative;
|
||||
const tone = getStorageUsageTone(rep?.usagePercent);
|
||||
const usagePercent = Number(rep?.usagePercent);
|
||||
const barValue = Number.isFinite(usagePercent)
|
||||
? Math.max(0, Math.min(100, usagePercent))
|
||||
: 0;
|
||||
const hasError = group.entries.every((e) => e?.error);
|
||||
const groupKey = group.mountPoint || group.entries.map((e) => e?.key).join('-');
|
||||
const labels = group.entries.map((e) => e?.label || e?.key || 'Pfad').join(' · ');
|
||||
return (
|
||||
<div
|
||||
key={`storage-group-${groupKey}`}
|
||||
className={`hardware-storage-item compact${hasError ? ' has-error' : ''}`}
|
||||
>
|
||||
<small className="hardware-storage-group-label">{labels}</small>
|
||||
{hasError ? (
|
||||
<small className="error-text">{rep?.error}</small>
|
||||
) : (
|
||||
<>
|
||||
<div className="hardware-storage-bar-row">
|
||||
<div className={`hardware-storage-bar tone-${tone}`}>
|
||||
<ProgressBar value={barValue} showValue={false} />
|
||||
</div>
|
||||
<span className={`hardware-storage-percent tone-${tone}`}>
|
||||
{formatPercent(rep?.usagePercent)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hardware-storage-summary">
|
||||
<small>Frei: {formatBytes(rep?.freeBytes)}</small>
|
||||
<small>Gesamt: {formatBytes(rep?.totalBytes)}</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-col dashboard-col-center">
|
||||
@@ -2923,6 +2806,116 @@ export default function DashboardPage({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{monitoringState.enabled ? (
|
||||
<Card title="Hardware Monitoring">
|
||||
<div className="hardware-monitor-head">
|
||||
{!monitoringState.enabled ? (
|
||||
<small>Hardware-Monitoring ist deaktiviert.</small>
|
||||
) : !monitoringSample ? (
|
||||
<small>Warte auf erste Messung ...</small>
|
||||
) : (
|
||||
<div className="hardware-monitor-grid">
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>CPU</h4>
|
||||
<div className="hardware-cpu-summary">
|
||||
<div className="hardware-cpu-chip" title="CPU Gesamtauslastung">
|
||||
<i className="pi pi-chart-line" />
|
||||
<span>{formatPercent(cpuMetrics?.overallUsagePercent)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="CPU Gesamttemperatur">
|
||||
<i className="pi pi-bolt" />
|
||||
<span>{formatTemperature(cpuMetrics?.overallTemperatureC)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-load-group">
|
||||
<div className="hardware-cpu-chip" title="CPU Load Average">
|
||||
<i className="pi pi-chart-bar" />
|
||||
<span>{Array.isArray(cpuMetrics?.loadAverage) ? cpuMetrics.loadAverage.join(' / ') : '-'}</span>
|
||||
</div>
|
||||
{cpuPerCoreMetrics.length > 0 ? (
|
||||
<button
|
||||
type="button"
|
||||
className="hardware-cpu-core-toggle-btn"
|
||||
onClick={() => setCpuCoresExpanded((prev) => !prev)}
|
||||
aria-label={cpuCoresExpanded ? 'CPU-Kerne ausblenden' : 'CPU-Kerne einblenden'}
|
||||
aria-expanded={cpuCoresExpanded}
|
||||
>
|
||||
<i className={`pi ${cpuCoresExpanded ? 'pi-angle-up' : 'pi-angle-down'}`} />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{cpuPerCoreMetrics.length === 0 ? (
|
||||
<small>Pro-Core-Daten sind noch nicht verfuegbar.</small>
|
||||
) : null}
|
||||
{cpuPerCoreMetrics.length > 0 && cpuCoresExpanded ? (
|
||||
<div className="hardware-core-grid compact">
|
||||
{cpuPerCoreMetrics.map((core) => (
|
||||
<div key={`core-${core.index}`} className="hardware-core-item compact">
|
||||
<div className="hardware-core-title">C{core.index}</div>
|
||||
<div className="hardware-core-metric" title="Auslastung">
|
||||
<i className="pi pi-chart-line" />
|
||||
<small>{formatPercent(core.usagePercent)}</small>
|
||||
</div>
|
||||
<div className="hardware-core-metric" title="Temperatur">
|
||||
<i className="pi pi-bolt" />
|
||||
<small>{formatTemperature(core.temperatureC)}</small>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>RAM</h4>
|
||||
<div className="hardware-cpu-summary">
|
||||
<div className="hardware-cpu-chip" title="RAM Auslastung">
|
||||
<i className="pi pi-chart-pie" />
|
||||
<span>{formatPercent(memoryMetrics?.usagePercent)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Belegt">
|
||||
<i className="pi pi-arrow-up" />
|
||||
<span>{formatBytes(memoryMetrics?.usedBytes)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Frei">
|
||||
<i className="pi pi-arrow-down" />
|
||||
<span>{formatBytes(memoryMetrics?.freeBytes)}</span>
|
||||
</div>
|
||||
<div className="hardware-cpu-chip" title="RAM Gesamt">
|
||||
<i className="pi pi-database" />
|
||||
<span>{formatBytes(memoryMetrics?.totalBytes)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="hardware-monitor-block">
|
||||
<h4>GPU</h4>
|
||||
{!gpuMetrics?.available ? (
|
||||
<small>{gpuMetrics?.message || 'Keine GPU-Metriken verfuegbar.'}</small>
|
||||
) : (
|
||||
<div className="hardware-gpu-list">
|
||||
{gpuDevices.map((gpu, index) => (
|
||||
<div key={`gpu-${gpu?.index ?? index}`} className="hardware-gpu-item">
|
||||
<strong>
|
||||
GPU {gpu?.index ?? index}
|
||||
{gpu?.name ? ` | ${gpu.name}` : ''}
|
||||
</strong>
|
||||
<small>Load: {formatPercent(gpu?.utilizationPercent)}</small>
|
||||
<small>Mem-Load: {formatPercent(gpu?.memoryUtilizationPercent)}</small>
|
||||
<small>Temp: {formatTemperature(gpu?.temperatureC)}</small>
|
||||
<small>VRAM: {formatBytes(gpu?.memoryUsedBytes)} / {formatBytes(gpu?.memoryTotalBytes)}</small>
|
||||
<small>Power: {Number.isFinite(Number(gpu?.powerDrawW)) ? `${gpu.powerDrawW} W` : 'n/a'} / {Number.isFinite(Number(gpu?.powerLimitW)) ? `${gpu.powerLimitW} W` : 'n/a'}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
<MetadataSelectionDialog
|
||||
visible={metadataDialogVisible}
|
||||
context={effectiveMetadataDialogContext}
|
||||
|
||||
Reference in New Issue
Block a user