// Global variables let integrityCheckInterval = null; let directivesLoading = false; // Integrity Check Functions function startIntegrityCheck() { console.log("Start button clicked"); // Debug log const startBtn = document.getElementById('startCheckBtn'); const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); startBtn.disabled = true; progressText.textContent = 'Starting check...'; progressBar.style.width = '0%'; // Show a notification for debugging showNotification('Attempting to start integrity check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=start_integrity_check' }) .then(response => { console.log("Response status:", response.status); // Debug log if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Parsed data:", data); // Debug log if (data.success) { updateIntegrityCheckUI(true); progressBar.classList.add('processing'); startProgressMonitoring(); showNotification('Integrity check started successfully', 'success'); } else { throw new Error(data.message || 'Failed to start check'); } }) .catch(error => { console.error('Error starting check:', error); progressText.textContent = 'Error: ' + error.message; startBtn.disabled = false; progressBar.classList.remove('processing'); showNotification('Error: ' + error.message, 'error'); }); } function startProgressMonitoring() { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); let failedAttempts = 0; const maxFailedAttempts = 10; const retryDelay = 3000; // 3 seconds between retries - increased to reduce server load let isPhase1 = true; // Track which phase we're in if (integrityCheckInterval) { clearInterval(integrityCheckInterval); } // Add processing animation class progressBar.classList.add('processing'); const checkProgress = async () => { try { const response = await fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' }, body: 'action=get_check_status' }); if (response.status === 503) { failedAttempts += 0.5; // Count 503s as half failures console.log('Server busy, attempt: ' + failedAttempts); return; // Continue monitoring without throwing error } if (!response.ok) { throw new Error(`Server returned ${response.status}: ${response.statusText}`); } // Get raw text and try to parse const text = await response.text(); let data; try { data = JSON.parse(text); } catch (e) { console.error("Failed to parse JSON response:", text); failedAttempts++; return; // Skip this update } if (data.success && data.status) { // Reset failed attempts on success failedAttempts = 0; const status = data.status; // Check for phase transition if (data.phase_change || (status.status && (status.status.includes('Phase 2') || status.status.includes('Starting phase 2')))) { if (isPhase1) { isPhase1 = false; // Phase transition notification showNotification('Phase 1 complete. Starting Phase 2: Grouping anomalies into directives.', 'info'); // Reset progress bar for phase 2 progressBar.style.width = '0%'; progressText.textContent = 'Starting Phase 2: Grouping anomalies...'; } } const progress = (parseInt(status.processed_count) / parseInt(status.total_count) * 100) || 0; // Update progress bar and text progressBar.style.width = `${progress}%`; progressText.textContent = status.status || `Processing: ${progress.toFixed(1)}%`; // Check if the process is complete or stopped if (!status.is_running || progress >= 100) { clearInterval(integrityCheckInterval); progressBar.classList.remove('processing'); if (progress >= 100) { progressText.textContent = 'Check completed'; showNotification('Integrity check completed successfully.', 'success'); // Process Phase 2 before switching tabs setTimeout(() => { checkPhase2Status(); }, 1500); } } // Update UI based on status if (status.is_paused == '1') { progressBar.classList.add('paused'); progressText.classList.add('paused'); updateIntegrityCheckUI(true, true); } else if (status.status && status.status.includes('cooling')) { progressBar.classList.add('cooling'); progressText.classList.add('cooling'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); } else { progressBar.classList.remove('cooling'); progressText.classList.remove('cooling'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); } } } catch (error) { console.error('Progress monitoring error:', error); failedAttempts++; if (failedAttempts >= maxFailedAttempts) { // Reset counter and show warning but keep monitoring failedAttempts = maxFailedAttempts / 2; showNotification('Server is busy. Progress will continue in background.', 'warning'); } } }; // Initial check checkProgress(); // Set up interval - check every 3 seconds integrityCheckInterval = setInterval(checkProgress, retryDelay); } function checkPhase2Status() { console.log("Checking Phase 2 status"); // Add a timeout counter if (!window.phase2TimeoutCounter) { window.phase2TimeoutCounter = 0; } window.phase2TimeoutCounter++; // If we've been checking for too long (20 attempts), force completion if (window.phase2TimeoutCounter > 20) { console.log("Phase 2 taking too long, forcing completion"); window.phase2TimeoutCounter = 0; switchTab('directives'); return; } fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=process_anomaly_batch' }) .then(response => { if (!response.ok) { console.log("Phase 2 status check response not OK:", response.status); throw new Error(`Server returned ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Phase 2 processing response:", data); if (data.success) { if (data.status === 'completed') { // All anomalies processed, load directives console.log("Phase 2 complete, switching to directives tab"); window.phase2TimeoutCounter = 0; // Reset counter switchTab('directives'); } else if (data.status === 'processing') { // Update progress bar const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); if (progressBar && data.progress !== undefined) { progressBar.style.width = `${data.progress}%`; progressText.textContent = `Processing: ${data.processed} of ${data.total} anomalies (${data.progress}%)`; } // Continue processing setTimeout(checkPhase2Status, 2000); } else { // Unknown status but successful, assume complete console.log("Phase 2 unknown status, assuming complete"); window.phase2TimeoutCounter = 0; // Reset counter switchTab('directives'); } } else { showNotification('Error processing anomalies: ' + (data.message || 'Unknown error'), 'error'); // Still continue after a delay setTimeout(checkPhase2Status, 5000); } }) .catch(error => { console.error("Error checking Phase 2 status:", error); // Retry after a delay setTimeout(checkPhase2Status, 5000); }); } function stopIntegrityCheck() { console.log("Stop button clicked"); if (!confirm('Are you sure you want to stop the integrity check? This will terminate the process.')) { return; } showNotification('Attempting to stop check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'action=stop' }) .then(response => { console.log("Stop response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Stop response data:", data); if (data.success) { if (integrityCheckInterval) { clearInterval(integrityCheckInterval); } const progressBar = document.getElementById('integrityProgressBar'); progressBar.classList.remove('processing'); progressBar.classList.remove('cooling'); updateIntegrityCheckUI(false); showNotification('Integrity check stopped.', 'info'); } else { throw new Error(data.message || 'Failed to stop integrity check'); } }) .catch(error => { console.error('Error stopping check:', error); showNotification('Failed to stop check: ' + error.message, 'error'); }); } // Pause and Resume Functions function pauseIntegrityCheck() { console.log("Pause button clicked"); const pauseBtn = document.getElementById('pauseCheckBtn'); // If already paused, this is a resume action if (pauseBtn.textContent.trim() === 'Resume') { resumeIntegrityCheck(); return; } showNotification('Attempting to pause check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=pause' }) .then(response => { console.log("Pause response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Pause response data:", data); if (data.success) { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); progressBar.classList.add('paused'); progressText.classList.add('paused'); progressText.textContent = 'Check paused'; pauseBtn.textContent = 'Resume'; showNotification('Integrity check paused.', 'info'); } else { throw new Error(data.message || 'Failed to pause integrity check'); } }) .catch(error => { console.error('Error pausing check:', error); showNotification('Failed to pause check: ' + error.message, 'error'); }); } function resumeIntegrityCheck() { console.log("Resume button clicked"); const pauseBtn = document.getElementById('pauseCheckBtn'); showNotification('Attempting to resume check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=resume' }) .then(response => { console.log("Resume response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Resume response data:", data); if (data.success) { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); progressText.textContent = 'Check resumed'; pauseBtn.textContent = 'Pause'; showNotification('Integrity check resumed.', 'info'); } else { throw new Error(data.message || 'Failed to resume integrity check'); } }) .catch(error => { console.error('Error resuming check:', error); showNotification('Failed to resume check: ' + error.message, 'error'); }); } function updateIntegrityCheckUI(running, paused = false) { const startBtn = document.getElementById('startCheckBtn'); const pauseBtn = document.getElementById('pauseCheckBtn'); const stopBtn = document.getElementById('stopCheckBtn'); const progressText = document.getElementById('integrityProgressText'); const progressBar = document.getElementById('integrityProgressBar'); if (running) { startBtn.style.display = 'none'; pauseBtn.style.display = 'inline-block'; stopBtn.style.display = 'inline-block'; if (paused) { pauseBtn.textContent = 'Resume'; progressText.textContent = 'Check paused'; progressBar.classList.add('paused'); progressText.classList.add('paused'); } else { pauseBtn.textContent = 'Pause'; progressBar.classList.remove('paused'); progressText.classList.remove('paused'); if (!progressText.textContent.includes('%')) { progressText.textContent = 'Processing...'; } } } else { startBtn.style.display = 'inline-block'; pauseBtn.style.display = 'none'; stopBtn.style.display = 'none'; progressText.textContent = 'Ready to start integrity check'; } } // Directives Functions function loadDirectives() { // Prevent multiple simultaneous requests if (directivesLoading) { console.log("Already loading directives, request ignored"); return; } directivesLoading = true; console.log("Loading directives"); const tableBody = document.getElementById('directivesTableBody'); if (!tableBody) { console.error('Directives table body not found'); directivesLoading = false; return; } tableBody.innerHTML = 'Loading directives...'; fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=get_directives' }) .then(response => { console.log("Get directives response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw directives response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { directivesLoading = false; console.log("Directives data:", data); if (!data.success) { throw new Error(data.message || 'Failed to load directives'); } if (!data.directives || data.directives.length === 0) { tableBody.innerHTML = 'No directives found'; return; } tableBody.innerHTML = data.directives.map(directive => ` ${directive.id} ${directive.description} ${directive.record_count || 0} ${directive.status.charAt(0).toUpperCase() + directive.status.slice(1)} ${directive.last_resolved ? `
Last resolved: ${new Date(directive.last_resolved).toLocaleString()} ` : ''}
${directive.status !== 'approved' && directive.status !== 'resolved' ? ` ` : ''} ${directive.status !== 'rejected' && directive.status !== 'resolved' ? ` ` : ''}
`).join(''); }) .catch(error => { directivesLoading = false; console.error('Error loading directives:', error); tableBody.innerHTML = `Failed to load directives: ${error.message}`; showNotification('Failed to load directives: ' + error.message, 'error'); }); } function handleResolveAction(directiveId) { console.log("Handling resolve action for directive:", directiveId); showNotification('Analyzing affected records...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=check_affected_members&directive_id=${directiveId}` }) .then(response => { console.log("Check affected members response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Affected members data:", data); if (!data.success) { throw new Error(data.message || 'Failed to check affected members'); } showResolveConfirmation(directiveId, data); }) .catch(error => { console.error('Error in handleResolveAction:', error); showNotification(error.message, 'error'); }); } function showResolveConfirmation(directiveId, data) { // Create modal overlay if it doesn't exist let overlay = document.querySelector('.modal-overlay'); if (!overlay) { overlay = document.createElement('div'); overlay.className = 'modal-overlay'; document.body.appendChild(overlay); } overlay.innerHTML = `

Resolve Directive

${data.description}

This action will delete ${data.affected_count} panel members with this anomaly.

`; overlay.style.display = 'flex'; } function closeResolveModal() { const overlay = document.querySelector('.modal-overlay'); if (overlay) { overlay.style.display = 'none'; } } function applyResolve(directiveId) { console.log("Applying resolve for directive:", directiveId); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=apply_resolve&directive_id=${directiveId}` }) .then(response => { console.log("Apply resolve response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Apply resolve data:", data); if (data.success) { closeResolveModal(); showNotification(`Anomaly resolved successfully. ${data.affected_count} records deleted.`, 'success'); loadDirectives(); } else { throw new Error(data.message || 'Failed to resolve directive'); } }) .catch(error => { console.error('Error in applyResolve:', error); showNotification('Failed to resolve directive: ' + error.message, 'error'); }); } function submitDirective() { const input = document.getElementById('directiveInput'); const description = input.value.trim(); if (!description) { showNotification('Please enter a directive description', 'error'); return; } console.log("Submitting new directive:", description); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=add_directive&description=${encodeURIComponent(description)}` }) .then(response => { console.log("Add directive response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Add directive data:", data); if (data.success) { input.value = ''; showNotification('Directive added successfully', 'success'); loadDirectives(); } else { throw new Error(data.message || 'Failed to add directive'); } }) .catch(error => { console.error('Error in submitDirective:', error); showNotification('Failed to add directive: ' + error.message, 'error'); }); } function handleDirectiveAction(id, action) { console.log(`Handling ${action} action for directive:`, id); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `action=${action}_directive&directive_id=${id}` }) .then(response => { console.log(`${action} directive response status:`, response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log(`${action} directive data:`, data); if (data.success) { showNotification(`Directive ${action}ed successfully`, 'success'); loadDirectives(); // Reload the directives list } else { throw new Error(data.message || `Failed to ${action} directive`); } }) .catch(error => { console.error(`Error in ${action} directive:`, error); showNotification(error.message, 'error'); }); } // Helper Functions function switchTab(tab) { console.log("Switching to tab:", tab); // Find the tab elements const tabs = document.querySelectorAll('.optimaize-tab'); const contents = document.querySelectorAll('.optimaize-content'); // Deactivate all tabs tabs.forEach(t => t.classList.remove('active')); contents.forEach(c => c.style.display = 'none'); // Activate the requested tab let tabElement = null; for (const t of tabs) { if (t.getAttribute('data-tab') === tab || t.textContent.toLowerCase().includes(tab.toLowerCase())) { tabElement = t; break; } } if (tabElement) { tabElement.classList.add('active'); } // Show the corresponding content let contentElement = null; if (tab === 'integrity') { contentElement = document.getElementById('integrityCheckContent'); } else if (tab === 'directives') { contentElement = document.getElementById('directivesContent'); } if (contentElement) { contentElement.style.display = 'block'; // Load directives when switching to that tab if (tab === 'directives') { loadDirectives(); } } } // Utility function to show notifications function showNotification(message, type = 'success') { console.log(`Notification (${type}):`, message); const container = document.getElementById('notificationContainer'); if (!container) { // Create container if it doesn't exist const newContainer = document.createElement('div'); newContainer.id = 'notificationContainer'; newContainer.style.position = 'fixed'; newContainer.style.top = '20px'; newContainer.style.right = '20px'; newContainer.style.zIndex = '9999'; document.body.appendChild(newContainer); // Use the newly created container showNotification(message, type); return; } const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.style.padding = '10px 15px'; notification.style.margin = '0 0 10px 0'; notification.style.borderRadius = '4px'; notification.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; notification.style.opacity = '0'; notification.style.transition = 'opacity 0.3s'; // Set background color based on type if (type === 'success') { notification.style.backgroundColor = '#4CAF50'; notification.style.color = 'white'; } else if (type === 'error') { notification.style.backgroundColor = '#F44336'; notification.style.color = 'white'; } else if (type === 'warning') { notification.style.backgroundColor = '#FF9800'; notification.style.color = 'white'; } else if (type === 'info') { notification.style.backgroundColor = '#2196F3'; notification.style.color = 'white'; } const icon = document.createElement('span'); icon.className = 'notification-icon'; icon.style.marginRight = '10px'; icon.textContent = type === 'success' ? '✓' : type === 'warning' ? '⚠' : type === 'info' ? 'ℹ' : '⨉'; const text = document.createElement('span'); text.className = 'notification-message'; text.textContent = message; notification.appendChild(icon); notification.appendChild(text); container.appendChild(notification); // Show notification with a small delay setTimeout(() => { notification.style.opacity = '1'; }, 10); // Hide and remove after 5 seconds setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, 5000); } // Initialize event handlers document.addEventListener('DOMContentLoaded', function() { console.log("DOM loaded, initializing integrity check handlers"); // Setup integrity check button handlers const startCheckBtn = document.getElementById('startCheckBtn'); if (startCheckBtn) { startCheckBtn.addEventListener('click', startIntegrityCheck); console.log("Start button handler attached"); } else { console.error("Start button not found"); } const pauseCheckBtn = document.getElementById('pauseCheckBtn'); if (pauseCheckBtn) { pauseCheckBtn.addEventListener('click', pauseIntegrityCheck); console.log("Pause button handler attached"); } else { console.error("Pause button not found"); } const stopCheckBtn = document.getElementById('stopCheckBtn'); if (stopCheckBtn) { stopCheckBtn.addEventListener('click', stopIntegrityCheck); console.log("Stop button handler attached"); } else { console.error("Stop button not found"); } // Setup tabs const integrityTab = document.querySelector('.optimaize-tab[data-tab="integrity"]'); if (integrityTab) { integrityTab.addEventListener('click', (e) => { console.log("Integrity tab clicked"); e.preventDefault(); switchTab('integrity'); }); console.log("Integrity tab handler attached"); } else { console.warn("Integrity tab not found"); } const directivesTab = document.querySelector('.optimaize-tab[data-tab="directives"]'); if (directivesTab) { directivesTab.addEventListener('click', (e) => { console.log("Directives tab clicked"); e.preventDefault(); switchTab('directives'); }); console.log("Directives tab handler attached"); } else { console.warn("Directives tab not found"); } // Load integrity tab by default if (document.querySelector('.optimaize-section') && document.querySelector('.optimaize-section').style.display === 'block') { console.log("Initializing default tab: integrity"); switchTab('integrity'); } console.log("Initialization complete"); });