Add storage management functionality to Proxmox extension

- Registered Livewire component for storage management.
- Updated routes to include storage management endpoint.
- Modified layout to add storage management link.
- Created storage management view with features to add, edit, and manage drives.
- Implemented modals for adding and editing drives with appropriate validations and options.
This commit is contained in:
2025-12-05 04:11:20 -05:00
parent 1a942c5975
commit 061ec6145c
5 changed files with 1467 additions and 0 deletions

View File

@@ -0,0 +1,594 @@
<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>
<div class="space-y-6">
<!-- Page Header -->
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2 min-w-0">
<h2 class="text-xl font-semibold break-words">Storage Management</h2>
<div class="relative" x-data="{ open: false }" x-cloak>
<button type="button" @click="open = true" class="text-base/50 hover:text-base/70 transition-colors">
<x-ri-question-line class="size-4" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="flex items-center gap-3 mb-4 pr-8">
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
<x-ri-hard-drive-2-line class="size-5 text-primary" />
</div>
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
Storage Management
</h6>
</div>
<p class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
Manage the storage drives attached to your VPS. You can add, remove, and configure drives with different bus types, cache modes, and performance options.
</p>
<div class="pt-4 mt-4 border-t border-neutral/30 space-y-2.5">
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
<span class="font-semibold text-base/90">Bus Types:</span> SCSI (VirtIO SCSI) offers the best performance with IO thread support. VirtIO Block is also high-performance. IDE and SATA are available for compatibility.
</p>
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
<span class="font-semibold text-base/90">Important:</span> The VM must be stopped to add, remove, or move drives. Total storage cannot exceed your plan allocation.
</p>
</div>
</div>
</div>
</div>
</div>
<p class="text-sm text-base/60">Manage virtual hard disks attached to your VPS. Add, configure, or remove storage drives.</p>
</div>
<!-- Storage Usage Overview -->
<div class="bg-background-secondary border border-neutral rounded-lg p-5">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-4">
<div>
<h3 class="text-lg font-semibold">Storage Quota</h3>
<p class="text-sm text-base/60 mt-1">
{{ number_format($totalAllocatedStorage, 1) }} GB used of {{ number_format($maxAllowedStorage, 1) }} GB allocated
</p>
</div>
<div class="flex items-center gap-2">
<button wire:click="loadStorageConfiguration"
class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
<x-ri-refresh-line class="size-4" wire:loading.class="animate-spin" wire:target="loadStorageConfiguration" />
Refresh
</button>
@if($this->remainingStorage > 0)
<button wire:click="openAddDriveModal"
class="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors">
<x-ri-add-line class="size-4" />
Add Drive
</button>
@endif
</div>
</div>
<!-- Progress Bar -->
<div class="w-full bg-neutral/30 rounded-full h-3 overflow-hidden">
@php
$usagePercent = $maxAllowedStorage > 0 ? min(100, ($totalAllocatedStorage / $maxAllowedStorage) * 100) : 0;
$colorClass = $usagePercent < 70 ? 'bg-success' : ($usagePercent < 90 ? 'bg-warning' : 'bg-danger');
@endphp
<div class="{{ $colorClass }} h-3 rounded-full transition-all duration-500" style="width: {{ $usagePercent }}%"></div>
</div>
<div class="flex justify-between mt-2 text-xs text-base/50">
<span>{{ number_format($this->remainingStorage, 1) }} GB remaining</span>
<span>{{ number_format($usagePercent, 1) }}% used</span>
</div>
</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">
<div>
<div class="flex items-center gap-2">
<h3 class="text-lg font-semibold">SCSI Controller</h3>
<div class="relative" x-data="{ open: false }" x-cloak>
<button type="button" @click="open = true" class="text-base/50 hover:text-base/70 transition-colors">
<x-ri-question-line class="size-4" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="flex items-center gap-3 mb-4 pr-8">
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
<x-ri-cpu-line class="size-5 text-primary" />
</div>
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
SCSI Controller Types
</h6>
</div>
<div class="space-y-3 text-sm text-base/90">
<p><strong>VirtIO SCSI / VirtIO SCSI Single:</strong> Best performance with paravirtualized drivers. Recommended for Linux and modern Windows.</p>
<p><strong>LSI 53C895A / LSI 53C810:</strong> Emulated LSI controllers with wide compatibility.</p>
<p><strong>MegaRAID SAS 8708EM2:</strong> Emulated MegaRAID controller.</p>
<p><strong>VMware PVSCSI:</strong> VMware paravirtualized controller, useful for VMs migrated from VMware.</p>
</div>
</div>
</div>
</div>
</div>
<p class="text-sm text-base/60 mt-1">Select the SCSI controller type for SCSI drives.</p>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-4">
<div class="flex-1">
<select wire:model.live="scsiControllerType"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($scsiControllerOptions as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</div>
<button wire:click="saveScsiController"
wire:loading.attr="disabled"
class="inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-primary text-white hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
@if($this->scsiControllerSynced) disabled @endif>
<x-loading target="saveScsiController" />
<span wire:loading.remove wire:target="saveScsiController">Save Changes</span>
<span wire:loading wire:target="saveScsiController">Saving...</span>
</button>
</div>
@if($status && $status->status !== 'stopped' && !$this->scsiControllerSynced)
<div class="flex items-center gap-2 p-3 rounded-lg bg-warning/10 border border-warning/30 text-warning text-sm">
<x-ri-alert-line class="size-4 flex-shrink-0" />
<span>VM must be stopped to change the SCSI controller type.</span>
</div>
@endif
</div>
<!-- Drives List -->
<div class="bg-background-secondary border border-neutral rounded-lg overflow-hidden">
<div class="p-5 border-b border-neutral">
<h3 class="text-lg font-semibold">Attached Drives</h3>
<p class="text-sm text-base/60 mt-1">Manage the virtual hard disks connected to your VPS.</p>
</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>
@if($this->remainingStorage > 0)
<button wire:click="openAddDriveModal" class="mt-4 text-primary hover:text-primary/80 text-sm font-medium">
Add your first drive →
</button>
@endif
</div>
@else
<div class="divide-y divide-neutral">
@foreach($drives as $driveKey => $drive)
<div class="p-4 hover:bg-background/50 transition-colors">
<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" />
</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>
@if($drive['ssd'])
<span class="px-2 py-0.5 text-xs font-medium rounded bg-success/20 text-success">SSD</span>
@endif
@if($drive['iothread'])
<span class="px-2 py-0.5 text-xs font-medium rounded bg-info/20 text-info">IO Thread</span>
@endif
</div>
<div class="flex flex-wrap gap-x-4 gap-y-1 mt-1 text-xs text-base/60">
<span>{{ number_format($drive['size'], 1) }} GB</span>
<span>Cache: {{ ucfirst($drive['cache']) }}</span>
<span>AIO: {{ $drive['aio'] }}</span>
@if($drive['discard'])
<span class="text-success">Discard enabled</span>
@endif
</div>
</div>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<button wire:click="openEditDriveModal('{{ $driveKey }}')"
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
<x-ri-settings-3-line class="size-3.5" />
Settings
</button>
<button wire:click="openMoveDriveModal('{{ $driveKey }}')"
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
<x-ri-arrow-left-right-line class="size-3.5" />
Move
</button>
<button wire:click="detachDrive('{{ $driveKey }}')"
wire:confirm="Are you sure you want to detach this drive? The data will be preserved but the drive will no longer be accessible."
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-warning/50 bg-warning/10 text-warning hover:bg-warning/20 transition-colors">
<x-ri-eject-line class="size-3.5" />
Detach
</button>
<button wire:click="deleteDrive('{{ $driveKey }}')"
wire:confirm="Are you sure you want to DELETE this drive? This action cannot be undone and all data will be permanently lost!"
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-danger/50 bg-danger/10 text-danger hover:bg-danger/20 transition-colors">
<x-ri-delete-bin-line class="size-3.5" />
Delete
</button>
</div>
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
<!-- Add Drive Modal -->
@if($showAddDriveModal)
<div class="fixed inset-0 z-50 overflow-y-auto" x-data x-init="$el.querySelector('input')?.focus()">
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm" wire:click="closeAddDriveModal"></div>
<div class="relative min-h-screen flex items-center justify-center p-4">
<div class="relative w-full max-w-lg bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl">
<div class="flex items-center justify-between p-5 border-b border-neutral">
<h3 class="text-lg font-semibold">Add New Drive</h3>
<button wire:click="closeAddDriveModal" class="text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
</div>
<div class="p-5 space-y-4">
<!-- Bus Type -->
<div>
<label class="block text-sm font-medium mb-2">Bus/Device Type</label>
<select wire:model.live="newDriveBus"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($busTypes as $busKey => $busConfig)
<option value="{{ $busKey }}">{{ $busConfig['label'] }} (max {{ $busConfig['max'] }} drives)</option>
@endforeach
</select>
<p class="text-xs text-base/50 mt-1">
@if($newDriveBus === 'scsi')
SCSI provides best performance with IO thread support. Recommended for most use cases.
@elseif($newDriveBus === 'virtio')
VirtIO Block offers high performance but does not support SSD emulation.
@elseif($newDriveBus === 'sata')
SATA provides good compatibility with most operating systems.
@else
IDE is primarily for legacy compatibility.
@endif
</p>
</div>
<!-- Size -->
<div>
<label class="block text-sm font-medium mb-2">Size (GB)</label>
<input type="number"
wire:model="newDriveSize"
min="1"
max="{{ $this->remainingStorage }}"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
<p class="text-xs text-base/50 mt-1">Maximum available: {{ number_format($this->remainingStorage, 1) }} GB</p>
</div>
<!-- Cache Mode -->
<div>
<label class="block text-sm font-medium mb-2">Cache Mode</label>
<select wire:model="newDriveCache"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($cacheOptions as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</div>
<!-- Async IO -->
<div>
<label class="block text-sm font-medium mb-2">Async IO Type</label>
<select wire:model="newDriveAio"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($aioOptions as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</div>
<!-- Options Row -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<!-- SSD Emulation -->
<label class="flex items-center gap-2 cursor-pointer {{ !$this->newDriveSsdSupported ? 'opacity-50' : '' }}">
<input type="checkbox"
wire:model="newDriveSsd"
class="rounded border-neutral text-primary focus:ring-primary"
@if(!$this->newDriveSsdSupported) disabled @endif>
<span class="text-sm">SSD Emulation</span>
</label>
<!-- IO Thread -->
<label class="flex items-center gap-2 cursor-pointer {{ !$this->newDriveIothreadSupported ? 'opacity-50' : '' }}">
<input type="checkbox"
wire:model="newDriveIothread"
class="rounded border-neutral text-primary focus:ring-primary"
@if(!$this->newDriveIothreadSupported) disabled @endif>
<span class="text-sm">IO Thread</span>
</label>
<!-- Discard/TRIM -->
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox"
wire:model="newDriveDiscard"
class="rounded border-neutral text-primary focus:ring-primary">
<span class="text-sm">Discard (TRIM)</span>
</label>
</div>
@if($status && $status->status !== 'stopped')
<div class="flex items-center gap-2 p-3 rounded-lg bg-warning/10 border border-warning/30 text-warning text-sm">
<x-ri-alert-line class="size-4 flex-shrink-0" />
<span>VM must be stopped to add new drives.</span>
</div>
@endif
</div>
<div class="flex justify-end gap-3 p-5 border-t border-neutral">
<button wire:click="closeAddDriveModal"
class="px-4 py-2 text-sm font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
Cancel
</button>
<button wire:click="addDrive"
wire:loading.attr="disabled"
class="px-4 py-2 text-sm font-medium rounded-lg bg-primary text-white hover:bg-primary/90 disabled:opacity-50 transition-colors"
@if($status && $status->status !== 'stopped') disabled @endif>
<x-loading target="addDrive" />
<span wire:loading.remove wire:target="addDrive">Add Drive</span>
<span wire:loading wire:target="addDrive">Adding...</span>
</button>
</div>
</div>
</div>
</div>
@endif
<!-- Edit Drive Modal -->
@if($showEditDriveModal && $editingDriveKey && isset($drives[$editingDriveKey]))
@php $editDrive = $drives[$editingDriveKey]; @endphp
<div class="fixed inset-0 z-50 overflow-y-auto">
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm" wire:click="closeEditDriveModal"></div>
<div class="relative min-h-screen flex items-center justify-center p-4">
<div class="relative w-full max-w-lg bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl">
<div class="flex items-center justify-between p-5 border-b border-neutral">
<h3 class="text-lg font-semibold">Edit Drive Settings - {{ $editDrive['label'] }}</h3>
<button wire:click="closeEditDriveModal" class="text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
</div>
<div class="p-5 space-y-4">
<div class="p-3 rounded-lg bg-neutral/20 text-sm">
<div class="grid grid-cols-2 gap-2">
<div><span class="text-base/50">Device:</span> {{ $editingDriveKey }}</div>
<div><span class="text-base/50">Size:</span> {{ number_format($editDrive['size'], 1) }} GB</div>
<div><span class="text-base/50">Bus Type:</span> {{ $busTypes[$editDrive['bus']]['label'] ?? ucfirst($editDrive['bus']) }}</div>
</div>
</div>
<!-- Cache Mode -->
<div>
<label class="block text-sm font-medium mb-2">Cache Mode</label>
<select wire:model="editDriveCache"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($cacheOptions as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</div>
<!-- Async IO -->
<div>
<label class="block text-sm font-medium mb-2">Async IO Type</label>
<select wire:model="editDriveAio"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($aioOptions as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</div>
<!-- Options Row -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<!-- SSD Emulation -->
<label class="flex items-center gap-2 cursor-pointer {{ !$this->editDriveSsdSupported ? 'opacity-50' : '' }}">
<input type="checkbox"
wire:model="editDriveSsd"
class="rounded border-neutral text-primary focus:ring-primary"
@if(!$this->editDriveSsdSupported) disabled @endif>
<span class="text-sm">SSD Emulation</span>
</label>
<!-- IO Thread -->
<label class="flex items-center gap-2 cursor-pointer {{ !$this->editDriveIothreadSupported ? 'opacity-50' : '' }}">
<input type="checkbox"
wire:model="editDriveIothread"
class="rounded border-neutral text-primary focus:ring-primary"
@if(!$this->editDriveIothreadSupported) disabled @endif>
<span class="text-sm">IO Thread</span>
</label>
<!-- Discard/TRIM -->
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox"
wire:model="editDriveDiscard"
class="rounded border-neutral text-primary focus:ring-primary">
<span class="text-sm">Discard (TRIM)</span>
</label>
</div>
@if(!$this->editDriveSsdSupported)
<p class="text-xs text-base/50">Note: SSD emulation is not available for {{ $busTypes[$editDrive['bus']]['label'] ?? ucfirst($editDrive['bus']) }} drives.</p>
@endif
@if(!$this->editDriveIothreadSupported)
<p class="text-xs text-base/50">Note: IO Thread is not available for {{ $busTypes[$editDrive['bus']]['label'] ?? ucfirst($editDrive['bus']) }} drives.</p>
@endif
</div>
<div class="flex justify-end gap-3 p-5 border-t border-neutral">
<button wire:click="closeEditDriveModal"
class="px-4 py-2 text-sm font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
Cancel
</button>
<button wire:click="saveDriveSettings"
wire:loading.attr="disabled"
class="px-4 py-2 text-sm font-medium rounded-lg bg-primary text-white hover:bg-primary/90 disabled:opacity-50 transition-colors">
<x-loading target="saveDriveSettings" />
<span wire:loading.remove wire:target="saveDriveSettings">Save Changes</span>
<span wire:loading wire:target="saveDriveSettings">Saving...</span>
</button>
</div>
</div>
</div>
</div>
@endif
<!-- Move Drive Modal -->
@if($showMoveDriveModal && $movingDriveKey && isset($drives[$movingDriveKey]))
@php $moveDrive = $drives[$movingDriveKey]; @endphp
<div class="fixed inset-0 z-50 overflow-y-auto">
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm" wire:click="closeMoveDriveModal"></div>
<div class="relative min-h-screen flex items-center justify-center p-4">
<div class="relative w-full max-w-lg bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl">
<div class="flex items-center justify-between p-5 border-b border-neutral">
<h3 class="text-lg font-semibold">Move Drive - {{ $moveDrive['label'] }}</h3>
<button wire:click="closeMoveDriveModal" class="text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
</div>
<div class="p-5 space-y-4">
<div class="p-3 rounded-lg bg-neutral/20 text-sm">
<p><span class="text-base/50">Current Device:</span> {{ $movingDriveKey }} ({{ $busTypes[$moveDrive['bus']]['label'] ?? ucfirst($moveDrive['bus']) }})</p>
<p class="mt-1"><span class="text-base/50">Size:</span> {{ number_format($moveDrive['size'], 1) }} GB</p>
</div>
<div class="flex items-center gap-2 p-3 rounded-lg bg-info/10 border border-info/30 text-info text-sm">
<x-ri-information-line class="size-4 flex-shrink-0" />
<span>Moving a drive changes its bus type. This may require reinstalling drivers in the guest OS.</span>
</div>
<!-- Target Bus Type -->
<div>
<label class="block text-sm font-medium mb-2">Target Bus Type</label>
<select wire:model="moveDriveTargetBus"
class="w-full px-3 py-2 text-sm rounded-lg border border-neutral bg-background focus:border-primary focus:ring-1 focus:ring-primary transition-colors">
@foreach($busTypes as $busKey => $busConfig)
<option value="{{ $busKey }}">{{ $busConfig['label'] }}</option>
@endforeach
</select>
</div>
@if($status && $status->status !== 'stopped')
<div class="flex items-center gap-2 p-3 rounded-lg bg-warning/10 border border-warning/30 text-warning text-sm">
<x-ri-alert-line class="size-4 flex-shrink-0" />
<span>VM must be stopped to move drives.</span>
</div>
@endif
</div>
<div class="flex justify-end gap-3 p-5 border-t border-neutral">
<button wire:click="closeMoveDriveModal"
class="px-4 py-2 text-sm font-medium rounded-lg border border-neutral bg-background hover:bg-background-secondary transition-colors">
Cancel
</button>
<button wire:click="moveDrive"
wire:loading.attr="disabled"
class="px-4 py-2 text-sm font-medium rounded-lg bg-primary text-white hover:bg-primary/90 disabled:opacity-50 transition-colors"
@if($status && $status->status !== 'stopped') disabled @endif>
<x-loading target="moveDrive" />
<span wire:loading.remove wire:target="moveDrive">Move Drive</span>
<span wire:loading wire:target="moveDrive">Moving...</span>
</button>
</div>
</div>
</div>
</div>
@endif
</x-proxmox::layout>