further updates
This commit is contained in:
+2
-50
@@ -106,57 +106,9 @@ export function removeProgressBar(id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Show operation queue status
|
||||
* @param {string} operation - Operation name
|
||||
* @param {string} status - Status (pending, processing, completed, failed)
|
||||
* @deprecated showOperationStatus has been replaced with the notification system
|
||||
* Use notificationManager.add() from './notifications.js' instead
|
||||
*/
|
||||
export function showOperationStatus(operation, status) {
|
||||
const statusContainer = document.getElementById('operation-status') || createOperationStatusContainer();
|
||||
|
||||
const statusItem = document.createElement('div');
|
||||
statusItem.className = `operation-status-item status-${status}`;
|
||||
statusItem.innerHTML = `
|
||||
<span class="operation-name">${operation}</span>
|
||||
<span class="operation-status-badge badge bg-${getStatusColor(status)}">${status}</span>
|
||||
`;
|
||||
|
||||
statusContainer.appendChild(statusItem);
|
||||
|
||||
// Auto-remove completed items after 3 seconds
|
||||
if (status === 'completed' || status === 'failed') {
|
||||
setTimeout(() => {
|
||||
statusItem.remove();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create operation status container if it doesn't exist
|
||||
* @returns {HTMLElement} - Status container
|
||||
*/
|
||||
function createOperationStatusContainer() {
|
||||
const container = document.createElement('div');
|
||||
container.id = 'operation-status';
|
||||
container.className = 'operation-status-container position-fixed top-0 end-0 m-3';
|
||||
container.style.zIndex = '1060';
|
||||
document.body.appendChild(container);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status color for badge
|
||||
* @param {string} status - Status
|
||||
* @returns {string} - Bootstrap color class
|
||||
*/
|
||||
function getStatusColor(status) {
|
||||
const colors = {
|
||||
pending: 'secondary',
|
||||
processing: 'primary',
|
||||
completed: 'success',
|
||||
failed: 'danger'
|
||||
};
|
||||
return colors[status] || 'secondary';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+22
-1
@@ -10,13 +10,21 @@ class NotificationManager {
|
||||
constructor() {
|
||||
this.notifications = [];
|
||||
this.listeners = [];
|
||||
this.loadFromStorage();
|
||||
this._storageLoaded = false;
|
||||
// Defer loading from storage to not block initialization
|
||||
if (typeof requestIdleCallback !== 'undefined') {
|
||||
requestIdleCallback(() => this.loadFromStorage(), { timeout: 1000 });
|
||||
} else {
|
||||
// Fallback for browsers without requestIdleCallback
|
||||
setTimeout(() => this.loadFromStorage(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load notifications from localStorage
|
||||
*/
|
||||
loadFromStorage() {
|
||||
if (this._storageLoaded) return;
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
@@ -29,9 +37,13 @@ class NotificationManager {
|
||||
// Limit to most recent notifications
|
||||
this.notifications = this.notifications.slice(-MAX_NOTIFICATIONS);
|
||||
}
|
||||
this._storageLoaded = true;
|
||||
// Notify listeners after loading
|
||||
this.notify();
|
||||
} catch (err) {
|
||||
console.error('[ERROR] Failed to load notifications from storage:', err);
|
||||
this.notifications = [];
|
||||
this._storageLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +177,10 @@ class NotificationManager {
|
||||
* @returns {number} Number of unread notifications
|
||||
*/
|
||||
getUnreadCount() {
|
||||
// Ensure storage is loaded
|
||||
if (!this._storageLoaded) {
|
||||
this.loadFromStorage();
|
||||
}
|
||||
return this.notifications.filter(n => !n.read).length;
|
||||
}
|
||||
|
||||
@@ -174,6 +190,11 @@ class NotificationManager {
|
||||
* @returns {Array} Filtered notifications
|
||||
*/
|
||||
getNotifications(filters = {}) {
|
||||
// Ensure storage is loaded
|
||||
if (!this._storageLoaded) {
|
||||
this.loadFromStorage();
|
||||
}
|
||||
|
||||
let filtered = [...this.notifications];
|
||||
|
||||
if (filters.type && filters.type !== 'all') {
|
||||
|
||||
+33
-40
@@ -1,6 +1,6 @@
|
||||
// Import dependencies first (ES6 imports must be at top)
|
||||
import { showOperationStatus } from './loadingStates.js';
|
||||
import { closeAllModals, showStatusIndicator, hideStatusIndicator, showAlert } from './uiUtils.js';
|
||||
import notificationManager from './notifications.js';
|
||||
|
||||
// DOM Elements
|
||||
const templateList = document.getElementById('template-list');
|
||||
@@ -750,19 +750,20 @@ deployForm.addEventListener('submit', async (e) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safely get container name with fallback
|
||||
const containerName = (formData && formData.containerName) ? String(formData.containerName) : 'container';
|
||||
|
||||
// Close modal immediately before async operation
|
||||
if (typeof closeAllModals === 'function') {
|
||||
closeAllModals();
|
||||
}
|
||||
closeDeployModal();
|
||||
|
||||
// Add notification for container creation
|
||||
notificationManager.add('info', `Creating container "${containerName}"...`, { autoDismiss: false });
|
||||
showStatusIndicator('Deploying container...');
|
||||
|
||||
try {
|
||||
// Safely get container name with fallback
|
||||
const containerName = (formData && formData.containerName) ? String(formData.containerName) : 'container';
|
||||
|
||||
showStatusIndicator('Deploying container...');
|
||||
try {
|
||||
if (typeof showOperationStatus === 'function') {
|
||||
showOperationStatus(`Deploying ${containerName}`, 'processing');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[WARN] Failed to show operation status:', e);
|
||||
}
|
||||
|
||||
// Deploy container with proper error handling
|
||||
const successResponse = await deployDockerContainer(formData);
|
||||
|
||||
@@ -773,24 +774,13 @@ deployForm.addEventListener('submit', async (e) => {
|
||||
|
||||
hideStatusIndicator();
|
||||
|
||||
try {
|
||||
if (typeof showOperationStatus === 'function') {
|
||||
showOperationStatus(`Deploying ${containerName}`, 'completed');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[WARN] Failed to show operation status:', e);
|
||||
}
|
||||
|
||||
// Close all modals including the deploy modal
|
||||
if (typeof closeAllModals === 'function') {
|
||||
closeAllModals();
|
||||
}
|
||||
closeDeployModal();
|
||||
|
||||
// Safely extract success message
|
||||
const successMessage = (successResponse && successResponse.message)
|
||||
? String(successResponse.message)
|
||||
: 'Container deployed successfully!';
|
||||
|
||||
// Update notification to success
|
||||
notificationManager.add('success', `Container "${containerName}" created successfully!`);
|
||||
showAlert('success', successMessage);
|
||||
} catch (error) {
|
||||
// Safely extract error message
|
||||
@@ -813,17 +803,8 @@ deployForm.addEventListener('submit', async (e) => {
|
||||
console.warn('[WARN] Failed to hide status indicator:', e);
|
||||
}
|
||||
|
||||
// Safely get container name for error status
|
||||
try {
|
||||
const containerName = (formData && formData.containerName)
|
||||
? String(formData.containerName)
|
||||
: 'container';
|
||||
if (typeof showOperationStatus === 'function') {
|
||||
showOperationStatus(`Deploying ${containerName}`, 'failed');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[WARN] Failed to show operation status:', e);
|
||||
}
|
||||
// Update notification to error (containerName already defined above)
|
||||
notificationManager.add('danger', `Failed to create container "${containerName}"`);
|
||||
|
||||
showAlert('danger', errorMessage);
|
||||
}
|
||||
@@ -831,8 +812,20 @@ deployForm.addEventListener('submit', async (e) => {
|
||||
|
||||
// Save template functionality removed to match working version
|
||||
|
||||
// Initialize templates on load
|
||||
document.addEventListener('DOMContentLoaded', fetchTemplates);
|
||||
// Initialize templates on load - defer to not block initialization
|
||||
if (typeof requestIdleCallback !== 'undefined') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
requestIdleCallback(() => {
|
||||
fetchTemplates();
|
||||
}, { timeout: 2000 });
|
||||
});
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(() => {
|
||||
fetchTemplates();
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
// Duplicate modal array management functions
|
||||
let duplicatePortCounter = 0;
|
||||
|
||||
+37
-7
@@ -6,11 +6,11 @@
|
||||
import notificationManager from './notifications.js';
|
||||
|
||||
/**
|
||||
* Close all open Bootstrap modals
|
||||
* Close all open Bootstrap modals and clean up any lingering backdrops
|
||||
*/
|
||||
export function closeAllModals() {
|
||||
// Find and hide all open modals
|
||||
const modals = document.querySelectorAll('.modal.show');
|
||||
const modals = document.querySelectorAll('.modal.show, .modal[style*="display"]');
|
||||
modals.forEach(modal => {
|
||||
const modalInstance = bootstrap.Modal.getInstance(modal);
|
||||
if (modalInstance) {
|
||||
@@ -20,7 +20,29 @@ export function closeAllModals() {
|
||||
const newInstance = new bootstrap.Modal(modal);
|
||||
newInstance.hide();
|
||||
}
|
||||
// Also directly hide the modal element as fallback
|
||||
modal.classList.remove('show');
|
||||
modal.style.display = 'none';
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
modal.removeAttribute('aria-modal');
|
||||
});
|
||||
|
||||
// Remove any lingering modal backdrops
|
||||
const backdrops = document.querySelectorAll('.modal-backdrop');
|
||||
backdrops.forEach(backdrop => {
|
||||
backdrop.remove();
|
||||
});
|
||||
|
||||
// Clean up body classes and styles
|
||||
document.body.classList.remove('modal-open');
|
||||
document.body.style.paddingRight = '';
|
||||
document.body.style.overflow = '';
|
||||
|
||||
// Also handle custom terminal modal if it exists
|
||||
const terminalModal = document.getElementById('terminal-modal');
|
||||
if (terminalModal && terminalModal.style.display !== 'none') {
|
||||
terminalModal.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,14 +66,22 @@ export function showStatusIndicator(message = 'Processing...') {
|
||||
|
||||
/**
|
||||
* Hide status indicator overlay
|
||||
* Safe to call before DOM is ready
|
||||
*/
|
||||
export function hideStatusIndicator() {
|
||||
const statusIndicator = document.getElementById('status-indicator');
|
||||
if (statusIndicator) {
|
||||
console.log('[DEBUG] Hiding status indicator');
|
||||
statusIndicator.remove();
|
||||
// Use requestAnimationFrame to ensure DOM is ready, or check immediately if already loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const statusIndicator = document.getElementById('status-indicator');
|
||||
if (statusIndicator) {
|
||||
statusIndicator.remove();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('[ERROR] Status indicator element not found!');
|
||||
const statusIndicator = document.getElementById('status-indicator');
|
||||
if (statusIndicator) {
|
||||
statusIndicator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user