0.10.2-11 Layout

This commit is contained in:
2026-03-16 11:30:09 +00:00
parent 42eceea521
commit bf72c40ae1
9 changed files with 214 additions and 293 deletions
+152 -159
View File
@@ -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}