169 lines
5.1 KiB
JavaScript
169 lines
5.1 KiB
JavaScript
|
|
// Global utility functions
|
||
|
|
|
||
|
|
// Toast notifications
|
||
|
|
function showToast(message, type = 'info') {
|
||
|
|
const container = document.getElementById('toast-container');
|
||
|
|
const toast = document.createElement('div');
|
||
|
|
|
||
|
|
const colors = {
|
||
|
|
success: 'bg-green-500',
|
||
|
|
error: 'bg-red-500',
|
||
|
|
info: 'bg-primary',
|
||
|
|
warning: 'bg-yellow-500'
|
||
|
|
};
|
||
|
|
|
||
|
|
toast.className = `${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg flex items-center gap-3 animate-slide-in`;
|
||
|
|
toast.innerHTML = `
|
||
|
|
<span class="material-symbols-outlined text-[20px]">
|
||
|
|
${type === 'success' ? 'check_circle' : type === 'error' ? 'error' : 'info'}
|
||
|
|
</span>
|
||
|
|
<span>${message}</span>
|
||
|
|
`;
|
||
|
|
|
||
|
|
container.appendChild(toast);
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
toast.style.opacity = '0';
|
||
|
|
toast.style.transform = 'translateX(100%)';
|
||
|
|
setTimeout(() => toast.remove(), 300);
|
||
|
|
}, 3000);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format currency
|
||
|
|
function formatCurrency(amount, currency = 'USD') {
|
||
|
|
const symbols = {
|
||
|
|
'USD': '$',
|
||
|
|
'EUR': '€',
|
||
|
|
'GBP': '£',
|
||
|
|
'RON': 'lei'
|
||
|
|
};
|
||
|
|
|
||
|
|
const symbol = symbols[currency] || currency;
|
||
|
|
const formatted = parseFloat(amount).toFixed(2);
|
||
|
|
|
||
|
|
if (currency === 'RON') {
|
||
|
|
return `${formatted} ${symbol}`;
|
||
|
|
}
|
||
|
|
return `${symbol}${formatted}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format date
|
||
|
|
function formatDate(dateString) {
|
||
|
|
const date = new Date(dateString);
|
||
|
|
const now = new Date();
|
||
|
|
const diff = now - date;
|
||
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||
|
|
|
||
|
|
if (days === 0) return 'Today';
|
||
|
|
if (days === 1) return 'Yesterday';
|
||
|
|
if (days < 7) return `${days} days ago`;
|
||
|
|
|
||
|
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// API helper
|
||
|
|
async function apiCall(url, options = {}) {
|
||
|
|
try {
|
||
|
|
const response = await fetch(url, {
|
||
|
|
...options,
|
||
|
|
headers: {
|
||
|
|
...options.headers,
|
||
|
|
'Content-Type': options.body instanceof FormData ? undefined : 'application/json',
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return await response.json();
|
||
|
|
} catch (error) {
|
||
|
|
console.error('API call failed:', error);
|
||
|
|
showToast('An error occurred. Please try again.', 'error');
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Theme management
|
||
|
|
function initTheme() {
|
||
|
|
// Check for saved theme preference or default to system preference
|
||
|
|
const savedTheme = localStorage.getItem('theme');
|
||
|
|
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||
|
|
|
||
|
|
if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) {
|
||
|
|
document.documentElement.classList.add('dark');
|
||
|
|
updateThemeUI(true);
|
||
|
|
} else {
|
||
|
|
document.documentElement.classList.remove('dark');
|
||
|
|
updateThemeUI(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function toggleTheme() {
|
||
|
|
const isDark = document.documentElement.classList.contains('dark');
|
||
|
|
|
||
|
|
if (isDark) {
|
||
|
|
document.documentElement.classList.remove('dark');
|
||
|
|
localStorage.setItem('theme', 'light');
|
||
|
|
updateThemeUI(false);
|
||
|
|
} else {
|
||
|
|
document.documentElement.classList.add('dark');
|
||
|
|
localStorage.setItem('theme', 'dark');
|
||
|
|
updateThemeUI(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dispatch custom event for other components to react to theme change
|
||
|
|
window.dispatchEvent(new CustomEvent('theme-changed', { detail: { isDark: !isDark } }));
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateThemeUI(isDark) {
|
||
|
|
const themeIcon = document.getElementById('theme-icon');
|
||
|
|
const themeText = document.getElementById('theme-text');
|
||
|
|
|
||
|
|
if (themeIcon && themeText) {
|
||
|
|
if (isDark) {
|
||
|
|
themeIcon.textContent = 'dark_mode';
|
||
|
|
themeText.textContent = 'Dark Mode';
|
||
|
|
} else {
|
||
|
|
themeIcon.textContent = 'light_mode';
|
||
|
|
themeText.textContent = 'Light Mode';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mobile menu toggle
|
||
|
|
document.addEventListener('DOMContentLoaded', () => {
|
||
|
|
// Initialize theme
|
||
|
|
initTheme();
|
||
|
|
|
||
|
|
// Theme toggle button
|
||
|
|
const themeToggle = document.getElementById('theme-toggle');
|
||
|
|
if (themeToggle) {
|
||
|
|
themeToggle.addEventListener('click', toggleTheme);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mobile menu
|
||
|
|
const menuToggle = document.getElementById('menu-toggle');
|
||
|
|
const sidebar = document.getElementById('sidebar');
|
||
|
|
|
||
|
|
if (menuToggle && sidebar) {
|
||
|
|
menuToggle.addEventListener('click', () => {
|
||
|
|
sidebar.classList.toggle('hidden');
|
||
|
|
sidebar.classList.toggle('flex');
|
||
|
|
sidebar.classList.toggle('absolute');
|
||
|
|
sidebar.classList.toggle('z-50');
|
||
|
|
sidebar.style.left = '0';
|
||
|
|
});
|
||
|
|
|
||
|
|
// Close sidebar when clicking outside on mobile
|
||
|
|
document.addEventListener('click', (e) => {
|
||
|
|
if (window.innerWidth < 1024) {
|
||
|
|
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
|
||
|
|
sidebar.classList.add('hidden');
|
||
|
|
sidebar.classList.remove('flex');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|