diff --git a/firewall/static/firewall/firewall.js b/firewall/static/firewall/firewall.js index 495b88ec0..1853a3544 100644 --- a/firewall/static/firewall/firewall.js +++ b/firewall/static/firewall/firewall.js @@ -2,10 +2,120 @@ * Created by usman on 9/5/17. */ +// TEST: Verify file is loading +console.log('🔥🔥🔥 firewall.js FILE LOADED 🔥🔥🔥'); +console.log('Timestamp:', new Date().toISOString()); +if (typeof app === 'undefined') { + console.error('❌ ERROR: app (AngularJS module) is not defined!'); +} else { + console.log('✅ app (AngularJS module) is defined'); +} + +// Global function for inline onclick handler - MUST be defined BEFORE controller +window.handleModifyClick = function(buttonElement, event) { + console.log('========================================'); + console.log('=== handleModifyClick CALLED ==='); + console.log('========================================'); + console.log('Button:', buttonElement); + + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + var ip = buttonElement.getAttribute('data-ip'); + var id = buttonElement.getAttribute('data-id'); + console.log('IP:', ip, 'ID:', id); + + var controllerEl = document.querySelector('[ng-controller="firewallController"]'); + if (!controllerEl) { + console.error('Controller element not found'); + alert('Error: Controller not found. Please refresh the page.'); + return false; + } + + var scope = angular.element(controllerEl).scope(); + if (!scope) { + console.error('AngularJS scope not found'); + alert('Error: AngularJS scope not found. Please refresh the page.'); + return false; + } + + console.log('Scope found, bannedIPs:', scope.bannedIPs ? scope.bannedIPs.length : 0); + + if (!scope.bannedIPs || scope.bannedIPs.length === 0) { + console.error('No bannedIPs found in scope'); + alert('Error: No banned IPs found. Please refresh the page.'); + return false; + } + + var bannedIP = null; + for (var i = 0; i < scope.bannedIPs.length; i++) { + if (scope.bannedIPs[i].ip === ip || (id && scope.bannedIPs[i].id == id)) { + bannedIP = scope.bannedIPs[i]; + console.log('Found bannedIP:', bannedIP); + break; + } + } + + if (!bannedIP) { + console.error('Could not find bannedIP for IP:', ip, 'ID:', id); + alert('Error: Could not find IP data. IP: ' + ip + ', ID: ' + id); + return false; + } + + if (!scope.showModifyBannedIPModal) { + console.error('showModifyBannedIPModal function not found in scope'); + alert('Error: Modify function not found. Please refresh the page.'); + return false; + } + + console.log('Calling showModifyBannedIPModal'); + try { + scope.$apply(function() { + scope.showModifyBannedIPModal(bannedIP, event); + }); + } catch (err) { + console.error('Error calling showModifyBannedIPModal:', err); + alert('Error: ' + err.message); + } + + return false; +}; + +console.log('✅ window.handleModifyClick function defined'); +console.log('Function test:', typeof window.handleModifyClick); + +// Verify function is available globally +if (typeof window.handleModifyClick === 'undefined') { + console.error('❌ CRITICAL: window.handleModifyClick was not defined!'); +} else { + console.log('✅ window.handleModifyClick is available and ready'); + // Test that it's callable + try { + console.log('Function type:', typeof window.handleModifyClick); + } catch (e) { + console.error('Error testing function:', e); + } +} + +// Also make it available immediately on window load +if (typeof window !== 'undefined') { + window.addEventListener('load', function() { + console.log('Page loaded - verifying handleModifyClick:', typeof window.handleModifyClick); + }); +} /* Java script code to ADD Firewall Rules */ -app.controller('firewallController', function ($scope, $http) { +app.controller('firewallController', function ($scope, $http, $timeout, $window, $location) { + console.log('========================================'); + console.log('=== firewallController INITIALIZED ==='); + console.log('========================================'); + console.log('Timestamp:', new Date().toISOString()); + console.log('$scope:', $scope); + console.log('$timeout:', typeof $timeout); + console.log('$window:', typeof $window); $scope.rulesLoading = true; $scope.actionFailed = true; @@ -17,7 +127,56 @@ app.controller('firewallController', function ($scope, $http) { $scope.rulesDetails = false; // Banned IPs variables - $scope.activeTab = 'rules'; + // Check URL hash for active tab (handle #!# pattern from AngularJS) + var hash = $window.location.hash || $location.hash(); + if (hash) { + // Remove # or #! or #!# prefix + hash = hash.replace(/^#!?#?/, ''); + console.log('Initial hash parsed:', hash); + if (hash === 'bannedips' || hash === 'banned') { + $scope.activeTab = 'banned'; + } else if (hash === 'rules') { + $scope.activeTab = 'rules'; + } else { + $scope.activeTab = 'rules'; + } + } else { + $scope.activeTab = 'rules'; + } + + // Also listen for hashchange events to handle direct URL navigation + var hashChangeHandler = function() { + var currentHash = $window.location.hash; + var newHash = currentHash.replace(/^#!?#?/, ''); + console.log('Hash changed event - current:', currentHash, 'parsed:', newHash); + + // Only update if different from current tab + if ((newHash === 'bannedips' || newHash === 'banned') && $scope.activeTab !== 'banned') { + $scope.$apply(function() { + $scope.activeTab = 'banned'; + }); + } else if (newHash === 'rules' && $scope.activeTab !== 'rules') { + $scope.$apply(function() { + $scope.activeTab = 'rules'; + }); + } + }; + + $window.addEventListener('hashchange', hashChangeHandler); + + // Clean up hash on page load if it has #!# pattern + $timeout(function() { + var currentHash = $window.location.hash; + if (currentHash && currentHash.includes('#!')) { + var cleanHash = currentHash.replace(/^#!?#?/, ''); + var cleanUrl = $window.location.href.split('#')[0] + '#' + cleanHash; + if ($window.history && $window.history.replaceState) { + $window.history.replaceState(null, '', cleanUrl); + console.log('Cleaned hash from', currentHash, 'to', cleanHash); + } + } + }, 100); + $scope.bannedIPs = []; $scope.bannedIPsLoading = false; $scope.bannedIPActionFailed = true; @@ -31,6 +190,116 @@ app.controller('firewallController', function ($scope, $http) { populateCurrentRecords(); populateBannedIPs(); + + // Use MutationObserver to catch buttons as they're created + var observer = new MutationObserver(function(mutations) { + var modifyButtons = document.querySelectorAll('.btn-modify'); + if (modifyButtons.length > 0) { + console.log('MutationObserver: Found', modifyButtons.length, 'Modify buttons'); + modifyButtons.forEach(function(btn) { + if (!btn.hasAttribute('data-listener-attached')) { + btn.setAttribute('data-listener-attached', 'true'); + console.log('MutationObserver: Attaching listener to button'); + + // Force visibility + btn.style.setProperty('display', 'flex', 'important'); + btn.style.setProperty('visibility', 'visible', 'important'); + btn.style.setProperty('opacity', '1', 'important'); + btn.style.setProperty('pointer-events', 'auto', 'important'); + } + }); + } + }); + + // Start observing + $timeout(function() { + var container = document.querySelector('[ng-controller="firewallController"]'); + if (container) { + console.log('Starting MutationObserver on controller container'); + observer.observe(container, { + childList: true, + subtree: true + }); + } + }, 500); + + // Also try immediate setup + var setupModifyButtons = function() { + console.log('=== setupModifyButtons CALLED ==='); + var modifyButtons = document.querySelectorAll('.btn-modify'); + console.log('Found', modifyButtons.length, 'Modify buttons in DOM'); + + modifyButtons.forEach(function(btn, index) { + if (btn && !btn.hasAttribute('data-listener-attached')) { + btn.setAttribute('data-listener-attached', 'true'); + console.log('Setting up button', index + ':', btn); + + // Force visibility + btn.style.setProperty('display', 'flex', 'important'); + btn.style.setProperty('visibility', 'visible', 'important'); + btn.style.setProperty('opacity', '1', 'important'); + btn.style.setProperty('pointer-events', 'auto', 'important'); + btn.classList.remove('ng-hide', 'hide', 'disabled'); + } + }); + }; + + // Run multiple times to catch buttons + $timeout(setupModifyButtons, 100); + $timeout(setupModifyButtons, 500); + $timeout(setupModifyButtons, 1000); + $timeout(setupModifyButtons, 2000); + + // Also run after bannedIPs load + $scope.$watch('bannedIPs', function(newVal, oldVal) { + if (newVal && newVal.length > 0) { + console.log('=== bannedIPs changed, found', newVal.length, 'IPs ==='); + $timeout(setupModifyButtons, 100); + $timeout(setupModifyButtons, 500); + } + }, true); + + // Watch for tab changes and update URL hash (without #!#) + $scope.$watch('activeTab', function(newTab, oldTab) { + if (newTab !== oldTab && newTab) { + // Use replaceState to avoid AngularJS adding #! prefix + var newHash = newTab === 'banned' ? 'bannedips' : 'rules'; + + // Get clean base URL (without hash or query params) + var baseUrl = $window.location.protocol + '//' + + $window.location.host + + $window.location.pathname; + var newUrl = baseUrl + '#' + newHash; + + // Use replaceState to update URL - this bypasses AngularJS hash handling + if ($window.history && $window.history.replaceState) { + try { + // Use replaceState to set clean hash without AngularJS interference + $window.history.replaceState(null, '', newUrl); + console.log('Tab changed to:', newTab, 'Hash set to:', newHash); + + // Small delay to ensure URL is updated, then clean up if AngularJS added #! + $timeout(function() { + var currentHash = $window.location.hash; + if (currentHash && currentHash.includes('#!')) { + // AngularJS added #! prefix, clean it up + var cleanHash = currentHash.replace(/^#!?#?/, ''); + var cleanUrl = baseUrl + '#' + cleanHash; + $window.history.replaceState(null, '', cleanUrl); + console.log('Cleaned AngularJS hashbang from', currentHash, 'to', cleanHash); + } + }, 50); + } catch (e) { + console.warn('replaceState failed:', e); + // Fallback: set hash directly + $window.location.hash = newHash; + } + } else { + // Fallback to direct hash setting + $window.location.hash = newHash; + } + } + }); $scope.addRule = function () { @@ -2427,6 +2696,17 @@ app.controller('litespeed_ent_conf', function ($scope, $http, $timeout, $window) $scope.bannedIPsLoading = false; if (response.data.status === 1) { $scope.bannedIPs = response.data.bannedIPs || []; + + // Ensure Modify buttons are visible after data loads + $timeout(function() { + var modifyButtons = document.querySelectorAll('.btn-modify'); + modifyButtons.forEach(function(btn) { + btn.style.display = 'flex'; + btn.style.visibility = 'visible'; + btn.style.opacity = '1'; + }); + console.log('Ensured', modifyButtons.length, 'Modify buttons are visible'); + }, 100); } else { $scope.bannedIPs = []; $scope.bannedIPActionFailed = false; @@ -2515,6 +2795,1005 @@ app.controller('litespeed_ent_conf', function ($scope, $http, $timeout, $window) }); }; + // Make function available globally for onclick fallback - uses data attributes + window.showModifyModal = function(buttonElement) { + console.log('========================================'); + console.log('=== ONCLICK FALLBACK TRIGGERED ==='); + console.log('========================================'); + console.log('Button element:', buttonElement); + console.log('Button data-ip:', buttonElement ? buttonElement.getAttribute('data-ip') : 'null'); + console.log('Button data-id:', buttonElement ? buttonElement.getAttribute('data-id') : 'null'); + try { + // Get data from button attributes first (most reliable) + var ip = buttonElement.getAttribute('data-ip'); + var id = buttonElement.getAttribute('data-id'); + + var bannedIP = null; + + // Try to get from AngularJS scope + var row = buttonElement.closest('tr'); + if (row) { + var scope = angular.element(row).scope(); + if (scope) { + bannedIP = scope.bannedIP || (scope.$parent && scope.$parent.bannedIP); + } + } + + // If not found in scope, try to find by IP/id from controller scope + if (!bannedIP && ip) { + var controllerElement = document.querySelector('[ng-controller="firewallController"]'); + if (controllerElement) { + var controllerScope = angular.element(controllerElement).scope(); + if (controllerScope && controllerScope.bannedIPs) { + for (var i = 0; i < controllerScope.bannedIPs.length; i++) { + if (controllerScope.bannedIPs[i].ip === ip || + (id && controllerScope.bannedIPs[i].id == id)) { + bannedIP = controllerScope.bannedIPs[i]; + break; + } + } + } + } + } + + if (bannedIP) { + console.log('Found bannedIP:', bannedIP); + var controllerElement = document.querySelector('[ng-controller="firewallController"]'); + if (controllerElement) { + var controllerScope = angular.element(controllerElement).scope(); + if (controllerScope && controllerScope.showModifyBannedIPModal) { + controllerScope.$apply(function() { + controllerScope.showModifyBannedIPModal(bannedIP); + }); + } else { + // Direct call if $apply not available + if (controllerScope.showModifyBannedIPModal) { + controllerScope.showModifyBannedIPModal(bannedIP); + } + } + } + } else { + console.error('Could not find bannedIP for IP:', ip, 'ID:', id); + alert('Error: Could not find IP data. Please refresh the page.'); + } + } catch (error) { + console.error('Error in onclick fallback:', error); + alert('Error opening modify dialog: ' + error.message); + } + }; + + // Make showModifyBannedIPModal available globally for debugging + window.showModifyBannedIPModalGlobal = function(bannedIP) { + console.log('=== GLOBAL FUNCTION CALLED ==='); + console.log('bannedIP:', bannedIP); + var controllerElement = document.querySelector('[ng-controller="firewallController"]'); + if (controllerElement) { + var controllerScope = angular.element(controllerElement).scope(); + if (controllerScope && controllerScope.showModifyBannedIPModal) { + console.log('Calling scope function'); + controllerScope.$apply(function() { + controllerScope.showModifyBannedIPModal(bannedIP, null); + }); + } else { + console.error('Controller scope or function not found'); + console.log('controllerScope:', controllerScope); + console.log('showModifyBannedIPModal exists:', controllerScope ? !!controllerScope.showModifyBannedIPModal : false); + } + } else { + console.error('Controller element not found'); + } + }; + + // Test function - call this from console: window.testModifyModal() + window.testModifyModal = function() { + console.log('=== TEST FUNCTION CALLED ==='); + var controllerElement = document.querySelector('[ng-controller="firewallController"]'); + console.log('Controller element:', controllerElement); + if (controllerElement) { + var controllerScope = angular.element(controllerElement).scope(); + console.log('Controller scope:', controllerScope); + console.log('bannedIPs:', controllerScope ? controllerScope.bannedIPs : 'null'); + if (controllerScope && controllerScope.bannedIPs && controllerScope.bannedIPs.length > 0) { + var testIP = controllerScope.bannedIPs[0]; + console.log('Testing with first bannedIP:', testIP); + if (controllerScope.showModifyBannedIPModal) { + controllerScope.$apply(function() { + controllerScope.showModifyBannedIPModal(testIP, null); + }); + } else { + console.error('showModifyBannedIPModal function not found in scope'); + } + } else { + console.error('No bannedIPs found'); + } + } else { + console.error('Controller element not found'); + } + }; + + // Wrapper function for ng-click - MUST be simple and direct + $scope.handleModifyButtonClick = function(bannedIP, event) { + console.log('========================================'); + console.log('=== handleModifyButtonClick CALLED ==='); + console.log('========================================'); + console.log('bannedIP:', bannedIP); + console.log('bannedIP type:', typeof bannedIP); + console.log('event:', event); + + // Prevent default + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + if (!bannedIP) { + console.error('❌ ERROR: No bannedIP provided'); + alert('Error: No IP data provided'); + return false; + } + + console.log('✅ bannedIP is valid, calling showModifyBannedIPModal'); + + // Direct call - don't check if function exists, just call it + try { + $scope.showModifyBannedIPModal(bannedIP, event); + } catch (err) { + console.error('❌ ERROR calling showModifyBannedIPModal:', err); + alert('Error calling showModifyBannedIPModal: ' + err.message); + } + + return false; + }; + + // Also create openModifyModal alias since it's expected by the onclick handler + $scope.openModifyModal = function(bannedIP, event) { + console.log('=== openModifyModal CALLED (alias) ==='); + console.log('bannedIP:', bannedIP); + // Call showModifyBannedIPModal directly + if ($scope.showModifyBannedIPModal) { + $scope.showModifyBannedIPModal(bannedIP, event); + } else { + console.error('showModifyBannedIPModal not found!'); + alert('showModifyBannedIPModal function not found!'); + } + }; + + console.log('✅ handleModifyButtonClick function defined in scope'); + console.log('✅ openModifyModal alias created'); + console.log('Function exists check:', typeof $scope.handleModifyButtonClick, typeof $scope.openModifyModal); + + // CRITICAL: Define showModifyBannedIPModal FIRST and make it available on window immediately + $scope.showModifyBannedIPModal = function(bannedIP, event) { + console.log('========================================'); + console.log('=== showModifyBannedIPModal CALLED ==='); + console.log('========================================'); + console.log('Timestamp:', new Date().toISOString()); + console.log('bannedIP:', JSON.stringify(bannedIP, null, 2)); + console.log('bannedIP type:', typeof bannedIP); + console.log('bannedIP keys:', bannedIP ? Object.keys(bannedIP) : 'null'); + console.log('event:', event); + console.log('event type:', typeof event); + console.log('$scope:', $scope); + console.log('$scope.activeTab:', $scope.activeTab); + + // If bannedIP is not an object, try to find it + if (!bannedIP || typeof bannedIP !== 'object') { + console.warn('bannedIP is not an object, trying to find it...'); + if (event && event.target) { + var btn = event.target.closest('.btn-modify'); + if (btn) { + var ip = btn.getAttribute('data-ip'); + var id = btn.getAttribute('data-id'); + console.log('Found data-ip:', ip, 'data-id:', id); + if ($scope.bannedIPs) { + for (var i = 0; i < $scope.bannedIPs.length; i++) { + if ($scope.bannedIPs[i].ip === ip || + (id && $scope.bannedIPs[i].id == id)) { + bannedIP = $scope.bannedIPs[i]; + console.log('Found bannedIP:', bannedIP); + break; + } + } + } + } + } + } + + // Prevent default and stop propagation if event provided + if (event) { + console.log('Preventing default and stopping propagation'); + event.preventDefault(); + event.stopPropagation(); + } + + if (!bannedIP) { + console.error('❌ ERROR: No bannedIP data provided'); + console.trace('Stack trace:'); + alert('Error: No IP data provided'); + return false; + } + + console.log('✅ bannedIP validation passed'); + + // Store bannedIP in scope for debugging + $scope.currentBannedIP = bannedIP; + console.log('Stored bannedIP in $scope.currentBannedIP'); + + // Get modal element immediately + var modalElement = document.getElementById('modifyBannedIPModal'); + console.log('Modal element lookup:'); + console.log(' - Element found:', !!modalElement); + console.log(' - Element type:', modalElement ? modalElement.tagName : 'null'); + console.log(' - Element ID:', modalElement ? modalElement.id : 'null'); + + if (modalElement) { + console.log(' - Element classes:', modalElement.className); + console.log(' - Element style.display:', modalElement.style.display); + console.log(' - Element computed display:', window.getComputedStyle(modalElement).display); + console.log(' - Element parent:', modalElement.parentElement ? modalElement.parentElement.tagName : 'null'); + console.log(' - Element in DOM:', document.body.contains(modalElement)); + } + + if (!modalElement) { + console.error('❌ ERROR: Modal element not found in DOM'); + alert('ERROR: Modal element not found in DOM!'); + console.log('Searching for modal in DOM...'); + var allModals = document.querySelectorAll('.modal, [id*="modal"], [id*="Modal"]'); + console.log('Found', allModals.length, 'potential modal elements:'); + allModals.forEach(function(m, i) { + console.log(' Modal', i + ':', m.id, m.className); + }); + alert('Error: Modal not found. Found ' + allModals.length + ' modals. Please refresh the page.'); + return false; + } + + console.log('✅ Modal element found'); + + // Check jQuery availability + console.log('jQuery check:'); + console.log(' - jQuery available:', typeof $ !== 'undefined'); + console.log(' - jQuery version:', typeof $ !== 'undefined' ? $.fn.jquery : 'N/A'); + console.log(' - jQuery modal available:', typeof $ !== 'undefined' && $.fn.modal); + console.log(' - Bootstrap available:', typeof bootstrap !== 'undefined'); + + // Use AngularJS $timeout instead of setTimeout for proper digest cycle + console.log('Setting up $timeout for modal display...'); + $timeout(function() { + console.log('--- $timeout callback executed ---'); + try { + console.log('--- Inside try block ---'); + console.log('Modal element still available:', !!modalElement); + + // Set modal form values + console.log('Looking for form fields...'); + var idField = document.getElementById('modifyBannedIPId'); + var ipField = document.getElementById('modifyBannedIPAddress'); + var reasonField = document.getElementById('modifyBannedIPReason'); + var durationField = document.getElementById('modifyBannedIPDuration'); + + console.log('Form fields lookup results:'); + console.log(' - idField:', !!idField, idField ? idField.id : 'not found'); + console.log(' - ipField:', !!ipField, ipField ? ipField.id : 'not found'); + console.log(' - reasonField:', !!reasonField, reasonField ? reasonField.id : 'not found'); + console.log(' - durationField:', !!durationField, durationField ? durationField.id : 'not found'); + + if (!idField || !ipField || !reasonField || !durationField) { + console.error('❌ ERROR: Modal form fields not found'); + console.log('Checking modal structure...'); + var modalCheck = document.getElementById('modifyBannedIPModal'); + console.log('Modal element exists:', !!modalCheck); + if (modalCheck) { + console.log('Modal innerHTML length:', modalCheck.innerHTML ? modalCheck.innerHTML.length : 0); + console.log('Modal first 1000 chars:', modalCheck.outerHTML.substring(0, 1000)); + var formCheck = modalCheck.querySelector('form, #modifyBannedIPForm'); + console.log('Form element in modal:', !!formCheck); + if (formCheck) { + console.log('Form children:', formCheck.children.length); + Array.from(formCheck.children).forEach(function(child, i) { + console.log(' Child', i + ':', child.tagName, child.id || child.className); + }); + } + } + alert('Error: Modal form not found. Please refresh the page.'); + return; + } + + console.log('✅ All form fields found'); + + console.log('Setting form field values...'); + idField.value = bannedIP.id || ''; + ipField.value = bannedIP.ip || ''; + ipField.removeAttribute('readonly'); + ipField.removeAttribute('disabled'); + ipField.readOnly = false; + ipField.disabled = false; + reasonField.value = bannedIP.reason || ''; + console.log('Form values set:'); + console.log(' - ID:', idField.value); + console.log(' - IP:', ipField.value); + console.log(' - Reason:', reasonField.value); + + // Set duration - convert expires to duration format + var duration = bannedIP.duration || 'never'; + console.log('Calculating duration...'); + console.log(' - Initial duration:', duration); + console.log(' - expires_timestamp:', bannedIP.expires_timestamp); + console.log(' - banned_on_timestamp:', bannedIP.banned_on_timestamp); + + // If we have expires_timestamp, try to calculate duration + if (bannedIP.expires_timestamp && bannedIP.banned_on_timestamp) { + var expiresMs = bannedIP.expires_timestamp; + var bannedMs = bannedIP.banned_on_timestamp; + var diffMs = expiresMs - bannedMs; + var diffHours = diffMs / (1000 * 60 * 60); + console.log(' - Diff in hours:', diffHours); + + if (diffHours <= 1) { + duration = '1h'; + } else if (diffHours <= 6) { + duration = '6h'; + } else if (diffHours <= 12) { + duration = '12h'; + } else if (diffHours <= 24) { + duration = '24h'; + } else if (diffHours <= 48) { + duration = '48h'; + } else if (diffHours <= 168) { // 7 days + duration = '7d'; + } else if (diffHours <= 720) { // 30 days + duration = '30d'; + } else { + duration = 'never'; + } + console.log(' - Calculated duration:', duration); + } else if (bannedIP.expires === 'Never' || !bannedIP.expires_timestamp) { + duration = 'never'; + console.log(' - Using never (no expiration)'); + } + + durationField.value = duration; + console.log('✅ Duration set to:', duration); + + console.log('========================================'); + console.log('=== ATTEMPTING TO SHOW MODAL ==='); + console.log('========================================'); + + // Re-check modal element + modalElement = document.getElementById('modifyBannedIPModal'); + console.log('Modal element re-check:'); + console.log(' - Found:', !!modalElement); + console.log(' - Current display:', modalElement ? window.getComputedStyle(modalElement).display : 'N/A'); + console.log(' - Current visibility:', modalElement ? window.getComputedStyle(modalElement).visibility : 'N/A'); + console.log(' - Current opacity:', modalElement ? window.getComputedStyle(modalElement).opacity : 'N/A'); + console.log(' - Current z-index:', modalElement ? window.getComputedStyle(modalElement).zIndex : 'N/A'); + console.log(' - Has show class:', modalElement ? modalElement.classList.contains('show') : false); + + // Direct display approach - most reliable + console.log('Step 1: Cleaning up existing modals/backdrops...'); + + // Clean up any existing modals/backdrops first + var existingBackdrops = document.querySelectorAll('.modal-backdrop'); + console.log(' - Found', existingBackdrops.length, 'existing backdrops'); + existingBackdrops.forEach(function(b, i) { + console.log(' Removing backdrop', i + ':', b.id || 'no-id'); + b.remove(); + }); + + var existingModals = document.querySelectorAll('.modal.show'); + console.log(' - Found', existingModals.length, 'existing open modals'); + existingModals.forEach(function(m, i) { + console.log(' Closing modal', i + ':', m.id || 'no-id'); + m.classList.remove('show'); + }); + + if (typeof $ !== 'undefined') { + $('.modal-backdrop').remove(); + $('.modal.show').removeClass('show'); + console.log(' - jQuery cleanup completed'); + } + document.body.classList.remove('modal-open'); + console.log(' - Removed modal-open from body'); + + console.log('Step 2: Preparing modal element...'); + // Remove any inline styles that might hide it + console.log(' - Removing inline styles...'); + modalElement.removeAttribute('style'); + console.log(' - Inline styles removed'); + + console.log('Step 3: Moving modal to body and setting display properties...'); + + // CRITICAL: Move modal to body if it's not already there + if (modalElement.parentElement !== document.body) { + console.log('Moving modal to body (currently in:', modalElement.parentElement.tagName + ')'); + document.body.appendChild(modalElement); + console.log('Modal moved to body'); + } + + // Show modal directly with all necessary attributes + modalElement.classList.add('show'); + modalElement.classList.add('fade'); + modalElement.style.cssText = 'display: flex !important; position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; z-index: 99999 !important; opacity: 1 !important; visibility: visible !important; align-items: center !important; justify-content: center !important;'; + modalElement.removeAttribute('aria-hidden'); + modalElement.setAttribute('aria-hidden', 'false'); + modalElement.setAttribute('aria-modal', 'true'); + // Force remove aria-hidden again after a brief delay to ensure it sticks + setTimeout(function() { + modalElement.removeAttribute('aria-hidden'); + modalElement.setAttribute('aria-hidden', 'false'); + }, 50); + document.body.classList.add('modal-open'); + document.body.style.overflow = 'hidden'; + + console.log('Modal styles applied:'); + console.log(' - display:', modalElement.style.display); + console.log(' - position:', modalElement.style.position); + console.log(' - z-index:', modalElement.style.zIndex); + console.log(' - opacity:', modalElement.style.opacity); + console.log(' - visibility:', modalElement.style.visibility); + console.log(' - has show class:', modalElement.classList.contains('show')); + console.log(' - body has modal-open:', document.body.classList.contains('modal-open')); + + console.log('Step 4: Creating backdrop...'); + // Create and show backdrop + var backdrop = document.createElement('div'); + backdrop.className = 'modal-backdrop fade show'; + backdrop.style.position = 'fixed'; + backdrop.style.top = '0'; + backdrop.style.left = '0'; + backdrop.style.width = '100%'; + backdrop.style.height = '100%'; + backdrop.style.zIndex = '99998'; + backdrop.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; + backdrop.id = 'modifyBannedIPModalBackdrop'; + document.body.appendChild(backdrop); + console.log(' - Backdrop created and appended to body'); + console.log(' - Backdrop ID:', backdrop.id); + console.log(' - Backdrop in DOM:', document.body.contains(backdrop)); + + // Handle backdrop click to close + backdrop.addEventListener('click', function(e) { + console.log('Backdrop clicked'); + if (e.target === backdrop) { + $scope.closeModifyModal(); + } + }); + + console.log('Step 5: Configuring modal dialog...'); + // Ensure modal dialog is centered and visible + var modalDialog = modalElement.querySelector('.modal-dialog'); + if (modalDialog) { + console.log(' - Modal dialog found'); + modalDialog.style.zIndex = '100000'; + modalDialog.style.position = 'relative'; + modalDialog.style.margin = '1.75rem auto'; + console.log(' - Dialog styles applied'); + } else { + console.error(' - ❌ Modal dialog NOT found!'); + console.log(' - Modal children:', modalElement.children.length); + Array.from(modalElement.children).forEach(function(child, i) { + console.log(' Child', i + ':', child.tagName, child.className); + }); + } + + console.log('========================================'); + console.log('=== MODAL DISPLAY COMPLETE ==='); + console.log('========================================'); + console.log('Final checks:'); + console.log(' - Modal display (computed):', window.getComputedStyle(modalElement).display); + console.log(' - Modal visibility (computed):', window.getComputedStyle(modalElement).visibility); + console.log(' - Modal opacity (computed):', window.getComputedStyle(modalElement).opacity); + console.log(' - Modal z-index (computed):', window.getComputedStyle(modalElement).zIndex); + console.log(' - Modal in viewport:', modalElement.getBoundingClientRect()); + console.log(' - Backdrop display:', window.getComputedStyle(backdrop).display); + console.log(' - Body modal-open:', document.body.classList.contains('modal-open')); + + // Verify modal is actually visible + var rect = modalElement.getBoundingClientRect(); + console.log('Modal bounding rect:', { + top: rect.top, + left: rect.left, + width: rect.width, + height: rect.height, + visible: rect.width > 0 && rect.height > 0 + }); + + // CRITICAL: Force modal to be visible with multiple approaches + console.log('=== FORCING MODAL VISIBILITY ==='); + + // Remove any hiding classes + modalElement.classList.remove('hide', 'ng-hide', 'hidden'); + + // Force display with multiple methods + modalElement.style.setProperty('display', 'block', 'important'); + modalElement.style.setProperty('visibility', 'visible', 'important'); + modalElement.style.setProperty('opacity', '1', 'important'); + modalElement.style.setProperty('z-index', '99999', 'important'); + modalElement.style.setProperty('position', 'fixed', 'important'); + modalElement.removeAttribute('aria-hidden'); + modalElement.setAttribute('aria-hidden', 'false'); + + // Check parent containers for overflow issues + var parent = modalElement.parentElement; + var depth = 0; + while (parent && depth < 5) { + var parentOverflow = window.getComputedStyle(parent).overflow; + var parentZIndex = window.getComputedStyle(parent).zIndex; + console.log('Parent', depth + ':', parent.tagName, parent.className, 'overflow:', parentOverflow, 'z-index:', parentZIndex); + if (parentOverflow === 'hidden') { + console.warn('⚠️ Parent has overflow:hidden, might hide modal'); + parent.style.setProperty('overflow', 'visible', 'important'); + } + parent = parent.parentElement; + depth++; + } + + // Final verification + setTimeout(function() { + var finalDisplay = window.getComputedStyle(modalElement).display; + var finalVisibility = window.getComputedStyle(modalElement).visibility; + var finalOpacity = window.getComputedStyle(modalElement).opacity; + console.log('=== FINAL MODAL STATE (after 100ms) ==='); + console.log('Display:', finalDisplay); + console.log('Visibility:', finalVisibility); + console.log('Opacity:', finalOpacity); + if (finalDisplay === 'none' || finalVisibility === 'hidden' || finalOpacity === '0') { + console.error('❌ MODAL IS STILL HIDDEN!'); + console.error('Trying emergency show...'); + modalElement.style.cssText = 'display: block !important; visibility: visible !important; opacity: 1 !important; z-index: 99999 !important; position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important;'; + } else { + console.log('✅ Modal should be visible'); + } + }, 100); + + // Try jQuery/Bootstrap modal as primary method (most reliable) + console.log('Step 6: Attempting to show modal...'); + console.log(' - jQuery available:', typeof $ !== 'undefined'); + console.log(' - Bootstrap modal available:', typeof $ !== 'undefined' && $.fn.modal); + + if (typeof $ !== 'undefined' && $.fn.modal) { + console.log(' - Using jQuery/Bootstrap modal...'); + var $modal = $('#modifyBannedIPModal'); + console.log(' - jQuery modal object found:', !!$modal.length); + console.log(' - Modal element:', $modal[0]); + + if ($modal.length === 0) { + console.error(' - ❌ Modal element not found by jQuery!'); + console.log(' - Searching DOM for modal...'); + var modalCheck = document.getElementById('modifyBannedIPModal'); + console.log(' - Found by getElementById:', !!modalCheck); + if (modalCheck) { + console.log(' - Modal parent:', modalCheck.parentElement ? modalCheck.parentElement.tagName : 'null'); + console.log(' - Modal display:', window.getComputedStyle(modalCheck).display); + } + } else { + // Ensure modal is in body + var modalParent = $modal.parent()[0]; + console.log(' - Modal parent:', modalParent ? modalParent.tagName : 'null'); + if (modalParent !== document.body) { + console.log(' - Moving modal to body...'); + $modal.appendTo('body'); + console.log(' - ✅ Modal moved to body'); + } + + // CRITICAL: Show modal using Bootstrap - this is the most reliable method + console.log(' - Attempting to show modal with Bootstrap...'); + + // Initialize modal if not already initialized + if (!$modal.data('bs.modal')) { + console.log(' - Initializing Bootstrap modal first...'); + try { + $modal.modal({show: false, backdrop: true, keyboard: true}); + console.log(' - ✅ Modal initialized'); + } catch (initErr) { + console.warn(' - ⚠️ Modal initialization failed:', initErr); + } + } + + // Show using Bootstrap modal + try { + console.log(' - Calling Bootstrap modal.show()...'); + alert('About to call Bootstrap modal.show()'); // TEST ALERT + $modal.modal('show'); + console.log(' - ✅ Bootstrap modal.show() called successfully'); + alert('Bootstrap modal.show() called!'); // TEST ALERT + + // Force display immediately as backup (don't wait) + $modal.addClass('show'); + $modal[0].style.cssText = 'display: flex !important; position: fixed !important; z-index: 99999 !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; align-items: center !important; justify-content: center !important; opacity: 1 !important; visibility: visible !important;'; + + // Ensure backdrop exists + if (!$('.modal-backdrop').length) { + var backdrop = $('
'); + backdrop.css({ + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'width': '100%', + 'height': '100%', + 'z-index': '99998', + 'background-color': 'rgba(0, 0, 0, 0.5)' + }); + $('body').append(backdrop); + console.log(' - Created backdrop'); + } + + // Force body styles + $('body').addClass('modal-open').css('overflow', 'hidden'); + + console.log(' - ✅ Modal display forced'); + alert('Modal should be visible now! Check if you see it.'); // TEST ALERT + + // Verify modal is visible after a short delay + setTimeout(function() { + var modalEl = $modal[0]; + var isVisible = $modal.hasClass('show') && $modal.is(':visible'); + var display = window.getComputedStyle(modalEl).display; + var visibility = window.getComputedStyle(modalEl).visibility; + var opacity = window.getComputedStyle(modalEl).opacity; + var zIndex = window.getComputedStyle(modalEl).zIndex; + + console.log(' - === MODAL VISIBILITY CHECK (after 200ms) ==='); + console.log(' - Has show class:', $modal.hasClass('show')); + console.log(' - jQuery :visible:', $modal.is(':visible')); + console.log(' - Display:', display); + console.log(' - Visibility:', visibility); + console.log(' - Opacity:', opacity); + console.log(' - Z-index:', zIndex); + console.log(' - In DOM:', document.body.contains(modalEl)); + + if (!isVisible || display === 'none' || visibility === 'hidden' || opacity === '0') { + console.error(' - ❌ MODAL IS STILL NOT VISIBLE!'); + console.log(' - Applying emergency CSS override...'); + modalEl.style.cssText = 'display: flex !important; position: fixed !important; z-index: 99999 !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; align-items: center !important; justify-content: center !important; opacity: 1 !important; visibility: visible !important;'; + modalEl.classList.add('show'); + + // Also ensure backdrop exists + if (!$('.modal-backdrop').length) { + var backdrop = $(''); + backdrop.css({ + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'width': '100%', + 'height': '100%', + 'z-index': '99998', + 'background-color': 'rgba(0, 0, 0, 0.5)' + }); + $('body').append(backdrop); + console.log(' - Created backdrop manually'); + } + } else { + console.log(' - ✅ Modal appears to be visible'); + } + }, 200); + } catch (modalErr) { + console.error(' - ❌ Bootstrap modal.show() failed:', modalErr); + console.error(' - Error details:', modalErr.message, modalErr.stack); + // Fallback to manual display + console.log(' - Using fallback manual display...'); + $modal.addClass('show').css({ + 'display': 'flex', + 'position': 'fixed', + 'z-index': '99999', + 'top': '0', + 'left': '0', + 'width': '100%', + 'height': '100%', + 'align-items': 'center', + 'justify-content': 'center', + 'opacity': '1', + 'visibility': 'visible' + }); + } + } + } else { + console.warn(' - ❌ jQuery/Bootstrap modal not available!'); + console.warn(' - jQuery:', typeof $); + console.warn(' - $.fn.modal:', typeof $ !== 'undefined' ? typeof $.fn.modal : 'N/A'); + console.log(' - Using manual display fallback...'); + } + + console.log('========================================'); + console.log('=== END OF MODAL DISPLAY LOGIC ==='); + console.log('========================================'); + + // Try Bootstrap 5 as additional backup + if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { + console.log('Also trying Bootstrap 5 modal...'); + try { + var modal = new bootstrap.Modal(modalElement); + modal.show(); + } catch (bsErr) { + console.log('Bootstrap 5 modal failed:', bsErr); + } + } + console.log('Using manual modal display'); + modalElement.classList.add('show'); + modalElement.style.display = 'block'; + modalElement.removeAttribute('aria-hidden'); + modalElement.setAttribute('aria-hidden', 'false'); + modalElement.setAttribute('aria-modal', 'true'); + document.body.classList.add('modal-open'); + + // Create backdrop + var backdrop = document.createElement('div'); + backdrop.className = 'modal-backdrop fade show'; + backdrop.id = 'modifyBannedIPModalBackdrop'; + backdrop.style.position = 'fixed'; + backdrop.style.top = '0'; + backdrop.style.left = '0'; + backdrop.style.zIndex = '99998'; + backdrop.style.width = '100%'; + backdrop.style.height = '100%'; + backdrop.style.backgroundColor = 'rgba(0,0,0,0.5)'; + document.body.appendChild(backdrop); + + // Handle backdrop click to close + backdrop.addEventListener('click', function() { + $scope.closeModifyModal(); + }); + + // Ensure modal is on top with very high z-index + modalElement.style.zIndex = '99999'; + modalElement.style.position = 'fixed'; + modalElement.style.top = '0'; + modalElement.style.left = '0'; + modalElement.style.width = '100%'; + modalElement.style.height = '100%'; + modalElement.style.display = 'flex'; + modalElement.style.alignItems = 'center'; + modalElement.style.justifyContent = 'center'; + + // Ensure modal dialog is visible + var modalDialog = modalElement.querySelector('.modal-dialog'); + if (modalDialog) { + modalDialog.style.zIndex = '100000'; + modalDialog.style.position = 'relative'; + } + } + } catch (error) { + console.error('========================================'); + console.error('❌ ERROR in showModifyBannedIPModal'); + console.error('========================================'); + console.error('Error type:', error.name); + console.error('Error message:', error.message); + console.error('Error stack:', error.stack); + console.error('Error object:', error); + console.trace('Full stack trace:'); + + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Error', + text: 'Failed to open modify dialog: ' + error.message, + type: 'error' + }); + } else { + alert('Error: Failed to open modify dialog - ' + error.message + '\n\nCheck console for details.'); + } + } + }, 100); + + console.log('=== showModifyBannedIPModal function completed (timeout scheduled) ==='); + return true; + }; + + // Make showModifyBannedIPModal available on window immediately after definition + window.showModifyBannedIPModalScope = $scope.showModifyBannedIPModal; + window.handleModifyButtonClickScope = $scope.handleModifyButtonClick; + window.openModifyModalScope = $scope.openModifyModal; + console.log('✅ All functions available on window:', { + showModifyBannedIPModal: typeof window.showModifyBannedIPModalScope, + handleModifyButtonClick: typeof window.handleModifyButtonClickScope, + openModifyModal: typeof window.openModifyModalScope + }); + + $scope.closeModifyModal = function() { + console.log('=== closeModifyModal called ==='); + + var modalElement = document.getElementById('modifyBannedIPModal'); + if (!modalElement) { + console.warn('Modal element not found for closing'); + return; + } + + // Try jQuery/Bootstrap modal first + if (typeof $ !== 'undefined' && $.fn.modal) { + try { + $('#modifyBannedIPModal').modal('hide'); + console.log('Closed modal using Bootstrap'); + } catch (e) { + console.warn('Bootstrap modal hide failed:', e); + } + } + + // Manual cleanup + modalElement.classList.remove('show'); + modalElement.style.display = 'none'; + modalElement.removeAttribute('aria-hidden'); + modalElement.setAttribute('aria-hidden', 'true'); + + // Remove backdrop + var backdrops = document.querySelectorAll('.modal-backdrop'); + backdrops.forEach(function(b) { b.remove(); }); + + document.body.classList.remove('modal-open'); + document.body.style.overflow = ''; + }; + + // Make closeModifyModal available globally for onclick handlers + window.closeModifyModalGlobal = function() { + var controllerEl = document.querySelector('[ng-controller=firewallController]'); + if (controllerEl) { + var scope = angular.element(controllerEl).scope(); + if (scope && scope.closeModifyModal) { + scope.$apply(function() { + scope.closeModifyModal(); + }); + return; + } + } + // Fallback if AngularJS not available + var modal = document.getElementById('modifyBannedIPModal'); + if (modal) { + modal.classList.remove('show'); + modal.removeAttribute('aria-hidden'); + modal.setAttribute('aria-hidden', 'true'); + modal.style.display = 'none'; + document.body.classList.remove('modal-open'); + document.body.style.overflow = ''; + var backdrops = document.querySelectorAll('.modal-backdrop'); + for (var i = 0; i < backdrops.length; i++) backdrops[i].remove(); + } + }; + + // Make modifyBannedIP available globally for onclick handlers + window.modifyBannedIPGlobal = function() { + var controllerEl = document.querySelector('[ng-controller=firewallController]'); + if (controllerEl) { + var scope = angular.element(controllerEl).scope(); + if (scope && scope.modifyBannedIP) { + scope.$apply(function() { + scope.modifyBannedIP(); + }); + return; + } + } + alert('Error: Cannot save changes. Please refresh the page.'); + }; + + console.log('Modal closed'); + var modalElement = document.getElementById('modifyBannedIPModal'); + var backdrop = document.getElementById('modifyBannedIPModalBackdrop') || document.querySelector('.modal-backdrop'); + + // Hide modal + if (modalElement) { + modalElement.classList.remove('show', 'fade'); + modalElement.style.display = 'none'; + modalElement.removeAttribute('style'); + modalElement.setAttribute('aria-hidden', 'true'); + } + + // Remove backdrop + if (backdrop) { + backdrop.remove(); + } + + // Also remove any jQuery-created backdrops + if (typeof $ !== 'undefined') { + $('.modal-backdrop').remove(); + $('#modifyBannedIPModal').modal('hide'); + } + + document.body.classList.remove('modal-open'); + console.log('Modal closed'); + }; + + $scope.modifyBannedIP = function() { + var banId = document.getElementById('modifyBannedIPId').value; + var ipAddress = document.getElementById('modifyBannedIPAddress').value.trim(); + var reason = document.getElementById('modifyBannedIPReason').value.trim(); + var duration = document.getElementById('modifyBannedIPDuration').value; + + if (!ipAddress) { + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Validation Error', + text: 'Please enter an IP address', + type: 'error' + }); + } else { + alert('Please enter an IP address'); + } + return; + } + + // Validate IP address format + var ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/(?:[0-9]|[1-2][0-9]|3[0-2]))?$/; + if (!ipRegex.test(ipAddress)) { + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Validation Error', + text: 'Please enter a valid IP address (e.g., 192.168.1.1 or 192.168.1.0/24)', + type: 'error' + }); + } else { + alert('Please enter a valid IP address'); + } + return; + } + + if (!reason) { + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Validation Error', + text: 'Please enter a reason for the ban', + type: 'error' + }); + } else { + alert('Please enter a reason for the ban'); + } + return; + } + + $scope.bannedIPsLoading = true; + $scope.bannedIPActionFailed = true; + $scope.bannedIPActionSuccess = true; + $scope.bannedIPCouldNotConnect = true; + + var data = { + id: banId, + ip: ipAddress, + reason: reason, + duration: duration + }; + + var url = "/firewall/modifyBannedIP"; + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(function(response) { + $scope.bannedIPsLoading = false; + if (response.data.status === 1) { + $scope.bannedIPActionSuccess = false; + $('#modifyBannedIPModal').modal('hide'); + populateBannedIPs(); // Refresh the list + + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Success!', + text: 'Banned IP modified successfully', + type: 'success' + }); + } + } else { + $scope.bannedIPActionFailed = false; + $scope.bannedIPErrorMessage = response.data.error_message || 'Failed to modify banned IP'; + + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Error!', + text: response.data.error_message || 'Failed to modify banned IP', + type: 'error' + }); + } + } + }, function(error) { + $scope.bannedIPsLoading = false; + $scope.bannedIPCouldNotConnect = false; + + if (typeof PNotify !== 'undefined') { + new PNotify({ + title: 'Connection Error', + text: 'Could not connect to server. Please refresh this page.', + type: 'error' + }); + } + }); + }; + $scope.deleteBannedIP = function(id, ip) { if (!confirm('Are you sure you want to permanently delete the record for IP address ' + ip + '? This action cannot be undone.')) { return; diff --git a/firewall/templates/firewall/firewall.html b/firewall/templates/firewall/firewall.html index 711043857..a04e17f21 100644 --- a/firewall/templates/firewall/firewall.html +++ b/firewall/templates/firewall/firewall.html @@ -8,6 +8,7 @@ max-width: 1400px; margin: 0 auto; padding: 2rem; + overflow: visible; /* Allow modal to show above container */ } .page-header { @@ -611,7 +612,61 @@ border-radius: 16px; box-shadow: 0 4px 12px var(--shadow-medium, rgba(0,0,0,0.15)); border: 1px solid var(--border-color, #e8e9ff); - overflow: hidden; + overflow: visible; /* Changed from hidden to allow modal to show */ + position: relative; + z-index: 1; + } + + /* Modal Styles - Ensure it's above everything */ + #modifyBannedIPModal { + z-index: 99999 !important; + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + } + + #modifyBannedIPModal.show { + display: flex !important; + opacity: 1 !important; + visibility: visible !important; + align-items: center !important; + justify-content: center !important; + } + + #modifyBannedIPModal.fade.show { + opacity: 1 !important; + } + + #modifyBannedIPModal .modal-dialog { + z-index: 100000 !important; + position: relative !important; + margin: 1.75rem auto !important; + max-width: 500px !important; + width: 90% !important; + } + + .modal-backdrop { + z-index: 99998 !important; + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + background-color: rgba(0, 0, 0, 0.5) !important; + } + + /* Ensure modal is always on top */ + body.modal-open #modifyBannedIPModal { + display: flex !important; + } + + /* Make sure modal is not hidden by parent containers */ + .modern-container #modifyBannedIPModal, + .banned-ips-panel #modifyBannedIPModal { + position: fixed !important; + z-index: 99999 !important; } .add-banned-section { @@ -770,18 +825,68 @@ box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3); } + .btn-modify { + background: #2196f3 !important; + color: var(--bg-secondary, white) !important; + border: none !important; + padding: 0.75rem 1.5rem !important; + border-radius: 6px !important; + font-weight: 600 !important; + cursor: pointer !important; + transition: all 0.3s ease; + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; + font-size: 0.95rem !important; + visibility: visible !important; + opacity: 1 !important; + min-width: 100px !important; + min-height: 40px !important; + position: relative !important; + z-index: 10 !important; + } + + .btn-modify:hover { + background: #1976d2; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(33, 150, 243, 0.3); + } + + /* Ensure actions column and buttons are always visible */ + .banned-table td.actions { + display: flex !important; + visibility: visible !important; + } + + .banned-table td.actions { + display: flex !important; + gap: 0.5rem !important; + align-items: center !important; + } + + .banned-table td.actions .btn-modify { + display: flex !important; + visibility: visible !important; + opacity: 1 !important; + width: auto !important; + height: auto !important; + } + .btn-delete { background: var(--danger-color, #ef4444); color: var(--bg-secondary, white); border: none; - padding: 0.5rem; + padding: 0.75rem 1.5rem !important; border-radius: 6px; + font-weight: 600 !important; cursor: pointer; transition: all 0.3s ease; - width: 32px; - height: 32px; - display: flex; + display: flex !important; align-items: center; + gap: 0.5rem !important; + font-size: 0.95rem !important; + min-width: 100px !important; + min-height: 40px !important; justify-content: center; } @@ -1115,22 +1220,23 @@ -