refactor: update resource allocation and server details layout for improved styling and usability

This commit is contained in:
2025-12-17 18:13:30 -05:00
parent 9971cee9d0
commit 054eae7932

View File

@@ -311,293 +311,358 @@
$lastSampleTimestamp = now()->toIso8601String();
@endphp
<x-proxmox::ui.card class="mt-4">
<div class="flex flex-col gap-1 md:flex-row md:items-center md:justify-between mb-4">
<div class="flex items-center gap-2">
<h3 class="text-base font-semibold text-gray-800 dark:text-gray-100">Resource Allocation</h3>
<x-proxmox::ui.help-modal title="Resource Allocation">
<p class="text-sm text-gray-700 dark:text-gray-300 mb-3">
VM resource consumption vs allocated entitlements.
</p>
<div class="pt-3 border-t border-gray-200 dark:border-gray-700 space-y-1.5 text-xs text-gray-600 dark:text-gray-400">
<p><span class="font-semibold text-gray-700 dark:text-gray-300">CPU:</span> Live usage % of vCPU entitlement.</p>
<p><span class="font-semibold text-gray-700 dark:text-gray-300">Memory:</span> RAM usage vs provisioned.</p>
<p><span class="font-semibold text-gray-700 dark:text-gray-300">Uptime:</span> Live uptime + power events.</p>
<p><span class="font-semibold text-gray-700 dark:text-gray-300">Badges:</span> Healthy (green), Watchlist (yellow 85%+), Over (red).</p>
<!-- Resource Allocation Cards -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.25rem; margin-top: 1.5rem;">
@foreach($resourceAllocations as $resource)
<div class="lte-card" style="margin-bottom: 0; overflow: hidden;">
<div class="lte-card-body" style="padding: 1.5rem;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;">
<h5 style="font-size: 0.875rem; font-weight: 600; margin: 0; text-transform: uppercase; letter-spacing: 0.05em;" class="lte-text-muted">{{ $resource['label'] }}</h5>
@if(isset($resource['badge']))
<span class="lte-badge lte-badge-{{ $resource['badge']['label'] === 'Healthy' ? 'success' : ($resource['badge']['label'] === 'Watchlist' ? 'warning' : 'danger') }}" style="font-size: 0.7rem; padding: 0.25rem 0.5rem;">
{{ $resource['badge']['label'] }}
</span>
@endif
</div>
<div style="margin-bottom: 1rem;">
<div style="font-size: 1.75rem; font-weight: 700; line-height: 1; margin-bottom: 0.25rem;">
{{ $resource['usage_summary'] }}
</div>
</x-proxmox::ui.help-modal>
<span class="text-xs text-gray-500 dark:text-gray-400 ml-2 hidden md:inline">Live refresh</span>
<p class="lte-text-muted" style="font-size: 0.75rem; margin: 0;">{{ $resource['entitlement_label'] }}</p>
</div>
@if($resource['used_percent'] !== null)
<div style="margin-bottom: 0.75rem;">
<div class="lte-progress" style="height: 8px; border-radius: 10px;">
<div class="lte-progress-bar" role="progressbar"
style="width: {{ min(100, $resource['used_percent']) }}%; background: {{ $resource['gradient'] }}; border-radius: 10px;"
aria-valuenow="{{ $resource['used_percent'] }}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<div style="display: flex; justify-content: space-between; margin-top: 0.375rem;">
<span class="lte-text-muted" style="font-size: 0.75rem;">{{ number_format($resource['used_percent'], 1) }}% used</span>
@if($resource['used_percent'] > 100)
<span class="lte-text-danger" style="font-size: 0.75rem; font-weight: 600;">Over entitlement</span>
@endif
</div>
</div>
@endif
<p class="lte-text-muted" style="font-size: 0.75rem; margin: 0; line-height: 1.4;">
<x-ri-information-line class="size-3" style="display: inline; vertical-align: text-top; margin-right: 0.25rem;" />
{{ $resource['description'] }}
</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3">
@foreach($resourceAllocations as $resource)
<x-proxmox::ui.allocation-card
:label="$resource['label']"
:percent="$resource['used_percent']"
:usage-summary="$resource['usage_summary']"
:entitlement-label="$resource['entitlement_label']"
:description="$resource['description']"
:gradient="$resource['gradient']"
:badge="$resource['badge']"
/>
@endforeach
</div>
</x-proxmox::ui.card>
@endforeach
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4 md:items-stretch">
<div class="grid gap-4 items-start">
<!-- Details -->
<div>
<div>
<div style="background-image: url('https://billing.virtnet.bond/assets/images/main.png'); background-size: cover; height: 3rem;" class="rounded-t-lg border-t border-x border-gray-200 dark:border-gray-700"></div>
<!-- Status Section -->
<div class="bg-white dark:bg-gray-800 border-x border-gray-200 dark:border-gray-700 p-3 space-y-3">
<!-- Status -->
<div class="flex items-center justify-between">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">Status</label>
<div class="flex items-center gap-1.5 text-sm">
<div class="relative inline-flex size-4">
@if ($status && $status->status === 'running')
<span class="absolute inset-0 rounded-full bg-green-500 animate-ping opacity-75"></span>
<span class="relative flex items-center justify-center size-4 rounded-full text-green-600 bg-green-100 dark:bg-green-900">
<x-ri-circle-fill class="size-2" />
</span>
@else
<span class="flex items-center justify-center size-4 rounded-full text-amber-600 bg-amber-100 dark:bg-amber-900">
<x-ri-error-warning-fill class="size-2" />
</span>
@endif
</div>
<span class="font-medium text-gray-800 dark:text-gray-200">
{{ $status && $status->status === 'running' ? 'Running' : 'Stopped' }}
<!-- Main Content Grid -->
<div style="display: grid; grid-template-columns: 1fr; gap: 1.5rem; margin-top: 1.5rem;">
@media (min-width: 768px) {
<style>
.overview-grid { grid-template-columns: 1fr 1fr; }
</style>
}
</div>
<div class="overview-grid" style="display: grid; grid-template-columns: 1fr; gap: 1.5rem; margin-top: 1.5rem;">
<!-- Server Details Card -->
<div class="lte-card lte-card-outline-primary">
<div class="lte-card-header" style="background: linear-gradient(135deg, var(--lte-primary), var(--lte-info)); color: white; padding: 1rem 1.25rem;">
<div class="lte-card-title" style="display: flex; align-items: center; gap: 0.75rem; margin: 0; font-weight: 600;">
<x-ri-server-fill class="size-5" />
<span>Server Information</span>
</div>
</div>
<div class="lte-card-body" style="padding: 0;">
<div style="background-image: url('https://billing.virtnet.bond/assets/images/main.png'); background-size: cover; height: 100px; border-bottom: 1px solid rgba(0,0,0,0.1);"></div>
<div style="padding: 1.25rem;">
<!-- Status -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid rgba(0,0,0,0.05);">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-pulse-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">Status</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
@if ($status && $status->status === 'running')
<span class="lte-badge lte-badge-success" style="display: flex; align-items: center; gap: 0.375rem; padding: 0.375rem 0.75rem;">
<span style="width: 6px; height: 6px; background: currentColor; border-radius: 50%; animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;"></span>
Running
</span>
</div>
@else
<span class="lte-badge lte-badge-warning" style="display: flex; align-items: center; gap: 0.375rem; padding: 0.375rem 0.75rem;">
<x-ri-stop-circle-line class="size-3" />
Stopped
</span>
@endif
</div>
</div>
<!-- Operating System -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid rgba(0,0,0,0.05);">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-ubuntu-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">Operating System</span>
</div>
<span style="font-size: 0.875rem; font-weight: 500;">{{ $server->os->name }}</span>
</div>
<!-- Hostname -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid rgba(0,0,0,0.05);" x-data="{ showHostnameEditModal: @entangle('isEditingHostname') }">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-computer-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">Hostname</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<code style="font-size: 0.875rem; background: rgba(0,0,0,0.05); padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-family: 'Courier New', monospace;">{{ $server->hostname ?? $status->name ?? 'N/A' }}</code>
<button wire:click="startEditingHostname" type="button" class="lte-btn lte-btn-xs lte-btn-outline-secondary">
<x-ri-edit-fill class="size-3" />
</button>
</div>
<!-- Operating System -->
<div class="flex items-center justify-between">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">OS</label>
<span class="text-sm text-gray-800 dark:text-gray-200">{{ $server->os->name }}</span>
<!-- Hostname Edit Modal (unchanged for brevity) -->
<div x-cloak x-show="showHostnameEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showHostnameEditModal = false; $wire.cancelEditingHostname()"
@keydown.escape.window="showHostnameEditModal = false; $wire.cancelEditingHostname()"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<!-- Hostname -->
<div class="flex items-center justify-between" x-data="{ showHostnameEditModal: @entangle('isEditingHostname') }">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">Hostname</label>
<div class="flex items-center gap-2">
<span class="text-sm font-mono text-gray-800 dark:text-gray-200">{{ $server->hostname ?? $status->name ?? 'N/A' }}</span>
<button wire:click="startEditingHostname" type="button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400">
<x-ri-edit-fill class="size-3.5" />
</button>
</div>
<!-- Hostname Edit Modal -->
<div x-cloak x-show="showHostnameEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showHostnameEditModal = false; $wire.cancelEditingHostname()"
@keydown.escape.window="showHostnameEditModal = false; $wire.cancelEditingHostname()"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<div x-cloak x-show="showHostnameEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showHostnameEditModal = false; $wire.cancelEditingHostname()"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;">
<div class="relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-xl w-full max-w-md p-4 space-y-3">
<div class="flex items-center justify-between">
<h4 class="text-sm font-semibold text-gray-800 dark:text-gray-100">Edit Hostname</h4>
<button type="button" class="text-gray-400 hover:text-gray-600 p-1"
<div x-cloak x-show="showHostnameEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showHostnameEditModal = false; $wire.cancelEditingHostname()"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;">
<div class="lte-card" style="width: 100%; max-width: 500px; margin: 0;">
<div class="lte-card-header lte-bg-primary" style="color: white;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<h5 class="lte-card-title" style="margin: 0; color: white;">Edit Hostname</h5>
<button type="button" class="lte-btn lte-btn-sm" style="background: rgba(255,255,255,0.2); border: none; color: white;"
@click="showHostnameEditModal = false; $wire.cancelEditingHostname()">
<x-ri-close-line class="size-4" />
</button>
</div>
<div class="space-y-2">
<div>
<label class="text-xs font-medium text-gray-600 dark:text-gray-400">Hostname</label>
<input type="text" wire:model.defer="hostname" class="form-input w-full mt-1 text-sm" placeholder="Enter hostname" />
</div>
@if($hostnameError)
<div class="text-xs text-red-600">{{ $hostnameError }}</div>
@endif
<p class="text-xs text-gray-500 dark:text-gray-400">
<strong>Save:</strong> Database only. <strong>Save & Apply:</strong> + cloud-init on next boot.
</p>
</div>
<div class="lte-card-body">
<div style="margin-bottom: 1rem;">
<label style="font-size: 0.875rem; font-weight: 600; display: block; margin-bottom: 0.5rem;">Hostname</label>
<input type="text" wire:model.defer="hostname" class="lte-form-control" placeholder="Enter hostname" />
</div>
<div class="flex justify-end gap-2">
<x-proxmox::ui.button type="button" @click="showHostnameEditModal = false; $wire.cancelEditingHostname()" variant="secondary">
Cancel
</x-proxmox::ui.button>
<x-proxmox::ui.button type="button" wire:click="updateHostname" variant="primary">
Save
</x-proxmox::ui.button>
<x-proxmox::ui.button type="button" wire:click="applyHostname" variant="primary">
Save & Apply
</x-proxmox::ui.button>
@if($hostnameError)
<div class="lte-alert lte-alert-danger" style="font-size: 0.875rem;">{{ $hostnameError }}</div>
@endif
<div class="lte-callout lte-callout-info" style="font-size: 0.875rem; padding: 0.75rem;">
<strong>Save:</strong> Database only. <strong>Save & Apply:</strong> + cloud-init on next boot.
</div>
</div>
<div class="lte-card-footer" style="display: flex; justify-content: flex-end; gap: 0.5rem;">
<button type="button" @click="showHostnameEditModal = false; $wire.cancelEditingHostname()" class="lte-btn lte-btn-secondary">
Cancel
</button>
<button type="button" wire:click="updateHostname" class="lte-btn lte-btn-primary">
Save
</button>
<button type="button" wire:click="applyHostname" class="lte-btn lte-btn-success">
<x-ri-check-line class="size-4" />
Save & Apply
</button>
</div>
</div>
</div>
</div>
<!-- Primary IPv4 Address -->
@if($server->primaryIpv4)
<div class="flex items-center justify-between" x-data="{ copied: false, copyText: '{{ $server->primaryIpv4->ip }}', copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); } }">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">IPv4</label>
<div class="flex items-center gap-1.5">
<span class="text-sm font-mono text-gray-800 dark:text-gray-200">{{ $server->primaryIpv4->ip }}</span>
<button @click="copy()" type="button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400">
<x-ri-check-fill x-show="copied" class="size-3.5 text-green-600" />
<x-ri-file-copy-fill x-show="!copied" class="size-3.5" />
</button>
</div>
<!-- Primary IPv4 Address -->
@if($server->primaryIpv4)
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid rgba(0,0,0,0.05);" x-data="{ copied: false, copyText: '{{ $server->primaryIpv4->ip }}', copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); } }">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-global-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">IPv4 Address</span>
</div>
@endif
<!-- Primary IPv6 Address -->
@if($server->primaryIpv6)
<div class="flex items-center justify-between" x-data="{ copied: false, copyText: '{{ $server->primaryIpv6->ip }}', copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); } }">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">IPv6</label>
<div class="flex items-center gap-1.5">
<span class="text-xs font-mono text-gray-800 dark:text-gray-200 truncate max-w-[180px]">{{ $server->primaryIpv6->ip }}</span>
<button @click="copy()" type="button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400">
<x-ri-check-fill x-show="copied" class="size-3.5 text-green-600" />
<x-ri-file-copy-fill x-show="!copied" class="size-3.5" />
</button>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<code style="font-size: 0.875rem; background: rgba(0,0,0,0.05); padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-family: 'Courier New', monospace;">{{ $server->primaryIpv4->ip }}</code>
<button @click="copy()" type="button" class="lte-btn lte-btn-xs lte-btn-outline-secondary">
<x-ri-check-fill x-show="copied" class="size-3 lte-text-success" />
<x-ri-file-copy-fill x-show="!copied" class="size-3" />
</button>
</div>
@endif
</div>
@endif
<!-- Root Password -->
<div class="flex items-center justify-between" x-data="{ showPasswordViewModal: false, showPasswordEditModal: @entangle('isEditingPassword') }">
<label class="text-xs font-semibold text-gray-600 dark:text-gray-400">Password</label>
<div class="flex items-center gap-1.5">
<span class="text-sm text-gray-800 dark:text-gray-200">
@if(isset($server->password) && $server->password)
••••••••
@else
Emailed
@endif
</span>
<!-- Primary IPv6 Address -->
@if($server->primaryIpv6)
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0; border-bottom: 1px solid rgba(0,0,0,0.05);" x-data="{ copied: false, copyText: '{{ $server->primaryIpv6->ip }}', copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); } }">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-global-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">IPv6 Address</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<code style="font-size: 0.75rem; background: rgba(0,0,0,0.05); padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-family: 'Courier New', monospace; max-width: 200px; overflow: hidden; text-overflow: ellipsis;">{{ $server->primaryIpv6->ip }}</code>
<button @click="copy()" type="button" class="lte-btn lte-btn-xs lte-btn-outline-secondary">
<x-ri-check-fill x-show="copied" class="size-3 lte-text-success" />
<x-ri-file-copy-fill x-show="!copied" class="size-3" />
</button>
</div>
</div>
@endif
<!-- Root Password -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 0;" x-data="{ showPasswordViewModal: false, showPasswordEditModal: @entangle('isEditingPassword') }">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<x-ri-lock-password-line class="size-4 lte-text-muted" />
<span style="font-size: 0.875rem; font-weight: 600;" class="lte-text-muted">Root Password</span>
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<span style="font-size: 0.875rem;">
@if(isset($server->password) && $server->password)
<button type="button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400" @click="showPasswordViewModal = true">
<x-ri-eye-line class="size-3.5" />
</button>
••••••••
@else
<span class="lte-badge lte-badge-info">Emailed</span>
@endif
<button type="button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400" wire:click="startEditingPassword">
<x-ri-edit-fill class="size-3.5" />
</span>
@if(isset($server->password) && $server->password)
<button type="button" class="lte-btn lte-btn-xs lte-btn-outline-secondary" @click="showPasswordViewModal = true">
<x-ri-eye-line class="size-3" />
</button>
</div>
@endif
<button type="button" class="lte-btn lte-btn-xs lte-btn-outline-primary" wire:click="startEditingPassword">
<x-ri-edit-fill class="size-3" />
</button>
</div>
<!-- Password View Modal -->
<div x-cloak x-show="showPasswordViewModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showPasswordViewModal = false"
@keydown.escape.window="showPasswordViewModal = false"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<div x-cloak x-show="showPasswordViewModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showPasswordViewModal = false"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;"
x-data="{
copied: false,
copyText: {{ json_encode($server->password) }},
copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); }
}">
<div class="relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-xl w-full max-w-md p-4 space-y-3">
<div class="flex items-center justify-between">
<h4 class="text-sm font-semibold text-gray-800 dark:text-gray-100">Root Password</h4>
<button type="button" class="text-gray-400 hover:text-gray-600 p-1" @click="showPasswordViewModal = false">
<!-- Password View Modal -->
<div x-cloak x-show="showPasswordViewModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showPasswordViewModal = false"
@keydown.escape.window="showPasswordViewModal = false"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<div x-cloak x-show="showPasswordViewModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showPasswordViewModal = false"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;"
x-data="{
copied: false,
copyText: {{ json_encode($server->password) }},
copy() { navigator.clipboard.writeText(this.copyText); this.copied = true; setTimeout(() => this.copied = false, 2000); }
}">
<div class="lte-card" style="width: 100%; max-width: 500px; margin: 0;">
<div class="lte-card-header lte-bg-primary" style="color: white;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<h5 class="lte-card-title" style="margin: 0; color: white;">Root Password</h5>
<button type="button" class="lte-btn lte-btn-sm" style="background: rgba(255,255,255,0.2); border: none; color: white;" @click="showPasswordViewModal = false">
<x-ri-close-line class="size-4" />
</button>
</div>
<div class="bg-gray-100 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded p-2 text-sm text-gray-800 dark:text-gray-200 overflow-auto">
<code class="break-all block font-mono text-xs">{{ $server->password }}</code>
</div>
<div class="flex justify-end">
<button type="button" class="px-3 py-1.5 rounded bg-blue-600 text-white text-xs font-medium hover:bg-blue-700" @click="copy()">
<span x-show="!copied">Copy</span>
<span x-show="copied">Copied!</span>
</button>
</div>
<div class="lte-card-body">
<div style="background: rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.1); border-radius: 0.375rem; padding: 1rem;">
<code style="display: block; font-family: 'Courier New', monospace; font-size: 0.875rem; word-break: break-all;">{{ $server->password }}</code>
</div>
</div>
<div class="lte-card-footer" style="display: flex; justify-content: flex-end;">
<button type="button" class="lte-btn lte-btn-success" @click="copy()">
<x-ri-file-copy-line class="size-4" />
<span x-show="!copied">Copy Password</span>
<span x-show="copied">Copied!</span>
</button>
</div>
</div>
</div>
<!-- Password Edit Modal -->
<div x-cloak x-show="showPasswordEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showPasswordEditModal = false; $wire.cancelEditingPassword()"
@keydown.escape.window="showPasswordEditModal = false; $wire.cancelEditingPassword()"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<div x-cloak x-show="showPasswordEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showPasswordEditModal = false; $wire.cancelEditingPassword()"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;">
<div class="relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-xl w-full max-w-md p-4 space-y-3">
<div class="flex items-center justify-between">
<h4 class="text-sm font-semibold text-gray-800 dark:text-gray-100">Edit Root Password</h4>
<button type="button" class="text-gray-400 hover:text-gray-600 p-1"
<!-- Password Edit Modal (keeping structure but styling with AdminLTE) -->
<div x-cloak x-show="showPasswordEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="showPasswordEditModal = false; $wire.cancelEditingPassword()"
@keydown.escape.window="showPasswordEditModal = false; $wire.cancelEditingPassword()"
class="fixed inset-0 z-[60] bg-gray-900 bg-opacity-50"
style="display: none;">
</div>
<div x-cloak x-show="showPasswordEditModal"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="showPasswordEditModal = false; $wire.cancelEditingPassword()"
class="fixed inset-0 z-[60] flex items-center justify-center p-4"
style="display: none;">
<div class="lte-card" style="width: 100%; max-width: 500px; margin: 0;">
<div class="lte-card-header lte-bg-primary" style="color: white;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<h5 class="lte-card-title" style="margin: 0; color: white;">Edit Root Password</h5>
<button type="button" class="lte-btn lte-btn-sm" style="background: rgba(255,255,255,0.2); border: none; color: white;"
@click="showPasswordEditModal = false; $wire.cancelEditingPassword()">
<x-ri-close-line class="size-4" />
</button>
</div>
<div class="space-y-2">
<div>
<label class="text-xs font-medium text-gray-600 dark:text-gray-400">New Password</label>
<input type="password" wire:model.defer="password" class="form-input w-full mt-1 text-sm" placeholder="Enter new password" />
</div>
<div>
<label class="text-xs font-medium text-gray-600 dark:text-gray-400">Confirm Password</label>
<input type="password"
wire:model.defer="password_confirmation"
class="form-input w-full mt-1 text-sm"
placeholder="Confirm password" />
</div>
@if($passwordError)
<div class="text-xs text-red-600">{{ $passwordError }}</div>
@endif
<p class="text-xs text-gray-500 dark:text-gray-400">
<strong>Save:</strong> Database only. <strong>Save & Apply:</strong> + cloud-init (reboot required).
</p>
</div>
<div class="lte-card-body">
<div style="margin-bottom: 1rem;">
<label style="font-size: 0.875rem; font-weight: 600; display: block; margin-bottom: 0.5rem;">New Password</label>
<input type="password" wire:model.defer="password" class="lte-form-control" placeholder="Enter new password" />
</div>
<div class="flex justify-end gap-2">
<div style="margin-bottom: 1rem;">
<label style="font-size: 0.875rem; font-weight: 600; display: block; margin-bottom: 0.5rem;">Confirm Password</label>
<input type="password" wire:model.defer="password_confirmation" class="lte-form-control" placeholder="Confirm password" />
</div>
@if($passwordError)
<div class="lte-alert lte-alert-danger" style="font-size: 0.875rem; margin-bottom: 1rem;">{{ $passwordError }}</div>
@endif
<div class="lte-callout lte-callout-info" style="font-size: 0.875rem; padding: 0.75rem;">
<strong>Save:</strong> Database only. <strong>Save & Apply:</strong> + cloud-init (reboot required).
</div>
</div>
<div class="lte-card-footer" style="display: flex; justify-content: flex-end; gap: 0.5rem;">
<button type="button" @click="showPasswordEditModal = false; $wire.cancelEditingPassword()" class="lte-btn lte-btn-secondary">
Cancel
</button>
<button type="button" wire:click="updatePassword" class="lte-btn lte-btn-primary">
Save
</button>
<button type="button" wire:click="applyPassword" class="lte-btn lte-btn-success">
<x-ri-check-line class="size-4" />
Save & Apply
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<x-proxmox::ui.button type="button" @click="showPasswordEditModal = false; $wire.cancelEditingPassword()" variant="secondary">
Cancel
</x-proxmox::ui.button>