rooting-future / static /js /main.js
mtornani's picture
Initial HF Spaces deployment (clean branch without large binaries)
38f9c15
/**
* Rooting Future Strategy Engine v5.4
* Main JavaScript
*/
// ============================================================================
// UTILITIES
// ============================================================================
/**
* Make API request with JSON
*/
async function apiRequest(url, method = 'GET', data = null) {
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (data) {
options.body = JSON.stringify(data);
}
try {
const response = await fetch(url, options);
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Request failed');
}
return result;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
/**
* Show notification toast
*/
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
// Animate in
setTimeout(() => toast.classList.add('show'), 10);
// Remove after 3 seconds
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
/**
* Format date
*/
function formatDate(isoString) {
if (!isoString) return 'N/A';
const date = new Date(isoString);
return date.toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}
/**
* Debounce function
*/
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// ============================================================================
// FORM HANDLING
// ============================================================================
/**
* Serialize form to object
*/
function serializeForm(form) {
const formData = new FormData(form);
const data = {};
for (const [key, value] of formData.entries()) {
if (data[key]) {
if (!Array.isArray(data[key])) {
data[key] = [data[key]];
}
data[key].push(value);
} else {
data[key] = value;
}
}
return data;
}
/**
* Validate form
*/
function validateForm(form) {
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
isValid = false;
field.classList.add('error');
} else {
field.classList.remove('error');
}
});
return isValid;
}
// ============================================================================
// MODAL HANDLING
// ============================================================================
/**
* Open modal
*/
function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
}
/**
* Close modal
*/
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
document.body.style.overflow = '';
}
}
// Close modal on backdrop click
document.addEventListener('click', (e) => {
if (e.target.classList.contains('modal')) {
e.target.style.display = 'none';
document.body.style.overflow = '';
}
});
// Close modal on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const openModals = document.querySelectorAll('.modal[style*="flex"]');
openModals.forEach(modal => {
modal.style.display = 'none';
});
document.body.style.overflow = '';
}
});
// ============================================================================
// SEARCH & FILTER
// ============================================================================
/**
* Initialize live search
*/
function initLiveSearch(inputId, tableId) {
const input = document.getElementById(inputId);
const table = document.getElementById(tableId);
if (!input || !table) return;
const search = debounce(() => {
const query = input.value.toLowerCase();
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(query) ? '' : 'none';
});
}, 300);
input.addEventListener('input', search);
}
// ============================================================================
// LOADING STATES
// ============================================================================
/**
* Show loading spinner on button
*/
function setButtonLoading(button, loading = true) {
if (loading) {
button.disabled = true;
button.dataset.originalText = button.innerHTML;
button.innerHTML = '<span class="spinner"></span> Caricamento...';
} else {
button.disabled = false;
button.innerHTML = button.dataset.originalText || button.innerHTML;
}
}
/**
* Show loading overlay
*/
function showLoading(message = 'Caricamento...') {
let overlay = document.getElementById('loadingOverlay');
if (!overlay) {
overlay = document.createElement('div');
overlay.id = 'loadingOverlay';
overlay.className = 'loading-overlay';
overlay.innerHTML = `
<div class="loading-content">
<div class="spinner-large"></div>
<p id="loadingMessage">${message}</p>
</div>
`;
document.body.appendChild(overlay);
} else {
document.getElementById('loadingMessage').textContent = message;
overlay.style.display = 'flex';
}
}
/**
* Hide loading overlay
*/
function hideLoading() {
const overlay = document.getElementById('loadingOverlay');
if (overlay) {
overlay.style.display = 'none';
}
}
// ============================================================================
// CLIPBOARD
// ============================================================================
/**
* Copy text to clipboard
*/
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast('Copiato negli appunti', 'success');
} catch (err) {
console.error('Failed to copy:', err);
showToast('Errore nella copia', 'error');
}
}
// ============================================================================
// LOCAL STORAGE
// ============================================================================
/**
* Save to local storage
*/
function saveToStorage(key, data) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (e) {
console.error('Storage error:', e);
}
}
/**
* Load from local storage
*/
function loadFromStorage(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (e) {
console.error('Storage error:', e);
return defaultValue;
}
}
// ============================================================================
// INITIALIZATION
// ============================================================================
document.addEventListener('DOMContentLoaded', () => {
// Initialize any live search fields
initLiveSearch('searchInput', 'dataTable');
// Auto-hide alerts after 5 seconds
const alerts = document.querySelectorAll('.alert:not(.alert-persistent)');
alerts.forEach(alert => {
setTimeout(() => {
alert.style.opacity = '0';
setTimeout(() => alert.remove(), 300);
}, 5000);
});
console.log('Rooting Future Strategy Engine v5.4 initialized');
});
// ============================================================================
// EXPORT
// ============================================================================
window.RootingFuture = {
apiRequest,
showToast,
formatDate,
openModal,
closeModal,
setButtonLoading,
showLoading,
hideLoading,
copyToClipboard,
saveToStorage,
loadFromStorage,
};