'–', 'label' => 'SPEICHER BELEGT']; // Stacked-Bar + Legende public array $barSegments = []; // [{label,color,gb,percent}] public ?string $measuredAt = null; protected int $segCount = 100; 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)) { // $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)) { $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', ]; // 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); } // NEU: ersetzt buildSegments() + nutzt Breakdown protected function buildDonutSegmentsFromBreakdown(array $disk): array { $total = (float)($disk['total_gb'] ?? 0); if ($total <= 0) return []; // 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; $out[] = ['angle' => $angle, 'class' => $segments[$i]['class']]; } 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; // } //}