- 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.
475 lines
32 KiB
PHP
475 lines
32 KiB
PHP
<x-proxmox::layout :server="$server" view="subdomain" :status="$status" :status-polling="true" :status-polling-interval="'2s'">
|
|
<div class="flex items-center gap-1.5 text-xs text-base/50 mb-4">
|
|
<a href="{{ route('proxmox.server.overview', ['server' => $server->id]) }}" wire:navigate class="hover:text-primary transition-colors">Overview</a>
|
|
<x-ri-arrow-right-s-line class="size-3" />
|
|
<span class="text-base/70">Subdomain</span>
|
|
</div>
|
|
<div class="flex items-center justify-between mb-6">
|
|
<div>
|
|
<div class="flex items-center gap-2 min-w-0">
|
|
<h2 class="text-xl font-semibold break-words">Subdomain 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.self="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-global-line class="size-5 text-primary" />
|
|
</div>
|
|
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
|
|
Subdomain Management
|
|
</h6>
|
|
</div>
|
|
<p class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
|
|
Create and manage DNS subdomains for your virtual machine. Each subdomain points to one of your assigned public IP addresses and is automatically configured with A and AAAA records.
|
|
</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">Limitations:</span> Only A and AAAA records are created. One subdomain per public IP address. Wildcard subdomains (*) are automatically created for each subdomain.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">DNS Propagation:</span> Changes may take a few minutes to propagate across DNS servers worldwide.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p class="text-sm text-base/60 mt-1">Create and manage subdomains for dns.virtnet.host</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Disclaimer -->
|
|
<div class="bg-info/10 border border-info/30 rounded-lg p-4 mb-6"
|
|
x-data="{
|
|
isCollapsed: localStorage.getItem('subdomain-notice-collapsed') === 'true',
|
|
toggle() {
|
|
this.isCollapsed = !this.isCollapsed;
|
|
localStorage.setItem('subdomain-notice-collapsed', this.isCollapsed);
|
|
}
|
|
}">
|
|
<div class="flex items-start gap-3">
|
|
<x-ri-information-line class="size-5 text-info flex-shrink-0 mt-0.5" />
|
|
<div class="flex-1">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h3 class="text-sm font-semibold text-base">Subdomain Configuration Notice</h3>
|
|
<button @click="toggle()" type="button" class="text-base/60 hover:text-base transition-colors">
|
|
<x-ri-arrow-down-s-line x-bind:class="isCollapsed ? 'size-4 transition-transform rotate-180' : 'size-4 transition-transform'" />
|
|
</button>
|
|
</div>
|
|
<div x-show="!isCollapsed" x-transition>
|
|
<p class="text-xs text-base/70 leading-relaxed">
|
|
Only <strong>A and AAAA records</strong> are created. For any subdomain created, its child wildcard (*) subdomain is also automatically created. <strong>One subdomain per public IP address</strong> can be created. Under no circumstances will PTR, MX, NS, or other record types be added. These restrictions are in place to minimize abuse of the subdomain system.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-6">
|
|
<!-- Create Subdomain Section -->
|
|
<div class="bg-background-secondary hover:bg-background-secondary/80 border border-neutral rounded-lg p-6">
|
|
<div class="flex items-center justify-between mb-4 gap-4">
|
|
<div class="flex items-center gap-2 min-w-0">
|
|
<h3 class="text-lg font-semibold break-words">Create Subdomain</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.self="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-add-circle-line class="size-5 text-primary" />
|
|
</div>
|
|
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
|
|
Create Subdomain
|
|
</h6>
|
|
</div>
|
|
<p class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
|
|
Create a new subdomain that points to one of your assigned public IP addresses. The subdomain will be created under the dns.virtnet.host domain.
|
|
</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">Subdomain Name:</span> Choose a unique name for your subdomain. Only alphanumeric characters and hyphens are allowed.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">IP Address:</span> Select which public IP address this subdomain should point to. Each IP can only have one subdomain.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
A wildcard subdomain (*.yourname.dns.virtnet.host) will be automatically created along with your subdomain.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<x-button.primary wire:click="$set('showCreateModal', true)" :disabled="count($this->availableIpsForSubdomain) === 0" wire:loading.attr="disabled" class="!w-auto flex-shrink-0">
|
|
<x-ri-hourglass-fill wire:loading wire:target="$set('showCreateModal', true)" class="size-4 animate-pulse" />
|
|
<x-ri-add-circle-fill wire:loading.remove wire:target="$set('showCreateModal', true)" class="size-4" />
|
|
Create Subdomain
|
|
</x-button.primary>
|
|
</div>
|
|
@if(count($this->availableIpsForSubdomain) === 0)
|
|
<p class="text-sm text-base/60">All available public IP addresses already have subdomains assigned.</p>
|
|
@else
|
|
<p class="text-sm text-base/60">You can create {{ count($this->availableIpsForSubdomain) }} more subdomain(s).</p>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Existing Subdomains -->
|
|
<div class="bg-background-secondary hover:bg-background-secondary/80 border border-neutral rounded-lg p-6">
|
|
<div class="flex items-center gap-2 min-w-0 mb-4">
|
|
<h3 class="text-lg font-semibold break-words">Your Subdomains</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.self="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-list-check class="size-5 text-primary" />
|
|
</div>
|
|
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
|
|
Your Subdomains
|
|
</h6>
|
|
</div>
|
|
<p class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
|
|
Lists all subdomains you have created for this virtual machine. Each subdomain shows its full domain name, associated IP address, and automatically created wildcard record.
|
|
</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">Delete:</span> Removes the subdomain and all associated DNS records. This action cannot be undone.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">Wildcard Records:</span> Each subdomain automatically includes a wildcard (*) record that matches all subdomains under your chosen name.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@if(count($serverZones) === 0)
|
|
<p class="text-sm text-base/60">No subdomains created yet.</p>
|
|
@else
|
|
<div class="flex flex-col gap-4">
|
|
@foreach($serverZones as $zone)
|
|
<div wire:key="zone-{{ $zone['subdomain'] }}" class="bg-background border border-neutral rounded-lg p-4">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h4 class="text-md font-semibold">{{ $zone['subdomain'] }}.dns.virtnet.host</h4>
|
|
<p class="text-sm text-base/60">IP: {{ $zone['ipAddress'] }}</p>
|
|
@php
|
|
$hasWildcard = false;
|
|
$wildcardType = null;
|
|
if (isset($zone['records']) && is_array($zone['records'])) {
|
|
foreach ($zone['records'] as $record) {
|
|
if (($record['name'] ?? '') === '*') {
|
|
$hasWildcard = true;
|
|
$wildcardType = $record['type'] ?? 'A';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
@endphp
|
|
@if($hasWildcard)
|
|
<p class="text-xs text-base/50 mt-1">Wildcard: *.{{ $zone['subdomain'] }}.dns.virtnet.host ({{ $wildcardType }})</p>
|
|
@endif
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<x-button.danger wire:click="openDeleteModal('{{ $zone['subdomain'] }}', {{ $zone['ipAddressId'] }})" wire:loading.attr="disabled" wire:target="openDeleteModal('{{ $zone['subdomain'] }}', {{ $zone['ipAddressId'] }})">
|
|
<x-ri-hourglass-fill wire:loading wire:target="openDeleteModal('{{ $zone['subdomain'] }}', {{ $zone['ipAddressId'] }})" class="size-4 animate-pulse" />
|
|
<x-ri-delete-bin-line wire:loading.remove wire:target="openDeleteModal('{{ $zone['subdomain'] }}', {{ $zone['ipAddressId'] }})" class="size-4" />
|
|
Delete
|
|
</x-button.danger>
|
|
<button type="button"
|
|
wire:click="openRecordModal('{{ $zone['subdomain'] }}')"
|
|
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg bg-primary/10 text-primary hover:bg-primary/20 transition-colors">
|
|
<x-ri-settings-3-line class="size-4" />
|
|
Manage Records
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Subdomain Modal -->
|
|
@if($showCreateModal)
|
|
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" wire:click="$set('showCreateModal', false)" x-data @keydown.escape.window="$wire.set('showCreateModal', false)">
|
|
<div class="bg-background border border-neutral rounded-lg p-6 max-w-md w-full mx-4" wire:click.stop>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold">Create New Subdomain</h3>
|
|
<button wire:click="$set('showCreateModal', false)" class="text-base/60 hover:text-base" aria-label="Close">
|
|
<x-ri-close-line class="size-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-4">
|
|
<div class="flex flex-col gap-2">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<label class="text-sm font-semibold">Subdomain Name</label>
|
|
<button type="button"
|
|
wire:click="generateRandomSubdomain"
|
|
class="inline-flex items-center gap-1 text-xs font-semibold text-base/70 hover:text-base transition-colors">
|
|
<i class="ri-dice-line text-base"></i>
|
|
Random
|
|
</button>
|
|
</div>
|
|
<input type="text" wire:model.live="newSubdomain"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary"
|
|
placeholder="myserver">
|
|
<p class="text-xs text-base/60">Will create: <span class="font-semibold">{{ $newSubdomain ? $newSubdomain . '.dns.virtnet.host' : 'subdomain.dns.virtnet.host' }}</span></p>
|
|
@error('newSubdomain')
|
|
<p class="text-xs text-error">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2">
|
|
<label class="text-sm font-semibold">IP Address</label>
|
|
<select wire:model="selectedIpId"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
<option value="">Select an IP address</option>
|
|
@foreach($this->availableIpsForSubdomain as $ip)
|
|
<option value="{{ $ip->id }}">{{ $ip->ip }}</option>
|
|
@endforeach
|
|
</select>
|
|
@error('selectedIpId')
|
|
<p class="text-xs text-error">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="flex gap-2 justify-end">
|
|
<x-button.secondary wire:click="$set('showCreateModal', false)">Cancel</x-button.secondary>
|
|
<x-button.primary wire:click="createSubdomain" wire:loading.attr="disabled">
|
|
<x-loading target="createSubdomain" />
|
|
<span wire:loading.remove wire:target="createSubdomain">Create</span>
|
|
<span wire:loading wire:target="createSubdomain">Creating...</span>
|
|
</x-button.primary>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Manage Records Modal -->
|
|
@if($showRecordModal && $selectedZone)
|
|
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" wire:click="$set('showRecordModal', false)" x-data @keydown.escape.window="$wire.set('showRecordModal', false)">
|
|
<div class="bg-background border border-neutral rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto" wire:click.stop>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold">DNS Records for {{ $selectedZone }}.dns.virtnet.host</h3>
|
|
<button wire:click="$set('showRecordModal', false)" class="text-base/60 hover:text-base" aria-label="Close">
|
|
<x-ri-close-line class="size-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Add Record Form -->
|
|
<div class="bg-background-secondary border border-neutral rounded-lg p-4 mb-4">
|
|
<h4 class="text-md font-semibold mb-3">Add DNS Record</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div class="flex flex-col gap-2">
|
|
<label class="text-xs font-semibold">Name (use @ for root, * for wildcard)</label>
|
|
<input type="text" wire:model="recordName"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary"
|
|
placeholder="@ or www or *">
|
|
@error('recordName')
|
|
<p class="text-xs text-error">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2">
|
|
<label class="text-xs font-semibold">Type</label>
|
|
<select wire:model="recordType"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
<option value="A">A (IPv4)</option>
|
|
<option value="AAAA">AAAA (IPv6)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2">
|
|
<label class="text-xs font-semibold">IP Address</label>
|
|
<input type="text" wire:model="recordRdata"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary"
|
|
placeholder="1.2.3.4 or 2001:db8::1">
|
|
@error('recordRdata')
|
|
<p class="text-xs text-error">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2">
|
|
<label class="text-xs font-semibold">TTL (seconds)</label>
|
|
<input type="number" wire:model="recordTtl" min="60" max="86400"
|
|
class="bg-background border border-neutral rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
@error('recordTtl')
|
|
<p class="text-xs text-error">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<x-button.primary wire:click="addRecord" wire:loading.attr="disabled" size="sm">
|
|
<x-loading target="addRecord" />
|
|
<span wire:loading.remove wire:target="addRecord">Add Record</span>
|
|
<span wire:loading wire:target="addRecord">Adding...</span>
|
|
</x-button.primary>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Existing Records -->
|
|
<div wire:key="zone-records-{{ $selectedZone }}">
|
|
<h4 class="text-md font-semibold mb-3">Existing Records</h4>
|
|
@if(empty($zoneRecords) || count($zoneRecords) === 0)
|
|
<p class="text-sm text-base/60">No DNS records found.</p>
|
|
@else
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr class="border-b border-neutral">
|
|
<th class="text-left py-2 px-3 text-sm font-semibold">Name</th>
|
|
<th class="text-left py-2 px-3 text-sm font-semibold">Type</th>
|
|
<th class="text-left py-2 px-3 text-sm font-semibold">IP Address</th>
|
|
<th class="text-left py-2 px-3 text-sm font-semibold">TTL</th>
|
|
<th class="text-right py-2 px-3 text-sm font-semibold">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($zoneRecords as $index => $record)
|
|
<tr class="border-b border-neutral/40" wire:key="record-{{ $selectedZone }}-{{ $index }}">
|
|
<td class="py-2 px-3 text-sm">{{ $record['name'] ?? '@' }}</td>
|
|
<td class="py-2 px-3 text-sm">{{ $record['type'] ?? 'A' }}</td>
|
|
<td class="py-2 px-3 text-sm">{{ $record['rdata'] ?? $record['ipAddress'] ?? '' }}</td>
|
|
<td class="py-2 px-3 text-sm">{{ $record['ttl'] ?? 3600 }}</td>
|
|
<td class="py-2 px-3 text-sm text-right">
|
|
<x-button.danger wire:click="deleteRecord('{{ $record['name'] ?? '@' }}', '{{ $record['type'] ?? 'A' }}')"
|
|
wire:confirm="Are you sure you want to delete this record?" size="sm">
|
|
<x-ri-delete-bin-line class="size-4" />
|
|
</x-button.danger>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Delete Subdomain Confirmation Modal -->
|
|
@if($showDeleteModal && $subdomainToDelete)
|
|
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" wire:click="closeDeleteModal" x-data @keydown.escape.window="$wire.closeDeleteModal()">
|
|
<div class="bg-background border border-neutral rounded-lg p-6 max-w-md w-full mx-4" wire:click.stop>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold">Delete Subdomain</h3>
|
|
<button wire:click="closeDeleteModal" class="text-base/60 hover:text-base" aria-label="Close">
|
|
<x-ri-close-line class="size-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<p class="text-sm text-base/60 mb-2">Are you sure you want to delete the following subdomain?</p>
|
|
<p class="text-md font-semibold">{{ $subdomainToDeleteFull }}</p>
|
|
<p class="text-xs text-base/60 mt-2">This action cannot be undone. All DNS records associated with this subdomain will be deleted.</p>
|
|
</div>
|
|
|
|
<div class="flex gap-2 justify-end">
|
|
<x-button.secondary wire:click="closeDeleteModal">Cancel</x-button.secondary>
|
|
<x-button.danger wire:click="deleteSubdomain" wire:loading.attr="disabled">
|
|
<x-loading target="deleteSubdomain" />
|
|
<span wire:loading.remove wire:target="deleteSubdomain">Delete</span>
|
|
<span wire:loading wire:target="deleteSubdomain">Deleting...</span>
|
|
</x-button.danger>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</x-proxmox::layout>
|
|
|