Private
Public Access
1
0

Refactor modal close behavior and improve styling

- Changed modal close event from `@click.away` to `@click.self` for better control over closing modals.
- Updated various modal button styles for consistency and improved accessibility.
- Adjusted transition durations for modal animations to enhance user experience.
- Added aria-labels to close buttons for better screen reader support.
This commit is contained in:
2026-02-23 20:02:41 -05:00
parent f394483daa
commit af62a16354
45 changed files with 393 additions and 4700 deletions

View File

@@ -38,12 +38,7 @@
localStorage.setItem('overview-notice-collapsed', this.isCollapsed);
}
}"
x-init="
isAdvancedView = localStorage.getItem('proxmox-view-mode') === 'advanced';
window.addEventListener('proxmox-view-mode-changed', (e) => {
isAdvancedView = e.detail.isAdvancedView;
});
"
@proxmox-view-mode-changed.window="isAdvancedView = $event.detail.isAdvancedView"
x-show="isAdvancedView"
x-transition>
<div class="flex items-start gap-3">
@@ -103,7 +98,7 @@
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"
@click.self="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">
@@ -314,7 +309,7 @@
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"
@click.self="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">
@@ -470,7 +465,7 @@
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="showHostnameEditModal = false; $wire.cancelEditingHostname()"
@click.self="showHostnameEditModal = false; $wire.cancelEditingHostname()"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative bg-background-secondary border border-neutral rounded-lg shadow-xl w-full max-w-xl mx-4 p-5 space-y-4">
@@ -634,7 +629,7 @@
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="showPasswordViewModal = false"
@click.self="showPasswordViewModal = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;"
x-data="{
@@ -692,7 +687,7 @@
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="showPasswordEditModal = false; $wire.cancelEditingPassword()"
@click.self="showPasswordEditModal = false; $wire.cancelEditingPassword()"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative bg-background-secondary border border-neutral rounded-lg shadow-xl w-full max-w-xl mx-4 p-5 space-y-4">
@@ -796,7 +791,7 @@
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"
@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;">
@@ -894,7 +889,7 @@
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"
@click.self="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">
@@ -1096,7 +1091,7 @@
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"
@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;">
@@ -1163,7 +1158,7 @@
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="showExportErrorModal = false"
@click.self="showExportErrorModal = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-md bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
@@ -1269,7 +1264,7 @@
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"
@click.self="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">
@@ -1307,27 +1302,27 @@
<div class="mt-2 border-l-4 bg-info/10 border border-info/30 p-3 rounded-md">
<p class="text-sm dark:text-gray-100 font-bold leading-relaxed">
<p class="text-sm text-base/90 font-bold leading-relaxed">
Windows Server Networking (IPv4/IPv6 Setup):
</p>
<p class="text-sm dark:text-gray-200 italic leading-relaxed ">
<p class="text-sm text-base/70 italic leading-relaxed ">
Cloud-init does not automatically configure IPv4 or IPv6 networking on Windows Server installations. Please configure the addresses manually as assigned in the control panel of your installation.
</p>
</div>
<div class="mt-2 border-l-4 bg-info/10 border border-info/30 p-3 rounded-md">
<p class="text-sm dark:text-gray-100 font-bold leading-relaxed">
<p class="text-sm text-base/90 font-bold leading-relaxed">
Windows Server Activation (Licensing):
</p>
<p class="text-sm dark:text-gray-100 italic leading-relaxed ">
<p class="text-sm text-base/90 italic leading-relaxed ">
No Windows Server license key is provided by Virtnet.bond. It is the responsibility of the account holder
to provide and maintain active and compliant licensing with all Windows Server products.
</p>
</div>
<div class="mt-2 border-l-4 bg-info/10 border border-info/30 p-3 rounded-md">
<p class="text-sm dark:text-gray-100 font-bold leading-relaxed">
<p class="text-sm text-base/90 font-bold leading-relaxed">
Periodic Licensing Audits and User Compliance:
</p>
<p class="text-sm dark:text-gray-200 italic leading-relaxed ">
<p class="text-sm text-base/70 italic leading-relaxed ">
Virtnet.bond reserves the right to perform periodic audits of Windows Server installations to ensure compliance with Microsoft's licensing terms. Non-compliant installations may be subject to suspension or termination. If you have any questions regarding licensing compliance, please contact our support team.
</p>
</div>
@@ -1442,7 +1437,6 @@
);
if (historyLength > 0) {
console.log('Creating chart for interface:', interfaceId);
requestAnimationFrame(() => {
setTimeout(() => {
if (!charts[interfaceId]) {
@@ -1455,7 +1449,6 @@
}
if (retries > 0) {
console.log(`Chart data not ready for ${interfaceId}, retrying... (${retries} attempts left)`);
setTimeout(() => attemptCreateChart(retries - 1, delay), delay);
}
};
@@ -1594,15 +1587,6 @@
let uploadRate = interfaceData.uploadRate ?? 0;
// Debug: Log interface data to verify each interface gets unique data
console.log(`Updating interface ${interfaceId} (${interfaceData.id}):`, {
downloadRate,
uploadRate,
totalIn: interfaceData.totalIn,
totalOut: interfaceData.totalOut,
hasHistory: !!interfaceData.history,
historyLength: interfaceData.history?.download?.length || 0
});
// Update peak rates
if (!peakRates[interfaceId]) {
peakRates[interfaceId] = { download: 0, upload: 0 };
@@ -1651,7 +1635,6 @@
interfaceData.history.upload?.length || 0
);
if (historyLength > 0) {
console.log('Creating chart for', interfaceId, 'on data update (container was open)');
setTimeout(() => createChart(interfaceId, interfaceData), 100);
}
}
@@ -1800,15 +1783,6 @@
});
// Debug logging
console.log('Chart data for', interfaceId, {
interfaceName: interfaceData.name,
downloadData: downloadData,
uploadData: uploadData,
downloadMax: downloadData.length > 0 ? Math.max(...downloadData, 0) : 0,
uploadMax: uploadData.length > 0 ? Math.max(...uploadData, 0) : 0,
hasData: downloadData.some(v => v > 0) || uploadData.some(v => v > 0)
});
// Calculate 95th percentile from combined traffic
const combinedTraffic = downloadData.map((d, i) => d + (uploadData[i] || 0));
const sortedTraffic = [...combinedTraffic].filter(v => v > 0).sort((a, b) => a - b);
@@ -1818,14 +1792,6 @@
p95Value = sortedTraffic[Math.min(p95Index, sortedTraffic.length - 1)];
}
const p95Data = Array(downloadData.length).fill(p95Value);
console.log('95th Percentile calculation:', {
combinedTraffic: combinedTraffic,
sortedTraffic: sortedTraffic,
p95Value: p95Value,
p95DataLength: p95Data.length
});
// Create a local formatRate function that's guaranteed to be in scope
const formatRateForChart = (bytesPerSec) => {
if (typeof bytesPerSec !== 'number' || isNaN(bytesPerSec)) {
@@ -1939,9 +1905,6 @@
const poll = async () => {
if (pollInFlight || !pollingEndpoint) return;
pollInFlight = true;
console.log('Polling network data from:', pollingEndpoint);
try {
const csrfElement = document.querySelector('meta[name="csrf-token"]');
const csrfToken = csrfElement ? csrfElement.content : '';
@@ -1955,13 +1918,8 @@
},
credentials: 'same-origin',
});
console.log('Network Traffic Response status:', response.status);
if (response.ok) {
const data = await response.json();
console.log('Network Traffic Data received:', data);
const container = document.getElementById('network-traffic-container');
const wasEmpty = !networkData || !networkData.interfaces || networkData.interfaces.length === 0;
const hasExistingInterfaces = container && container.querySelectorAll('.chart-toggle').length > 0;
@@ -1983,7 +1941,6 @@
// 3. No existing interfaces found in container
if (data.interfaces && data.interfaces.length > 0) {
if (showsPlaceholder || !hasExistingInterfaces || wasEmpty) {
console.log('Rendering interfaces - showsPlaceholder:', showsPlaceholder, 'hasExistingInterfaces:', hasExistingInterfaces, 'wasEmpty:', wasEmpty);
// Render immediately - no delay
renderInterfaces();
// Also update data immediately after render to ensure values are set
@@ -2027,7 +1984,6 @@
);
if (historyLength > 0) {
console.log('Creating chart for interface:', interfaceId, 'after tab switch');
requestAnimationFrame(() => {
setTimeout(() => {
if (!charts[interfaceId]) {
@@ -2087,10 +2043,6 @@
const container = document.getElementById('network-traffic-container');
pollingEndpoint = container?.dataset?.liveTrafficEndpoint || null;
console.log('Network Traffic Bootstrap: Container found:', !!container);
console.log('Network Traffic Bootstrap: Endpoint:', pollingEndpoint);
if (pollingEndpoint && container) {
// Check if container exists and has endpoint - be less strict about visibility
// Sometimes container exists but offsetParent check fails initially
@@ -2099,8 +2051,6 @@
if (isVisible || containerExists) {
// Always poll immediately on bootstrap (don't wait for interval)
console.log('Network Traffic: Polling immediately on bootstrap...');
// Poll immediately - don't wait
poll();
@@ -2109,11 +2059,9 @@
setTimeout(() => {
if (!pollInterval) {
pollInterval = setInterval(poll, 30000);
console.log('Network Traffic: Polling interval set up (30 seconds)');
}
}, 100);
} else {
console.log('Network Traffic: Container not ready, will retry via visibility check');
}
}
};
@@ -2145,7 +2093,6 @@
}
} else {
// Successfully bootstrapped, also try immediate poll as backup
console.log('Network Traffic: Bootstrap successful, ensuring immediate poll...');
}
// Re-initialize on Livewire lifecycle events (immediate)
@@ -2208,12 +2155,6 @@
);
if (historyLength > 0) {
console.log('Creating chart for interface:', interfaceId, 'with data:', {
id: interfaceData.id,
name: interfaceData.name,
historyLength: historyLength
});
requestAnimationFrame(() => {
setTimeout(() => {
if (!charts[interfaceId]) {
@@ -2226,7 +2167,6 @@
}
if (retries > 0) {
console.log(`Chart data not ready for ${interfaceId}, retrying... (${retries} attempts left)`);
setTimeout(() => attemptCreateChart(retries - 1, delay), delay);
} else {
console.warn('Failed to create chart for', interfaceId, 'after multiple attempts');
@@ -2256,7 +2196,6 @@
// 1. No polling interval (not initialized), OR
// 2. Shows placeholder (was reset)
if (isVisible && hasEndpoint && (showsPlaceholder || !pollInterval)) {
console.log('Network Traffic: Container needs initialization (placeholder:', showsPlaceholder, 'no interval:', !pollInterval, '), re-initializing...');
bootstrap();
}
@@ -2266,7 +2205,6 @@
if (!pollingEndpoint) {
pollingEndpoint = hasEndpoint;
}
console.log('Network Traffic: Container visible with endpoint but no polling active, polling immediately...');
poll();
setTimeout(() => {
if (!pollInterval) {
@@ -2281,7 +2219,6 @@
// Register cleanup function for Livewire navigation
window.__networkTrafficCleanup = () => {
console.log('Network Traffic: Cleaning up for navigation...');
if (visibilityCheckInterval) {
clearInterval(visibilityCheckInterval);
}