AutoBackups/static/js/app.js

300 lines
9.6 KiB
JavaScript

// AutoBackups JavaScript Application
class AutoBackupsApp {
constructor() {
this.init();
}
init() {
this.initializeTheme();
this.updateCurrentTime();
this.setupEventListeners();
// Update time every minute
setInterval(() => this.updateCurrentTime(), 60000);
}
updateCurrentTime() {
const now = new Date();
const timeString = now.toLocaleString('es-ES', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
const timeElement = document.getElementById('current-time');
if (timeElement) {
timeElement.textContent = timeString;
}
}
// Theme Management
initializeTheme() {
// Load saved theme from localStorage or default to light
const savedTheme = localStorage.getItem('theme') || 'light';
this.setTheme(savedTheme);
}
setTheme(theme) {
const htmlElement = document.documentElement;
const themeIcon = document.getElementById('theme-icon');
if (theme === 'dark') {
htmlElement.setAttribute('data-theme', 'dark');
if (themeIcon) {
themeIcon.className = 'bi bi-moon-fill';
}
} else {
htmlElement.removeAttribute('data-theme');
if (themeIcon) {
themeIcon.className = 'bi bi-sun-fill';
}
}
localStorage.setItem('theme', theme);
}
toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
this.setTheme(newTheme);
}
setupEventListeners() {
// Theme toggle button
const themeToggleBtn = document.getElementById('theme-toggle');
if (themeToggleBtn) {
themeToggleBtn.addEventListener('click', () => this.toggleTheme());
}
// Global backup all button
const backupAllBtn = document.getElementById('backup-all-btn');
if (backupAllBtn) {
backupAllBtn.addEventListener('click', () => this.backupAllProjects());
}
// Save project configuration
const saveConfigBtn = document.getElementById('save-project-config-btn');
if (saveConfigBtn) {
saveConfigBtn.addEventListener('click', () => this.saveProjectConfig());
}
}
backupAllProjects() {
this.showAlert('info', 'Iniciando backup de todos los proyectos habilitados...');
// Get all enabled projects and trigger backup
fetch('/api/projects')
.then(response => response.json())
.then(data => {
if (data.error) {
this.showAlert('danger', `Error: ${data.error}`);
return;
}
const enabledProjects = data.projects.filter(project =>
project.schedule_config && project.schedule_config.enabled
);
if (enabledProjects.length === 0) {
this.showAlert('warning', 'No hay proyectos habilitados para backup.');
return;
}
// Backup each enabled project
let completed = 0;
enabledProjects.forEach(project => {
fetch(`/api/projects/${project.id}/backup`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(backupData => {
completed++;
if (completed === enabledProjects.length) {
this.showAlert('success',
`Backup completado para ${enabledProjects.length} proyectos.`);
}
})
.catch(error => {
console.error(`Error backing up project ${project.id}:`, error);
});
});
})
.catch(error => {
this.showAlert('danger', `Error de conexión: ${error.message}`);
});
}
saveProjectConfig() {
const projectId = document.getElementById('modal-project-id').value;
const schedule = document.getElementById('modal-project-schedule').value;
const time = document.getElementById('modal-project-time').value;
const enabled = document.getElementById('modal-project-enabled').checked;
const config = {
schedule_config: {
schedule: schedule,
schedule_time: time,
enabled: enabled
}
};
fetch(`/api/projects/${projectId}/config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(config)
})
.then(response => response.json())
.then(data => {
if (data.error) {
this.showAlert('danger', `Error: ${data.error}`);
} else {
this.showAlert('success', 'Configuración guardada correctamente.');
// Close modal
const modal = bootstrap.Modal.getInstance(
document.getElementById('projectConfigModal')
);
if (modal) {
modal.hide();
}
// Refresh page after a short delay
setTimeout(() => location.reload(), 1500);
}
})
.catch(error => {
this.showAlert('danger', `Error de conexión: ${error.message}`);
});
}
showAlert(type, message, duration = 5000) {
const alertsContainer = document.getElementById('alerts-container');
if (!alertsContainer) return;
const alertId = 'alert-' + Date.now();
const alertHtml = `
<div class="alert alert-${type} alert-dismissible fade show fade-in" role="alert" id="${alertId}">
<i class="bi bi-${this.getAlertIcon(type)}"></i>
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`;
alertsContainer.insertAdjacentHTML('beforeend', alertHtml);
// Auto-dismiss after duration
if (duration > 0) {
setTimeout(() => {
const alertElement = document.getElementById(alertId);
if (alertElement) {
const alert = new bootstrap.Alert(alertElement);
alert.close();
}
}, duration);
}
}
getAlertIcon(type) {
const icons = {
'success': 'check-circle',
'danger': 'exclamation-triangle',
'warning': 'exclamation-triangle',
'info': 'info-circle',
'primary': 'info-circle',
'secondary': 'info-circle'
};
return icons[type] || 'info-circle';
}
// Utility method to format file sizes
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Utility method to format dates
formatDate(dateString) {
if (!dateString) return 'N/A';
const date = new Date(dateString);
return date.toLocaleString('es-ES', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// Method to show loading state
showLoading(element) {
if (element) {
element.classList.add('loading');
const originalContent = element.innerHTML;
element.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span> Cargando...';
return originalContent;
}
}
// Method to hide loading state
hideLoading(element, originalContent) {
if (element && originalContent) {
element.classList.remove('loading');
element.innerHTML = originalContent;
}
}
// Method to make API calls with error handling
async apiCall(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `HTTP error! status: ${response.status}`);
}
return data;
} catch (error) {
console.error('API call failed:', error);
this.showAlert('danger', `Error de API: ${error.message}`);
throw error;
}
}
}
// Global functions for backward compatibility
function showAlert(type, message, duration = 5000) {
if (window.autobackupsApp) {
window.autobackupsApp.showAlert(type, message, duration);
}
}
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
window.autobackupsApp = new AutoBackupsApp();
});
// Export for modules if needed
if (typeof module !== 'undefined' && module.exports) {
module.exports = AutoBackupsApp;
}