// static/shared/js/utils.js // noqa: js-001 - Core utilities, may log before logger is available /** * Utility functions for the application */ const Utils = { /** * Format date for display * @param {string} dateString - ISO date string * @returns {string} Formatted date */ formatDate(dateString) { if (!dateString) return '-'; try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } catch (error) { console.error('Error formatting date:', error); return dateString; } }, /** * Format date with time * @param {string} dateString - ISO date string * @returns {string} Formatted date with time */ formatDateTime(dateString) { if (!dateString) return '-'; try { const date = new Date(dateString); return date.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (error) { console.error('Error formatting datetime:', error); return dateString; } }, /** * Format currency * @param {number} amount - Amount to format * @param {string} currency - Currency code (default: USD) * @returns {string} Formatted currency */ formatCurrency(amount, currency = 'USD') { if (amount === null || amount === undefined) return '-'; try { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency }).format(amount); } catch (error) { console.error('Error formatting currency:', error); return amount.toString(); } }, /** * Format number with commas * @param {number} num - Number to format * @returns {string} Formatted number */ formatNumber(num) { if (num === null || num === undefined) return '0'; return num.toLocaleString('en-US'); }, /** * Show toast notification * @param {string} message - Toast message * @param {string} type - Toast type: 'success', 'error', 'warning', 'info' * @param {number} duration - Duration in ms (default: 3000) */ showToast(message, type = 'info', duration = 3000) { // Create toast element const toast = document.createElement('div'); toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg text-white z-50 transition-opacity duration-300 ${ type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : type === 'warning' ? 'bg-yellow-500' : 'bg-blue-500' }`; toast.textContent = message; document.body.appendChild(toast); // Fade out and remove setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, duration); }, /** * Debounce function * @param {Function} func - Function to debounce * @param {number} wait - Wait time in ms * @returns {Function} Debounced function */ debounce(func, wait = 300) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, /** * Get query parameter from URL * @param {string} param - Parameter name * @returns {string|null} Parameter value */ getQueryParam(param) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(param); }, /** * Copy text to clipboard * @param {string} text - Text to copy */ async copyToClipboard(text) { try { await navigator.clipboard.writeText(text); // Use I18n if available, fallback to hardcoded string const message = typeof I18n !== 'undefined' ? I18n.t('clipboard.copied') : 'Copied to clipboard'; this.showToast(message, 'success'); } catch (error) { console.error('Failed to copy:', error); const message = typeof I18n !== 'undefined' ? I18n.t('clipboard.failed') : 'Failed to copy'; this.showToast(message, 'error'); } }, /** * Truncate string * @param {string} str - String to truncate * @param {number} maxLength - Maximum length * @returns {string} Truncated string */ truncate(str, maxLength = 50) { if (!str || str.length <= maxLength) return str; return str.substring(0, maxLength - 3) + '...'; }, /** * Validate email format * @param {string} email - Email to validate * @returns {boolean} Is valid email */ isValidEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return re.test(email); }, /** * Get status badge class * @param {string} status - Status value * @returns {string} Tailwind classes for badge */ getStatusBadgeClass(status) { const statusClasses = { 'active': 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100', 'inactive': 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', 'pending': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100', 'verified': 'bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100', 'rejected': 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100' }; return statusClasses[status.toLowerCase()] || 'bg-gray-100 text-gray-800'; } }; // ============================================================================ // Platform Settings // ============================================================================ // Make available globally // Note: PlatformSettings is defined in init-alpine.js for admin pages window.Utils = Utils; // Export for modules if (typeof module !== 'undefined' && module.exports) { module.exports = Utils; }