ai_upgrade_assistant-0.2.0-alpha2/js/status_page.js
js/status_page.js
(function ($, Drupal, once) {
'use strict';
Drupal.behaviors.aiUpgradeStatus = {
attach: function (context, settings) {
console.log('Initializing AI Upgrade Status...');
once('aiUpgradeStatus', '.ai-upgrade-status', context).forEach(function (element) {
console.log('Found status element, setting up polling...');
const terminalWindow = document.getElementById('terminal-window');
const countdownElement = element.querySelector('.countdown');
if (!terminalWindow) {
console.error('Terminal window element not found!');
return;
}
let isPolling = false;
let lastTimestamp = 0;
let countdownInterval;
let nextUpdateTime = 0;
function updateCountdown() {
if (nextUpdateTime > 0) {
const now = Math.floor(Date.now() / 1000);
const remaining = Math.max(0, nextUpdateTime - now);
if (countdownElement) {
countdownElement.textContent = remaining;
}
}
}
function scheduleNextUpdate(delaySeconds) {
if (countdownInterval) {
clearInterval(countdownInterval);
}
nextUpdateTime = Math.floor(Date.now() / 1000) + delaySeconds;
// Start countdown immediately
updateCountdown();
// Update countdown every second
countdownInterval = setInterval(updateCountdown, 1000);
// Schedule the actual update
return setTimeout(updateStatus, delaySeconds * 1000);
}
function updateStatus() {
if (isPolling) {
console.log('Already polling, skipping update');
return;
}
console.log('Starting status update...');
isPolling = true;
let newMessages = [];
$.ajax({
url: Drupal.url('admin/reports/upgrade-assistant/check-status'),
success: function (response) {
console.log('Received status update:', response);
// Update progress bar
const progressBar = element.querySelector('.progress-bar');
const progressText = element.querySelector('.progress-text');
if (progressBar && progressText) {
progressBar.style.width = response.progress + '%';
progressText.textContent = response.progress + '% Complete';
console.log('Updated progress:', response.progress);
}
// Update terminal output
if (response.terminal_output && response.terminal_output.length > 0) {
console.log('Got new terminal output:', response.terminal_output.length, 'messages');
// Filter out messages we've already seen
newMessages = response.terminal_output.filter(line => line.timestamp > lastTimestamp);
console.log('New messages:', newMessages.length);
if (newMessages.length > 0) {
// Update last timestamp
lastTimestamp = Math.max(...newMessages.map(line => line.timestamp));
// Add new messages to terminal
const terminalContent = newMessages.map(function (line) {
const timestamp = new Date(line.timestamp * 1000).toTimeString().split(' ')[0];
return `<div class="terminal-line type-${line.type}">` +
`<span class="timestamp">[${timestamp}]</span> ${line.message}</div>`;
}).join('');
// Clear placeholder if it exists
const placeholder = terminalWindow.querySelector('.terminal-placeholder');
if (placeholder) {
placeholder.remove();
}
// Append new content
terminalWindow.insertAdjacentHTML('beforeend', terminalContent);
terminalWindow.scrollTop = terminalWindow.scrollHeight;
console.log('Added new terminal content');
}
}
// Update module categories if they've changed
if (response.modules) {
Object.keys(response.modules).forEach(function (category) {
const details = element.querySelector(`.module-group.${category}`);
if (details) {
const count = response.modules[category].length;
const summary = details.querySelector('summary');
if (summary) {
const label = summary.textContent.split('(')[0].trim();
summary.textContent = `${label} (${count})`;
}
}
});
}
isPolling = false;
// Schedule next update based on activity
if (response.is_active || newMessages.length > 0) {
console.log('Activity detected, updating in 1 second');
scheduleNextUpdate(1);
} else {
console.log('No activity, updating in 5 seconds');
scheduleNextUpdate(5);
}
},
error: function (xhr, status, error) {
console.error('Error updating status:', error);
isPolling = false;
// Retry after error with longer delay
scheduleNextUpdate(5);
}
});
}
// Start polling immediately
updateStatus();
// Add click handlers for action buttons
once('actionButtons', '[data-action]', element).forEach(function (button) {
button.addEventListener('click', function (e) {
e.preventDefault();
const action = this.dataset.action;
const module = this.dataset.module;
let url;
switch (action) {
case 'composer-update':
url = Drupal.url(`admin/reports/upgrade-assistant/module/${module}/composer-update`);
break;
case 'apply-patch':
url = Drupal.url(`admin/reports/upgrade-assistant/module/${module}/apply-patch`);
break;
case 'auto-fix':
url = `/admin/reports/upgrade-assistant/auto-fix/${module}`;
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
Drupal.behaviors.messages.showMessage(data.message, 'status');
// Refresh status after a short delay
setTimeout(() => {
updateStatus();
}, 2000);
} else {
Drupal.behaviors.messages.showMessage(data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
Drupal.behaviors.messages.showMessage(
Drupal.t('An error occurred while processing the request.'),
'error'
);
});
return;
case 'analyze':
url = Drupal.url(`admin/reports/upgrade-assistant/analyze/${module}`);
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
window.location.href = Drupal.url(`admin/reports/upgrade-assistant/analysis/${module}`);
} else {
Drupal.behaviors.messages.showMessage(data.message || 'Analysis failed', 'error');
}
})
.catch(error => {
console.error('Error:', error);
Drupal.behaviors.messages.showMessage(
Drupal.t('An error occurred during analysis.'),
'error'
);
});
return;
default:
console.error('Unknown action:', action);
return;
}
$.ajax({
url: url,
method: 'POST',
success: function (response) {
if (response.message) {
// Show status message
Drupal.behaviors.messages.showMessage(response.message);
}
// Trigger a status update immediately
updateStatus();
},
error: function (xhr, status, error) {
console.error(`Error executing ${action} for ${module}:`, error);
Drupal.behaviors.messages.showMessage(error, 'error');
}
});
});
});
function updateRateLimitStatus() {
fetch('/admin/config/development/ai-upgrade-assistant/rate-limit-status')
.then(response => response.json())
.then(data => {
const statusHtml = `
<div class="rate-limit-status">
<h3>API Rate Limit Status</h3>
<div class="rate-limit-info">
<p>Status: ${data.enabled ? 'Enabled' : 'Disabled'}</p>
<p>Requests: ${data.current_requests}/${data.max_requests}</p>
<p>Window Remaining: ${Math.floor(data.window_remaining)}s</p>
<div class="progress-bar">
<div class="progress" style="width: ${(data.current_requests / data.max_requests) * 100}%"></div>
</div>
</div>
</div>
`;
const container = document.querySelector('.rate-limit-container');
if (container) {
container.innerHTML = statusHtml;
}
})
.catch(error => console.error('Error updating rate limit status:', error));
}
// Update rate limit status every 5 seconds
setInterval(updateRateLimitStatus, 5000);
});
}
};
// Helper for showing status messages
Drupal.behaviors.messages = {
showMessage: function (message, type = 'status') {
const messagesRegion = document.querySelector('[data-drupal-messages]');
if (messagesRegion) {
const messageWrapper = document.createElement('div');
messageWrapper.setAttribute('role', 'contentinfo');
messageWrapper.setAttribute('aria-label', Drupal.t('Status message'));
messageWrapper.className = `messages messages--${type}`;
messageWrapper.innerHTML = message;
messagesRegion.appendChild(messageWrapper);
// Remove the message after 5 seconds
setTimeout(() => {
messageWrapper.remove();
}, 5000);
}
}
};
})(jQuery, Drupal, once);
