/** * Common utilities for the SciPaperLoader application */ /** * Display a flash message to the user * @param {string} message - The message to display * @param {string} type - The type of message (success, error, warning, info) */ function showFlashMessage(message, type) { const flashContainer = document.createElement("div"); flashContainer.className = `alert alert-${ type === "error" ? "danger" : type } alert-dismissible fade show`; flashContainer.innerHTML = ` ${message} `; // Use the existing client flash container at the top of the page const clientFlashContainer = document.getElementById("clientFlashContainer"); if (clientFlashContainer) { clientFlashContainer.appendChild(flashContainer); } else { // Fallback to body if container not found document.body.appendChild(flashContainer); } // Auto dismiss after 5 seconds setTimeout(() => { flashContainer.classList.remove("show"); setTimeout(() => { flashContainer.remove(); }, 150); // Remove after fade out animation }, 5000); } /** * 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; } }