Files
orion/static/shared/js/utils.js
Samir Boulahtit 6f8434f200 feat: add PlatformSettings for pagination and vendor filter improvements
Platform Settings:
- Add PlatformSettings utility in init-alpine.js with 5-min cache
- Add Display tab in /admin/settings for rows_per_page config
- Integrate PlatformSettings.getRowsPerPage() in all paginated pages
- Standardize default per_page to 20 across all admin pages
- Add documentation at docs/frontend/shared/platform-settings.md

Architecture Rules:
- Add JS-010: enforce PlatformSettings usage for pagination
- Add JS-011: enforce standard pagination structure
- Add JS-012: detect double /api/v1 prefix in apiClient calls
- Implement all rules in validate_architecture.py

Vendor Filter (Tom Select):
- Add vendor filter to marketplace-products, vendor-products,
  customers, inventory, and vendor-themes pages
- Add selectedVendor display panel with clear button
- Add localStorage persistence for vendor selection
- Fix double /api/v1 prefix in vendor-selector.js

Bug Fixes:
- Remove duplicate PlatformSettings from utils.js
- Fix customers.js pagination structure (page_size → per_page)
- Fix code-quality-violations.js pagination structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 22:39:34 +01:00

197 lines
6.0 KiB
JavaScript

// static/shared/js/utils.js
/**
* 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);
this.showToast('Copied to clipboard', 'success');
} catch (error) {
console.error('Failed to copy:', error);
this.showToast('Failed to copy', '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;
}