Enhance Proxmox views with improved loading states and UI updates

- Updated serverplaceholder.blade.php to enhance loading skeletons for server details.
- Added navigation breadcrumbs to statistics, subdomain, and system views for better user experience.
- Improved loading indicators for CPU, network, memory, and disk charts in statistics.blade.php.
- Refined storage.blade.php layout with section dividers and improved drive display, including bus type indicators and quota usage.
- Enhanced VNC and Xterm popups with a new header design, loading animations, and reconnect functionality.
- Added error handling for VNC and Xterm connections to inform users when unable to connect.
This commit is contained in:
2026-02-24 19:51:46 -05:00
parent af62a16354
commit 16cd5f05de
40 changed files with 1423 additions and 475 deletions

View File

@@ -1,37 +1,4 @@
<x-proxmox::layout :server="$server" view="storage" :status="$status" :status-polling="true" :status-polling-interval="'2s'">
<style>
/* Hide sidebar in problematic resolution range (768px - 1023px) */
@media screen and (min-width: 768px) and (max-width: 1023px) {
html body > div.w-full.flex.flex-grow > aside.w-64.h-screen,
html body > div.w-full.flex > aside.w-64.h-screen,
html body aside.w-64.h-screen.mt-14,
html body aside.w-64.h-screen.mt-24,
body aside.w-64[class*="md:flex"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
width: 0 !important;
max-width: 0 !important;
overflow: hidden !important;
position: absolute !important;
left: -9999px !important;
}
html body > div.w-full.flex.flex-grow > div[class*="md:ml-64"],
html body > div.w-full.flex > div[class*="md:ml-64"],
body > div > div[class*="md:ml-64"],
div[class*="md:ml-64"] {
margin-left: 0 !important;
}
html body > div.w-full.flex.flex-grow > div[class*="rtl:md:mr-64"],
html body > div.w-full.flex > div[class*="rtl:md:mr-64"],
body > div > div[class*="rtl:md:mr-64"],
div[class*="rtl:md:mr-64"] {
margin-right: 0 !important;
}
}
</style>
@php
$vmRunning = $status && $status->status !== 'stopped';
@endphp
@@ -181,6 +148,9 @@
</div>
</div>
<!-- Section Divider -->
<div class="border-t border-neutral/20"></div>
<!-- SCSI Controller Configuration -->
<div class="bg-background-secondary border border-neutral rounded-lg p-5 space-y-4">
<div class="flex items-center justify-between">
@@ -269,6 +239,9 @@
@endif
</div>
<!-- Section Divider -->
<div class="border-t border-neutral/20"></div>
<!-- Drives List -->
<div class="bg-background-secondary border border-neutral rounded-lg overflow-hidden">
<div class="p-5 border-b border-neutral">
@@ -277,30 +250,55 @@
</div>
@if(count($drives) === 0)
<div class="p-8 text-center">
<x-ri-hard-drive-2-line class="size-12 mx-auto text-base/30 mb-3" />
<p class="text-base/60">No drives attached to this VM.</p>
<div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex items-center justify-center w-16 h-16 rounded-2xl bg-base/5 mb-4">
<x-ri-hard-drive-2-line class="size-8 text-base/20" />
</div>
<h3 class="text-sm font-semibold text-base/70 mb-1">No drives attached</h3>
<p class="text-xs text-base/50 max-w-sm">Virtual hard disks store your operating system, applications, and data. Add a drive to get started.</p>
@if($this->remainingStorage > 0 && !$vmRunning)
<button wire:click="openAddDriveModal" class="mt-4 text-primary hover:text-primary/80 text-sm font-medium">
Add your first drive →
<button wire:click="openAddDriveModal" class="mt-4 inline-flex items-center gap-1.5 text-primary hover:text-primary/80 text-sm font-medium transition-colors">
<x-ri-add-line class="size-4" />
Add your first drive
</button>
@elseif($this->remainingStorage > 0 && $vmRunning)
<p class="mt-4 text-base/50 text-sm">Power off server to add drives</p>
<p class="mt-3 text-base/50 text-xs">Power off server to add drives</p>
@endif
</div>
@else
<div class="divide-y divide-neutral">
@foreach($drives as $driveKey => $drive)
<div wire:key="drive-{{ $driveKey }}" class="p-4 hover:bg-background/50 transition-colors">
<div wire:key="drive-{{ $driveKey }}" class="group p-4 hover:bg-background/50 transition-all duration-300 hover:shadow-sm">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div class="flex items-start gap-4">
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10 flex-shrink-0">
<x-ri-hard-drive-2-fill class="size-5 text-primary" />
@if($drive['bus'] === 'scsi')
<x-ri-database-2-line class="size-5 text-primary" />
@elseif($drive['bus'] === 'virtio')
<x-ri-speed-line class="size-5 text-primary" />
@elseif($drive['bus'] === 'sata')
<x-ri-hard-drive-2-fill class="size-5 text-primary" />
@elseif($drive['bus'] === 'ide')
<x-ri-hard-drive-line class="size-5 text-primary" />
@else
<x-ri-hard-drive-2-fill class="size-5 text-primary" />
@endif
</div>
<div class="min-w-0">
<div class="flex items-center gap-2 flex-wrap">
<h4 class="font-semibold text-base">{{ $drive['label'] }}</h4>
<span class="px-2 py-0.5 text-xs font-medium rounded bg-neutral/30 text-base/70">{{ $driveKey }}</span>
@php
$busLabel = $busTypes[$drive['bus']]['label'] ?? strtoupper($drive['bus']);
$busColor = match($drive['bus']) {
'scsi' => 'bg-primary/15 text-primary',
'virtio' => 'bg-success/15 text-success',
'sata' => 'bg-info/15 text-info',
'ide' => 'bg-warning/15 text-warning',
default => 'bg-neutral/20 text-base/70',
};
@endphp
<span class="px-2 py-0.5 text-xs font-medium rounded {{ $busColor }}">{{ $busLabel }}</span>
@if($drive['ssd'])
<span class="px-2 py-0.5 text-xs font-medium rounded bg-success/20 text-success">SSD</span>
@endif
@@ -308,6 +306,18 @@
<span class="px-2 py-0.5 text-xs font-medium rounded bg-info/20 text-info">IO Thread</span>
@endif
</div>
<!-- Drive size relative to quota -->
@if($maxAllowedStorage > 0)
<div class="w-full max-w-[200px] mt-2">
<div class="w-full bg-neutral/20 rounded-full h-1.5 overflow-hidden">
@php
$drivePercent = min(100, ($drive['size'] / $maxAllowedStorage) * 100);
@endphp
<div class="bg-primary/60 h-1.5 rounded-full transition-all duration-500" style="width: {{ $drivePercent }}%"></div>
</div>
<p class="text-[10px] text-base/40 mt-0.5">{{ number_format($drivePercent, 1) }}% of quota</p>
</div>
@endif
<div class="flex flex-wrap gap-2 mt-2">
<span class="px-2 py-0.5 text-xs font-medium rounded bg-neutral/20 text-foreground-secondary">{{ number_format($drive['size'], 1) }} GB</span>
<span class="px-2 py-0.5 text-xs font-medium rounded bg-neutral/20 text-foreground-secondary">Cache: {{ ucfirst($drive['cache']) }}</span>
@@ -316,6 +326,15 @@
<span class="px-2 py-0.5 text-xs font-medium rounded bg-success/20 text-success">Discard</span>
@endif
</div>
<!-- Hover-reveal extra details -->
<div class="opacity-0 group-hover:opacity-100 max-h-0 group-hover:max-h-40 transition-all duration-300 overflow-hidden">
<div class="flex flex-wrap gap-x-4 gap-y-1 mt-2 pt-2 border-t border-neutral/10 text-[11px] text-base/50">
<span>Bus: <strong class="text-base/70">{{ strtoupper($drive['bus']) }}{{ $drive['bus_index'] }}</strong></span>
<span>SSD Emulation: <strong class="text-base/70">{{ $drive['ssd'] ? 'Yes' : 'No' }}</strong></span>
<span>IO Thread: <strong class="text-base/70">{{ $drive['iothread'] ? 'Enabled' : 'Disabled' }}</strong></span>
<span>Discard: <strong class="text-base/70">{{ $drive['discard'] ? 'On' : 'Off' }}</strong></span>
</div>
</div>
</div>
</div>