fix: update firewall toggle functionality to use wire:click and prevent direct changes
This commit is contained in:
@@ -274,12 +274,12 @@
|
||||
<span class="fw-toggle-label {{ $firewallEnabled ? 'fw-toggle-label-enabled' : 'fw-toggle-label-disabled' }}">
|
||||
{{ $firewallEnabled ? 'Enabled' : 'Disabled' }}
|
||||
</span>
|
||||
<label class="fw-toggle" wire:loading.class="pointer-events-none">
|
||||
<label class="fw-toggle" wire:loading.class="pointer-events-none" wire:click="toggleFirewall">
|
||||
<input
|
||||
type="checkbox"
|
||||
wire:change="toggleFirewall"
|
||||
wire:loading.attr="disabled"
|
||||
@checked($firewallEnabled)
|
||||
{{ $firewallEnabled ? 'checked' : '' }}
|
||||
readonly
|
||||
>
|
||||
<span class="fw-toggle-slider"></span>
|
||||
</label>
|
||||
@@ -365,13 +365,13 @@
|
||||
<span class="fw-toggle-label {{ ($adapterFirewallStates[$adapter['name']] ?? false) ? 'fw-toggle-label-enabled' : 'fw-toggle-label-disabled' }}">
|
||||
{{ ($adapterFirewallStates[$adapter['name']] ?? false) ? 'On' : 'Off' }}
|
||||
</span>
|
||||
<label class="fw-toggle-sm" wire:loading.class="pointer-events-none">
|
||||
<label class="fw-toggle-sm" wire:loading.class="pointer-events-none" wire:click="toggleAdapterFirewall('{{ $adapter['name'] }}')">
|
||||
<input
|
||||
type="checkbox"
|
||||
wire:change="toggleAdapterFirewall('{{ $adapter['name'] }}')"
|
||||
wire:loading.attr="disabled"
|
||||
wire:key="adapter-{{ $adapter['name'] }}"
|
||||
@checked($adapterFirewallStates[$adapter['name']] ?? false)
|
||||
{{ ($adapterFirewallStates[$adapter['name']] ?? false) ? 'checked' : '' }}
|
||||
readonly
|
||||
>
|
||||
<span class="fw-toggle-slider-sm"></span>
|
||||
</label>
|
||||
@@ -872,6 +872,478 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Rule Form Modal --}}
|
||||
@if($showRuleForm)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4"
|
||||
x-data="{
|
||||
macroDefinitions: @js($this->macroDefinitions),
|
||||
currentMacro: @entangle('ruleMacro'),
|
||||
handleMacroChange() {
|
||||
if (this.currentMacro && this.currentMacro !== '' && this.macroDefinitions[this.currentMacro]) {
|
||||
const macroDef = this.macroDefinitions[this.currentMacro];
|
||||
if (macroDef.proto) {
|
||||
$wire.set('ruleProto', macroDef.proto);
|
||||
}
|
||||
if (macroDef.dport) {
|
||||
$wire.set('ruleDport', macroDef.dport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-init="$watch('currentMacro', () => handleMacroChange())">
|
||||
<div class="relative w-full max-w-4xl bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl max-h-[90vh] overflow-y-auto">
|
||||
<button wire:click="cancelRuleForm" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors z-10">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-semibold mb-6">{{ $editingRuleIndex !== null ? 'Edit' : 'Create' }} Firewall Rule</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{{-- Action --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Action</label>
|
||||
<select wire:model="ruleAction" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="ACCEPT">Accept</option>
|
||||
<option value="REJECT">Reject</option>
|
||||
<option value="DROP">Drop</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Type --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Direction</label>
|
||||
<select wire:model="ruleType" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="in">Incoming</option>
|
||||
<option value="out">Outgoing</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Macro --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Macro (Pre-defined Service)</label>
|
||||
<select wire:model="ruleMacro" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
@foreach($availableMacros as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
<x-ri-lightbulb-line class="size-3 inline mr-0.5" />
|
||||
Selecting a macro will automatically set the protocol and port(s)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Interface --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Network Interface</label>
|
||||
<select wire:model="ruleIface" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="">Any</option>
|
||||
@foreach($networkAdapters as $adapter)
|
||||
<option value="{{ $adapter['name'] }}">{{ $adapter['name'] }} ({{ ucfirst($adapter['model'] ?? 'Unknown') }})</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Source --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Source IP/CIDR</label>
|
||||
<input type="text" wire:model="ruleSource" placeholder="e.g., 192.168.1.0/24 or leave empty for any" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Can use alias names or leave empty for any source</p>
|
||||
</div>
|
||||
|
||||
{{-- Destination --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Destination IP/CIDR</label>
|
||||
<select wire:model="ruleDest" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="">Any</option>
|
||||
@foreach($availableIps as $ipData)
|
||||
<option value="{{ $ipData['ip'] }}">{{ $ipData['ip'] }} ({{ $ipData['label'] }})</option>
|
||||
@endforeach
|
||||
@foreach($aliases as $alias)
|
||||
<option value="{{ $alias['name'] }}">{{ $alias['name'] }} (Alias)</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Protocol --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">
|
||||
Protocol
|
||||
<span class="text-xs font-normal text-gray-500">(auto-filled by macro)</span>
|
||||
</label>
|
||||
<select wire:model="ruleProto" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="">Any</option>
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
<option value="icmp">ICMP</option>
|
||||
<option value="igmp">IGMP</option>
|
||||
<option value="ah">AH (IPsec Authentication Header)</option>
|
||||
<option value="esp">ESP (IPsec Encapsulating Security Payload)</option>
|
||||
<option value="ospf">OSPF</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Destination Port --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">
|
||||
Destination Port
|
||||
<span class="text-xs font-normal text-gray-500">(auto-filled by macro)</span>
|
||||
</label>
|
||||
<input type="text" wire:model="ruleDport" placeholder="e.g., 80, 443, 1000:2000" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
{{-- Source Port --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Source Port (Optional)</label>
|
||||
<input type="text" wire:model="ruleSport" placeholder="e.g., 80, 443" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
{{-- Log Level --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Logging</label>
|
||||
<select wire:model="ruleLog" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
<option value="nolog">No Logging</option>
|
||||
<option value="emerg">Emergency</option>
|
||||
<option value="alert">Alert</option>
|
||||
<option value="crit">Critical</option>
|
||||
<option value="err">Error</option>
|
||||
<option value="warning">Warning</option>
|
||||
<option value="notice">Notice</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="debug">Debug</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Comment --}}
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium mb-2">Comment</label>
|
||||
<input type="text" wire:model="ruleComment" placeholder="Optional description" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
{{-- Enable --}}
|
||||
<div class="md:col-span-2 flex items-center gap-2">
|
||||
<input type="checkbox" wire:model="ruleEnable" id="ruleEnable" class="rounded border-gray-300 dark:border-gray-600">
|
||||
<label for="ruleEnable" class="text-sm font-medium">Enable this rule immediately</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 justify-end mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button type="button" wire:click="cancelRuleForm" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" wire:click="saveRule" wire:loading.attr="disabled" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||
<span wire:loading.remove wire:target="saveRule">Save Rule</span>
|
||||
<span wire:loading wire:target="saveRule">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Delete Rule Modal --}}
|
||||
@if($showDeleteRuleModal && $deletingRuleIndex !== null && isset($rules[$deletingRuleIndex]))
|
||||
@php $ruleToDelete = $rules[$deletingRuleIndex]; @endphp
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelDeleteRule" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="w-12 h-12 rounded-full bg-error/10 flex items-center justify-center flex-shrink-0">
|
||||
<x-ri-delete-bin-fill class="size-6 text-error" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-xl font-bold mb-2">Delete Firewall Rule</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Are you sure you want to delete this rule? This action cannot be undone.</p>
|
||||
<div class="p-3 bg-gray-50 dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-lg text-sm space-y-1">
|
||||
<div><span class="text-gray-500">Action:</span> <span class="font-semibold">{{ $ruleToDelete['action'] ?? '-' }}</span></div>
|
||||
<div><span class="text-gray-500">Source:</span> <span class="font-mono text-xs">{{ $ruleToDelete['source'] ?? 'Any' }}</span></div>
|
||||
<div><span class="text-gray-500">Destination:</span> <span class="font-mono text-xs">{{ $ruleToDelete['dest'] ?? 'Any' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 justify-end">
|
||||
<x-proxmox::ui.button wire:click="cancelDeleteRule" variant="secondary">
|
||||
Cancel
|
||||
</x-proxmox::ui.button>
|
||||
<x-proxmox::ui.button wire:click="deleteRule" wire:loading.attr="disabled" variant="danger">
|
||||
<span wire:loading.remove wire:target="deleteRule">Delete Rule</span>
|
||||
<span wire:loading wire:target="deleteRule">Deleting...</span>
|
||||
</x-proxmox::ui.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Alias Form Modal --}}
|
||||
@if($showAliasForm)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelAliasForm" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<h3 class="text-xl font-semibold mb-6">{{ $editingAliasName ? 'Edit' : 'Create' }} IP Alias</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Alias Name</label>
|
||||
<input type="text" wire:model="aliasName" placeholder="e.g., web-servers" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm" {{ $editingAliasName ? 'readonly' : '' }}>
|
||||
@error('aliasName') <span class="text-error text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">IP Address / CIDR</label>
|
||||
<input type="text" wire:model="aliasCidr" placeholder="e.g., 192.168.1.100 or 10.0.0.0/24" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
@error('aliasCidr') <span class="text-error text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Comment (Optional)</label>
|
||||
<input type="text" wire:model="aliasComment" placeholder="Description of this alias" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 justify-end mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button wire:click="cancelAliasForm" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium">
|
||||
Cancel
|
||||
</button>
|
||||
<button wire:click="saveAlias" wire:loading.attr="disabled" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||
<span wire:loading.remove wire:target="saveAlias">Save Alias</span>
|
||||
<span wire:loading wire:target="saveAlias">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Delete Alias Modal --}}
|
||||
@if($showDeleteAliasModal && $deletingAliasName)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-md bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelDeleteAlias" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="w-12 h-12 rounded-full bg-error/10 flex items-center justify-center flex-shrink-0">
|
||||
<x-ri-delete-bin-fill class="size-6 text-error" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Delete IP Alias</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Delete alias "<span class="font-semibold">{{ $deletingAliasName }}</span>"?</p>
|
||||
<p class="text-xs text-gray-500">This may affect firewall rules using this alias.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 justify-end">
|
||||
<x-proxmox::ui.button wire:click="cancelDeleteAlias" variant="secondary">
|
||||
Cancel
|
||||
</x-proxmox::ui.button>
|
||||
<x-proxmox::ui.button wire:click="deleteAlias" wire:loading.attr="disabled" variant="danger">
|
||||
<span wire:loading.remove wire:target="deleteAlias">Delete</span>
|
||||
<span wire:loading wire:target="deleteAlias">Deleting...</span>
|
||||
</x-proxmox::ui.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- IPSet Form Modal --}}
|
||||
@if($showIPSetForm)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelIPSetForm" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<h3 class="text-xl font-semibold mb-6">Create IP Set</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">IPSet Name</label>
|
||||
<input type="text" wire:model="ipsetName" placeholder="e.g., trusted-networks" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
@error('ipsetName') <span class="text-error text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Comment (Optional)</label>
|
||||
<input type="text" wire:model="ipsetComment" placeholder="Description of this IPSet" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-info/10 border border-info/30 rounded-lg">
|
||||
<p class="text-xs text-info">After creating the IPSet, you can add IP addresses and CIDR ranges to it.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 justify-end mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button wire:click="cancelIPSetForm" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium">
|
||||
Cancel
|
||||
</button>
|
||||
<button wire:click="saveIPSet" wire:loading.attr="disabled" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||
<span wire:loading.remove wire:target="saveIPSet">Create IPSet</span>
|
||||
<span wire:loading wire:target="saveIPSet">Creating...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Delete IPSet Modal --}}
|
||||
@if($showDeleteIPSetModal && $deletingIPSetName)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-md bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelDeleteIPSet" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="w-12 h-12 rounded-full bg-error/10 flex items-center justify-center flex-shrink-0">
|
||||
<x-ri-delete-bin-fill class="size-6 text-error" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Delete IP Set</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Delete IPSet "<span class="font-semibold">{{ $deletingIPSetName }}</span>"?</p>
|
||||
<p class="text-xs text-gray-500">All members will be removed and rules using this IPSet may be affected.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 justify-end">
|
||||
<x-proxmox::ui.button wire:click="cancelDeleteIPSet" variant="secondary">
|
||||
Cancel
|
||||
</x-proxmox::ui.button>
|
||||
<x-proxmox::ui.button wire:click="deleteIPSet" wire:loading.attr="disabled" variant="danger">
|
||||
<span wire:loading.remove wire:target="deleteIPSet">Delete</span>
|
||||
<span wire:loading wire:target="deleteIPSet">Deleting...</span>
|
||||
</x-proxmox::ui.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- IPSet Member Form Modal --}}
|
||||
@if($showIPSetMemberForm && $selectedIPSetName)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelIPSetMemberForm" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<h3 class="text-xl font-semibold mb-6">Add IP/CIDR to {{ $selectedIPSetName }}</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">IP Address / CIDR</label>
|
||||
<input type="text" wire:model="memberCidr" placeholder="e.g., 192.168.1.100 or 10.0.0.0/16" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
@error('memberCidr') <span class="text-error text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Comment (Optional)</label>
|
||||
<input type="text" wire:model="memberComment" placeholder="Description" class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-sm">
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="checkbox" wire:model="memberNomatch" id="memberNomatch" class="rounded border-gray-300 dark:border-gray-600">
|
||||
<label for="memberNomatch" class="text-sm">
|
||||
<span class="font-medium">No Match</span>
|
||||
<span class="text-gray-500">(Exclude this IP from the set)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 justify-end mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button wire:click="cancelIPSetMemberForm" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium">
|
||||
Cancel
|
||||
</button>
|
||||
<button wire:click="saveIPSetMember" wire:loading.attr="disabled" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||
<span wire:loading.remove wire:target="saveIPSetMember">Add Member</span>
|
||||
<span wire:loading wire:target="saveIPSetMember">Adding...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Delete IPSet Member Modal --}}
|
||||
@if($showDeleteIPSetMemberModal && $deletingMemberCidr)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-md bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="cancelDeleteIPSetMember" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="w-12 h-12 rounded-full bg-error/10 flex items-center justify-center flex-shrink-0">
|
||||
<x-ri-close-circle-fill class="size-6 text-error" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Remove Member</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Remove "<span class="font-mono font-semibold">{{ $deletingMemberCidr }}</span>" from {{ $selectedIPSetName }}?</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 justify-end">
|
||||
<x-proxmox::ui.button wire:click="cancelDeleteIPSetMember" variant="secondary">
|
||||
Cancel
|
||||
</x-proxmox::ui.button>
|
||||
<x-proxmox::ui.button wire:click="deleteIPSetMember" wire:loading.attr="disabled" variant="danger">
|
||||
<span wire:loading.remove wire:target="deleteIPSetMember">Remove</span>
|
||||
<span wire:loading wire:target="deleteIPSetMember">Removing...</span>
|
||||
</x-proxmox::ui.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Save Configuration Modal --}}
|
||||
@if($showSaveConfigModal)
|
||||
<div class="fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 shadow-2xl p-6">
|
||||
<button wire:click="closeSaveConfigModal" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
</button>
|
||||
<div class="flex items-start gap-4 mb-6">
|
||||
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0">
|
||||
<x-ri-save-3-line class="size-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-1">Save Current Configuration</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Save your current firewall rules and policies as a reusable configuration</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">Configuration Name</label>
|
||||
<input type="text" wire:model="newConfigName" placeholder="e.g., Web Server Rules"
|
||||
class="w-full px-4 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:border-primary focus:outline-none transition-colors">
|
||||
@error('newConfigName')
|
||||
<p class="text-error text-xs mt-1">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-lg p-4">
|
||||
<h4 class="text-sm font-semibold mb-3">Configuration Summary</h4>
|
||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||
<div class="flex items-center justify-between p-2 bg-white dark:bg-gray-900 rounded">
|
||||
<span class="text-gray-500">Rules</span>
|
||||
<span class="font-semibold">{{ count($rules) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-2 bg-white dark:bg-gray-900 rounded">
|
||||
<span class="text-gray-500">Incoming Policy</span>
|
||||
<span class="font-semibold {{ $policyIn === 'DROP' ? 'text-error' : 'text-success' }}">{{ $policyIn }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-2 bg-white dark:bg-gray-900 rounded">
|
||||
<span class="text-gray-500">Outgoing Policy</span>
|
||||
<span class="font-semibold {{ $policyOut === 'DROP' ? 'text-error' : 'text-success' }}">{{ $policyOut }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 justify-end mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button wire:click="closeSaveConfigModal" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm font-medium">
|
||||
Cancel
|
||||
</button>
|
||||
<button wire:click="saveConfiguration" wire:loading.attr="disabled" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||
<span wire:loading.remove wire:target="saveConfiguration">Save Configuration</span>
|
||||
<span wire:loading wire:target="saveConfiguration">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user