diff --git a/app/Console/Commands/StorageProbe.php b/app/Console/Commands/StorageProbe.php index 0efaed9..a92e5cd 100644 --- a/app/Console/Commands/StorageProbe.php +++ b/app/Console/Commands/StorageProbe.php @@ -8,25 +8,27 @@ use App\Models\Setting; class StorageProbe extends Command { protected $signature = 'health:probe-disk {target=/}'; - protected $description = 'Speichert Storage-Werte (inkl. Frei+5%) in settings:health.disk'; + protected $description = 'Speichert Storage-Werte (inkl. Breakdown) in settings:health.disk'; public function handle(): int { $target = $this->argument('target') ?: '/'; - $data = $this->probe($target); + $data = $this->probe($target); - // Persistiert (DB + Redis) über dein Settings-Model Setting::set('health.disk', $data); Setting::set('health.disk_updated_at', now()->toIso8601String()); $this->info(sprintf( - 'Storage %s → total:%dGB used:%dGB free_user:%dGB free+5%%:%dGB (%%used:%d)', + 'Storage %s → total:%dGB used:%dGB free_user:%dGB free+5%%:%dGB (%%used:%d) breakdown: system=%.1fGB mails=%.1fGB backups=%.1fGB', $data['mount'], $data['total_gb'], $data['used_gb'], $data['free_gb'], $data['free_plus_reserve_gb'], $data['percent_used_total'], + $data['breakdown']['system_gb'], + $data['breakdown']['mails_gb'], + $data['breakdown']['backup_gb'], )); return self::SUCCESS; @@ -34,7 +36,8 @@ class StorageProbe extends Command protected function probe(string $target): array { - $line = trim((string) @shell_exec('df -kP ' . escapeshellarg($target) . ' 2>/dev/null | tail -n1')); + // --- df: Gesamtdaten des Filesystems (inkl. Reserve) ----------------- + $line = trim((string)@shell_exec('LC_ALL=C df -kP ' . escapeshellarg($target) . ' 2>/dev/null | tail -n1')); $device = $mount = ''; $totalKb = $usedKb = $availKb = 0; @@ -42,35 +45,140 @@ class StorageProbe extends Command if ($line !== '') { $p = preg_split('/\s+/', $line); if (count($p) >= 6) { - $device = $p[0]; - $totalKb = (int) $p[1]; // TOTAL (inkl. Reserve) - $usedKb = (int) $p[2]; // Used - $availKb = (int) $p[3]; // Avail (User-sicht) - $mount = $p[5]; + $device = $p[0]; + $totalKb = (int)$p[1]; // TOTAL (inkl. Reserve) + $usedKb = (int)$p[2]; // Used + $availKb = (int)$p[3]; // Avail (User-sicht) + $mount = $p[5]; } } - $toGiB = static fn($kb) => (int) round(max(0, (int)$kb) / (1024*1024)); + $toGiB_i = static fn($kb) => (int)round(max(0, (int)$kb) / (1024 * 1024)); // ganzzahlig (UI: Gesamt/Genutzt/Frei) + $toGiB_f = static fn($kb) => round(max(0, (int)$kb) / (1024 * 1024), 1); // eine Nachkommastelle (Breakdown/Legende) - $totalGb = $toGiB($totalKb); - $freeGb = $toGiB($availKb); // user-verfügbar - $usedGb = max(0, $totalGb - $freeGb); // belegt inkl. Reserve - $res5Gb = (int) round($totalGb * 0.05); // 5% von Gesamt + $totalGb = $toGiB_i($totalKb); + $freeGb = $toGiB_i($availKb); // user-verfügbar + $usedGb = max(0, $totalGb - $freeGb); // belegt inkl. Reserve + $res5Gb = (int)round($totalGb * 0.05); // 5% von Gesamt $freePlusReserveGb = min($totalGb, $freeGb + $res5Gb); + $percentUsed = $totalGb > 0 ? (int)round($usedGb * 100 / $totalGb) : 0; - $percentUsed = $totalGb > 0 ? (int) round($usedGb * 100 / $totalGb) : 0; + // --- du: reale Verbräuche bestimmter Bäume (KB) ----------------------- + $duKb = function (string $path): int { + if (!is_dir($path)) return 0; + $kb = (int)trim((string)@shell_exec('LC_ALL=C du -sk --apparent-size ' . escapeshellarg($path) . ' 2>/dev/null | cut -f1')); + return max(0, $kb); + }; + + $kbMails = $duKb('/var/mail/vhosts'); + $kbBackup = $duKb('/var/backups/mailwolt'); + + // „System“ = alles übrige, was nicht Mails/Backups ist (OS, App, Logs, DB-Daten, …) + $gbMails = $toGiB_f($kbMails); + $gbBackup = $toGiB_f($kbBackup); + + // system_gb aus „usedGb – (mails+backup)“, nie negativ + $gbSystem = max(0, round($usedGb - ($gbMails + $gbBackup), 1)); return [ - 'device' => $device ?: 'unknown', - 'mount' => $mount ?: $target, + 'device' => $device ?: 'unknown', + 'mount' => $mount ?: $target, - 'total_gb' => $totalGb, - 'used_gb' => $usedGb, // inkl. Reserve - 'free_gb' => $freeGb, // User-sicht - 'reserve5_gb' => $res5Gb, // Info - 'free_plus_reserve_gb' => $freePlusReserveGb, // ← das willst du anzeigen + 'total_gb' => $totalGb, + 'used_gb' => $usedGb, // inkl. Reserve + 'free_gb' => $freeGb, // User-sicht + 'reserve5_gb' => $res5Gb, // Info + 'free_plus_reserve_gb' => $freePlusReserveGb, // Anzeige „Frei“ - 'percent_used_total' => $percentUsed, // fürs Donut (~15%) + 'percent_used_total' => $percentUsed, + + // Reale Breakdown-Werte + 'breakdown' => [ + 'system_gb' => $gbSystem, + 'mails_gb' => $gbMails, + 'backup_gb' => $gbBackup, + ], ]; } } + +// +//namespace App\Console\Commands; +// +//use Illuminate\Console\Command; +//use App\Models\Setting; +// +//class StorageProbe extends Command +//{ +// protected $signature = 'health:probe-disk {target=/}'; +// protected $description = 'Speichert Storage-Werte (inkl. Frei+5%) in settings:health.disk'; +// +// public function handle(): int +// { +// $target = $this->argument('target') ?: '/'; +// $data = $this->probe($target); +// +// // Persistiert (DB + Redis) über dein Settings-Model +// Setting::set('health.disk', $data); +// Setting::set('health.disk_updated_at', now()->toIso8601String()); +// +// $this->info(sprintf( +// 'Storage %s → total:%dGB used:%dGB free_user:%dGB free+5%%:%dGB (%%used:%d)', +// $data['mount'], +// $data['total_gb'], +// $data['used_gb'], +// $data['free_gb'], +// $data['free_plus_reserve_gb'], +// $data['percent_used_total'], +// )); +// +// return self::SUCCESS; +// } +// +// protected function probe(string $target): array +// { +// $line = trim((string) @shell_exec('df -kP ' . escapeshellarg($target) . ' 2>/dev/null | tail -n1')); +// +// $device = $mount = ''; +// $totalKb = $usedKb = $availKb = 0; +// +// if ($line !== '') { +// $p = preg_split('/\s+/', $line); +// if (count($p) >= 6) { +// $device = $p[0]; +// $totalKb = (int) $p[1]; // TOTAL (inkl. Reserve) +// $usedKb = (int) $p[2]; // Used +// $availKb = (int) $p[3]; // Avail (User-sicht) +// $mount = $p[5]; +// } +// } +// +// $toGiB = static fn($kb) => (int) round(max(0, (int)$kb) / (1024*1024)); +// +// $totalGb = $toGiB($totalKb); +// $freeGb = $toGiB($availKb); // user-verfügbar +// $usedGb = max(0, $totalGb - $freeGb); // belegt inkl. Reserve +// $res5Gb = (int) round($totalGb * 0.05); // 5% von Gesamt +// $freePlusReserveGb = min($totalGb, $freeGb + $res5Gb); +// +// $percentUsed = $totalGb > 0 ? (int) round($usedGb * 100 / $totalGb) : 0; +// +// return [ +// 'device' => $device ?: 'unknown', +// 'mount' => $mount ?: $target, +// +// 'total_gb' => $totalGb, +// 'used_gb' => $usedGb, // inkl. Reserve +// 'free_gb' => $freeGb, // User-sicht +// 'reserve5_gb' => $res5Gb, // Info +// 'free_plus_reserve_gb' => $freePlusReserveGb, // ← das willst du anzeigen +// +// 'percent_used_total' => $percentUsed, // fürs Donut (~15%) +// 'breakdown' => [ +// 'system_gb' => 5.2, // OS, App, Logs … +// 'mails_gb' => 2.8, // /var/mail/vhosts +// 'backup_gb' => 1.0, // /var/backups/mailwolt (oder wohin du sicherst) +// ], +// ]; +// } +//} diff --git a/app/Livewire/Ui/System/StorageCard.php b/app/Livewire/Ui/System/StorageCard.php index e086890..76a5d70 100644 --- a/app/Livewire/Ui/System/StorageCard.php +++ b/app/Livewire/Ui/System/StorageCard.php @@ -1,5 +1,6 @@ '–', 'label' => 'SPEICHER BELEGT']; + public int $diskSegOuterRadius = 92; + public int $diskInnerSize = 160; + public array $diskCenterText = ['percent' => '–', 'label' => 'SPEICHER BELEGT']; + + // Stacked-Bar + Legende + public array $barSegments = []; // [{label,color,gb,percent}] public ?string $measuredAt = null; - protected int $segCount = 48; + protected int $segCount = 100; public function mount(string $target = '/'): void { @@ -28,7 +34,10 @@ class StorageCard extends Component $this->loadFromSettings(); } - public function render() { return view('livewire.ui.system.storage-card'); } + public function render() + { + return view('livewire.ui.system.storage-card'); + } public function refresh(): void { @@ -36,41 +45,277 @@ class StorageCard extends Component $this->loadFromSettings(); } +// protected function loadFromSettings(): void +// { +// $disk = Setting::get('health.disk', []); +// if (!is_array($disk) || empty($disk)) { +// $this->resetUi(); +// return; +// } +// +// $this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null); +// $this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null); +// $this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null)); +// +// $percent = $disk['percent_used_total'] ?? null; +// $this->diskCenterText = [ +// 'percent' => is_numeric($percent) ? (string)round($percent) . '%' : '–', +// 'label' => 'SPEICHER BELEGT', +// ]; +// $this->diskSegments = $this->buildSegments(is_numeric($percent) ? (int)$percent : null); +// +// $this->barSegments = $this->buildBar($disk); +// $this->measuredAt = Setting::get('health.disk_updated_at', null); +// } + protected function loadFromSettings(): void { $disk = Setting::get('health.disk', []); - if (!is_array($disk) || empty($disk)) return; + if (!is_array($disk) || empty($disk)) { $this->resetUi(); return; } - $this->diskTotalGb = $disk['total_gb'] ?? null; - $this->diskUsedGb = $disk['used_gb'] ?? null; - $this->diskFreeGb = $disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null); + $this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null); + $this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null); + $this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null)); $percent = $disk['percent_used_total'] ?? null; $this->diskCenterText = [ - 'percent' => is_numeric($percent) ? $percent.'%' : '–', + 'percent' => is_numeric($percent) ? (string)round($percent).'%' : '–', 'label' => 'SPEICHER BELEGT', ]; - $this->diskSegments = $this->buildSegments($percent); + + // Donut farbig aus Breakdown + $this->diskSegments = $this->buildDonutSegmentsFromBreakdown($disk); + + // Legende unten (gleiche Farben wie Donut) + $total = max(1, (float)($disk['total_gb'] ?? 1)); + $bd = $disk['breakdown'] ?? []; + $sys = (float)($bd['system_gb'] ?? 0); + $mails = (float)($bd['mails_gb'] ?? 0); + $backup = (float)($bd['backup_gb'] ?? 0); + $free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0)); + $p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total))); + + $this->barSegments = [ + ['label' => 'System', 'gb' => round($sys,1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'], + ['label' => 'Mails', 'gb' => round($mails,1), 'percent' => $p($mails), 'color' => 'bg-rose-400'], + ['label' => 'Backups', 'gb' => round($backup,1),'percent' => $p($backup), 'color' => 'bg-sky-400'], + ['label' => 'Frei', 'gb' => round($free,1), 'percent' => $p($free), 'color' => 'bg-white/20'], + ]; + + $this->barSegments = array_values(array_filter( + $this->barSegments, + fn($b) => ($b['gb'] ?? 0) > 0 // nur Einträge mit realer Größe + )); $this->measuredAt = Setting::get('health.disk_updated_at', null); } - protected function buildSegments(?int $percent): array + // NEU: ersetzt buildSegments() + nutzt Breakdown + protected function buildDonutSegmentsFromBreakdown(array $disk): array { - $segments = []; - $active = is_int($percent) ? (int) round($this->segCount * $percent / 100) : 0; + $total = (float)($disk['total_gb'] ?? 0); + if ($total <= 0) return []; - $activeClass = match (true) { - !is_int($percent) => 'bg-white/15', - $percent >= 90 => 'bg-rose-400', - $percent >= 70 => 'bg-amber-300', - default => 'bg-emerald-400', + // Breakdown lesen + $bd = $disk['breakdown'] ?? []; + $sys = (float)($bd['system_gb'] ?? 0); + $mails = (float)($bd['mails_gb'] ?? 0); + $backup = (float)($bd['backup_gb'] ?? 0); + $free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0)); + + // Robust machen: falls used aus Settings größer ist als Breakdown-Summe → auf System draufschlagen + $usedReported = (float)($disk['used_gb'] ?? ($total - $free)); + $sumUsedBd = $sys + $mails + $backup; + if ($usedReported > $sumUsedBd && $usedReported <= $total) { + $sys += ($usedReported - $sumUsedBd); + } + // Grenzen + $sys = max(0, min($sys, $total)); + $mails = max(0, min($mails, $total)); + $backup = max(0, min($backup, $total)); + $free = max(0, min($free, $total)); + + // Segmente verteilen + $mkCount = function (float $gb) use ($total) { + return (int) round($this->segCount * $gb / $total); }; + $segments = []; + $add = function (int $count, string $class) use (&$segments) { + for ($i = 0; $i < $count; $i++) { + $segments[] = ['class' => $class]; + } + }; + + $add($mkCount($sys), 'bg-emerald-400'); // System + $add($mkCount($mails), 'bg-rose-400'); // Mails + $add($mkCount($backup), 'bg-sky-400'); // Backups + + // Rest = Frei (grau) + while (count($segments) < $this->segCount) { + $segments[] = ['class' => 'bg-white/15']; + } + + // Winkel setzen (gleich wie vorher) + $out = []; for ($i = 0; $i < $this->segCount; $i++) { $angle = (360 / $this->segCount) * $i - 90; - $segments[] = ['angle' => $angle, 'class' => $i < $active ? $activeClass : 'bg-white/15']; + $out[] = ['angle' => $angle, 'class' => $segments[$i]['class']]; } - return $segments; + return $out; + } + +// protected function buildSegments(?int $percent): array +// { +// $segments = []; +// $active = is_int($percent) ? (int)round($this->segCount * $percent / 100) : 0; +// +// $activeClass = match (true) { +// !is_int($percent) => 'bg-white/15', +// $percent >= 90 => 'bg-rose-400', +// $percent >= 70 => 'bg-amber-300', +// default => 'bg-emerald-400', +// }; +// +// for ($i = 0; $i < $this->segCount; $i++) { +// $angle = (360 / $this->segCount) * $i - 90; +// $segments[] = [ +// 'angle' => $angle, +// 'class' => $i < $active ? $activeClass : 'bg-white/15' +// ]; +// } +// return $segments; +// } + + protected function buildBar(array $disk): array + { + $total = (float)($disk['total_gb'] ?? 0); + if ($total <= 0) return []; + + // Breakdown lesen + normalisieren + [$sys, $mails, $backups] = $this->readBreakdown($disk); + + $free = max(0.0, (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0))); + $used = min($total, $sys + $mails + $backups); // robust bei Messrauschen + + // Falls Breakdown kleiner ist als used_gb: Rest als “System” draufschlagen, + // damit die Prozent-Summe 100 ergibt. + $usedReported = (float)($disk['used_gb'] ?? ($total - $free)); + if ($usedReported > 0 && $used < $usedReported) { + $sys += ($usedReported - $used); + $used = $usedReported; + } + + // Prozent berechnen + $p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total))); + + return [ + ['label' => 'System', 'gb' => round($sys, 1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'], + ['label' => 'Mails', 'gb' => round($mails, 1), 'percent' => $p($mails), 'color' => 'bg-rose-400'], + ['label' => 'Backups', 'gb' => round($backups, 1), 'percent' => $p($backups), 'color' => 'bg-sky-400'], + ['label' => 'Frei', 'gb' => round($free, 1), 'percent' => $p($free), 'color' => 'bg-white/20'], + ]; + } + + protected function readBreakdown(array $disk): array + { + // Deine Keys ggf. hier mappen + $bd = $disk['breakdown'] ?? []; + $sys = (float)($bd['system_gb'] ?? 0); + $mails = (float)($bd['mails_gb'] ?? 0); + $backup = (float)($bd['backup_gb'] ?? 0); + + return [$sys, $mails, $backup]; + } + + protected function resetUi(): void + { + $this->diskTotalGb = null; + $this->diskUsedGb = null; + $this->diskFreeGb = null; + $this->diskSegments = []; + $this->barSegments = []; + $this->diskCenterText = ['percent' => '–', 'label' => 'SPEICHER BELEGT']; + $this->measuredAt = null; + } + + protected static function intOrNull($v): ?int + { + return is_numeric($v) ? (int)round($v) : null; } } + +//namespace App\Livewire\Ui\System; +// +//use Livewire\Component; +//use Illuminate\Support\Facades\Artisan; +//use App\Models\Setting; +// +//class StorageCard extends Component +//{ +// public string $target = '/'; +// +// public ?int $diskTotalGb = null; +// public ?int $diskUsedGb = null; // inkl. Reserve (passt zum Donut) +// public ?int $diskFreeGb = null; // wird unten auf free_plus_reserve_gb gesetzt +// +// public array $diskSegments = []; +// public int $diskSegOuterRadius = 92; +// public int $diskInnerSize = 160; +// public array $diskCenterText = ['percent' => '–', 'label' => 'SPEICHER BELEGT']; +// public ?string $measuredAt = null; +// +// protected int $segCount = 48; +// +// public function mount(string $target = '/'): void +// { +// $this->target = $target ?: '/'; +// $this->loadFromSettings(); +// } +// +// public function render() { return view('livewire.ui.system.storage-card'); } +// +// public function refresh(): void +// { +// Artisan::call('health:probe-disk', ['target' => $this->target]); +// $this->loadFromSettings(); +// } +// +// protected function loadFromSettings(): void +// { +// $disk = Setting::get('health.disk', []); +// if (!is_array($disk) || empty($disk)) return; +// +// $this->diskTotalGb = $disk['total_gb'] ?? null; +// $this->diskUsedGb = $disk['used_gb'] ?? null; +// $this->diskFreeGb = $disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null); +// +// $percent = $disk['percent_used_total'] ?? null; +// $this->diskCenterText = [ +// 'percent' => is_numeric($percent) ? $percent.'%' : '–', +// 'label' => 'SPEICHER BELEGT', +// ]; +// $this->diskSegments = $this->buildSegments($percent); +// +// $this->measuredAt = Setting::get('health.disk_updated_at', null); +// } +// +// protected function buildSegments(?int $percent): array +// { +// $segments = []; +// $active = is_int($percent) ? (int) round($this->segCount * $percent / 100) : 0; +// +// $activeClass = match (true) { +// !is_int($percent) => 'bg-white/15', +// $percent >= 90 => 'bg-rose-400', +// $percent >= 70 => 'bg-amber-300', +// default => 'bg-emerald-400', +// }; +// +// for ($i = 0; $i < $this->segCount; $i++) { +// $angle = (360 / $this->segCount) * $i - 90; +// $segments[] = ['angle' => $angle, 'class' => $i < $active ? $activeClass : 'bg-white/15']; +// } +// return $segments; +// } +//} diff --git a/resources/views/livewire/ui/system/storage-card.blade.php b/resources/views/livewire/ui/system/storage-card.blade.php index db4c2d8..95b2f2c 100644 --- a/resources/views/livewire/ui/system/storage-card.blade.php +++ b/resources/views/livewire/ui/system/storage-card.blade.php @@ -1,4 +1,4 @@ -
+
{{-- Kopf --}}
@@ -12,14 +12,12 @@
{{-- Inhalt --}} -
+
{{-- Donut --}} -
+
- {{-- Innerer grauer Kreis --}}
- {{-- Prozentanzeige im Zentrum --}}
{{ $diskCenterText['percent'] }} @@ -27,41 +25,131 @@
{{ $diskCenterText['label'] }}
- @if($measuredAt) -
- zuletzt aktualisiert:
{{ \Carbon\Carbon::parse($measuredAt)->diffForHumans() }} -
- @endif + +{{-- @if($measuredAt)--}} +{{--
--}} +{{-- zuletzt aktualisiert:
{{ \Carbon\Carbon::parse($measuredAt)->diffForHumans() }}--}} +{{--
--}} +{{-- @endif--}}
- {{-- Segment-Ring --}} @foreach($diskSegments as $seg) + style="transform: rotate({{ $seg['angle'] }}deg) translateX({{ $diskSegOuterRadius + 14 }}px); + width: 12px; height: 3px; margin:-3px 0 0 -6px;"> @endforeach
- {{-- Zahlen --}} -
-
-
-
Gesamt
-
{{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '–' }}
+ {{-- Zahlen + Stacked Bar --}} +
+ {{-- Stacked-Bar (horizontale Leiste) --}} + @if(!empty($barSegments)) +
+{{--
--}} +{{--
--}} +{{-- @foreach($barSegments as $b)--}} +{{--
--}} +{{-- @endforeach--}} +{{--
--}} +{{--
--}} + + {{-- Legende --}} +
+ @foreach($barSegments as $b) +
+ + {{ $b['label'] }} {{ $b['gb'] }} GB ({{ $b['percent'] }}%) +
+ @endforeach +
-
-
Genutzt
-
{{ is_numeric($diskUsedGb) ? $diskUsedGb.' GB' : '–' }}
-
-
-
Frei
-
{{ is_numeric($diskFreeGb) ? $diskFreeGb.' GB' : '–' }}
-
-
+ @endif + + {{-- Zahlen unten rechts --}} +{{--
--}} +{{--
--}} +{{--
Gesamt
--}} +{{--
{{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}} +{{--
Genutzt
--}} +{{--
{{ is_numeric($diskUsedGb) ? $diskUsedGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}} +{{--
Frei
--}} +{{--
{{ is_numeric($diskFreeGb) ? $diskFreeGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}}
+{{--
--}} +{{-- --}}{{-- Kopf --}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- Storage--}} +{{--
--}} +{{-- --}} +{{--
--}} + +{{-- --}}{{-- Inhalt --}} +{{--
--}} +{{-- --}}{{-- Donut --}} +{{--
--}} +{{--
--}} +{{-- --}}{{-- Innerer grauer Kreis --}} +{{--
--}} + +{{-- --}}{{-- Prozentanzeige im Zentrum --}} +{{--
--}} +{{--
--}} +{{-- {{ $diskCenterText['percent'] }}--}} +{{--
--}} +{{--
--}} +{{-- {{ $diskCenterText['label'] }}--}} +{{--
--}} +{{-- @if($measuredAt)--}} +{{--
--}} +{{-- zuletzt aktualisiert:
{{ \Carbon\Carbon::parse($measuredAt)->diffForHumans() }}--}} +{{--
--}} +{{-- @endif--}} +{{--
--}} + +{{-- --}}{{-- Segment-Ring --}} +{{-- @foreach($diskSegments as $seg)--}} +{{-- --}} +{{-- --}} +{{-- --}} +{{-- @endforeach--}} +{{--
--}} +{{--
--}} + +{{-- --}}{{-- Zahlen --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Gesamt
--}} +{{--
{{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}} +{{--
Genutzt
--}} +{{--
{{ is_numeric($diskUsedGb) ? $diskUsedGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}} +{{--
Frei
--}} +{{--
{{ is_numeric($diskFreeGb) ? $diskFreeGb.' GB' : '–' }}
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}}