/** * Common utilities for the SciPaperLoader application */ /** * Display a flash message to the user as an overlay * @param {string} message - The message to display * @param {string} type - The type of message (success, error, warning, info) * @param {number} duration - Duration in milliseconds (default: 5000) */ function showFlashMessage(message, type = "success", duration = 5000) { const flashMsg = document.createElement("div"); const normalizedType = type === "error" ? "danger" : type; flashMsg.className = `flash-overlay flash-${normalizedType}`; // Get the appropriate icon based on type const getIcon = (messageType) => { switch (messageType) { case "success": return ''; case "danger": return ''; case "warning": return ''; case "info": return ''; default: return ''; } }; flashMsg.innerHTML = `
${getIcon(normalizedType)}
${message}
`; // Handle stacking of multiple messages const existingMessages = document.querySelectorAll('.flash-overlay'); existingMessages.forEach((msg, index) => { msg.style.setProperty('--flash-index', index + 1); }); // Add to page document.body.appendChild(flashMsg); // Auto dismiss setTimeout(() => { flashMsg.classList.add("fade-out"); setTimeout(() => { if (flashMsg.parentNode) { flashMsg.remove(); // Reposition remaining messages const remainingMessages = document.querySelectorAll('.flash-overlay'); remainingMessages.forEach((msg, index) => { msg.style.setProperty('--flash-index', index); }); } }, 300); }, duration); return flashMsg; } /** * Create a status badge HTML element * @param {string} status - The status to create a badge for * @returns {string} HTML string for the status badge */ function createStatusBadge(status) { switch (status) { case "New": return 'New'; case "Pending": return 'Pending'; case "Done": return 'Done'; case "Failed": return 'Failed'; case "success": return 'Success'; case "error": return 'Error'; case "pending": return 'Pending'; default: return `${status}`; } } /** * Format a timestamp to a readable time string * @param {string} timestamp - ISO timestamp string * @returns {string} Formatted time string */ function formatTimestamp(timestamp) { const date = new Date(timestamp); return date.toLocaleTimeString("de-DE", { year: "2-digit", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", }); } /** * Truncate text to a specified length * @param {string} text - The text to truncate * @param {number} maxLength - Maximum length before truncation * @returns {string} Truncated text with ellipsis if needed */ function truncateText(text, maxLength) { return text.length > maxLength ? text.substring(0, maxLength) + "..." : text; } /** * Toggle button loading state * @param {HTMLElement} button - The button element * @param {boolean} loading - Whether to show loading state * @param {string} loadingText - Text to show when loading */ function toggleButtonLoading(button, loading, loadingText = "Loading...") { if (loading) { button.disabled = true; button.dataset.originalText = button.innerHTML; button.innerHTML = ` ${loadingText}`; } else { button.disabled = false; button.innerHTML = button.dataset.originalText || button.innerHTML; } } /** * Generic fetch wrapper with error handling * @param {string} url - The URL to fetch * @param {object} options - Fetch options * @returns {Promise} Fetch promise */ async function apiRequest(url, options = {}) { const defaultOptions = { headers: { "Content-Type": "application/json", }, }; const mergedOptions = { ...defaultOptions, ...options }; try { const response = await fetch(url, mergedOptions); const data = await response.json(); return data; } catch (error) { console.error(`API request failed for ${url}:`, error); throw error; } }