/** * Main Application JavaScript * Handles interactions, animations, and utilities */ // ============================================ // SCROLL REVEAL ANIMATION // ============================================ function initScrollReveal() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); // Unobserve after revealing (performance) observer.unobserve(entry.target); } }); }, observerOptions); document.querySelectorAll('.scroll-reveal').forEach(el => { observer.observe(el); }); } // ============================================ // SMOOTH HORIZONTAL SCROLL // ============================================ function initHorizontalScroll() { document.querySelectorAll('.horizontal-scroll').forEach(container => { let isDown = false; let startX; let scrollLeft; container.addEventListener('mousedown', (e) => { isDown = true; container.style.cursor = 'grabbing'; startX = e.pageX - container.offsetLeft; scrollLeft = container.scrollLeft; }); container.addEventListener('mouseleave', () => { isDown = false; container.style.cursor = 'grab'; }); container.addEventListener('mouseup', () => { isDown = false; container.style.cursor = 'grab'; }); container.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - container.offsetLeft; const walk = (x - startX) * 2; container.scrollLeft = scrollLeft - walk; }); }); } // ============================================ // TOAST NOTIFICATIONS // ============================================ function showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.style.cssText = ` position: fixed; bottom: 100px; left: 50%; transform: translateX(-50%); background: ${type === 'success' ? 'var(--color-success)' : 'var(--color-error)'}; color: white; padding: var(--space-md) var(--space-lg); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); z-index: 1000; animation: fadeInUp 0.3s ease-out; `; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'fadeIn 0.3s ease-out reverse'; setTimeout(() => toast.remove(), 300); }, 3000); } // ============================================ // ADD TO CART // ============================================ function addToCart(serviceId, serviceName) { // Simulate API call fetch('../api/cart/add.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ service_id: serviceId, quantity: 1 }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast(`${serviceName} added to cart!`, 'success'); updateCartCount(); } else { showToast('Failed to add to cart', 'error'); } }) .catch(error => { console.error('Error:', error); showToast('Something went wrong', 'error'); }); } // ============================================ // UPDATE CART COUNT // ============================================ function updateCartCount() { fetch('../api/cart/count.php') .then(response => response.json()) .then(data => { const cartBadge = document.querySelector('.cart-badge'); if (cartBadge && data.count > 0) { cartBadge.textContent = data.count; cartBadge.style.display = 'flex'; } }); } // ============================================ // SEARCH FUNCTIONALITY // ============================================ function initSearch() { const searchInput = document.getElementById('searchInput'); if (!searchInput) return; let debounceTimer; searchInput.addEventListener('input', function(e) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const searchTerm = e.target.value.toLowerCase(); filterItems(searchTerm); }, 300); }); } function filterItems(searchTerm) { const items = document.querySelectorAll('[data-name]'); items.forEach(item => { const name = item.dataset.name.toLowerCase(); if (name.includes(searchTerm)) { item.style.display = ''; item.classList.add('fade-in'); } else { item.style.display = 'none'; } }); } // ============================================ // LOADING STATE // ============================================ function showLoading() { const loader = document.createElement('div'); loader.id = 'global-loader'; loader.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.9); display: flex; align-items: center; justify-content: center; z-index: 9999; `; loader.innerHTML = '
'; document.body.appendChild(loader); } function hideLoading() { const loader = document.getElementById('global-loader'); if (loader) { loader.style.animation = 'fadeIn 0.3s ease-out reverse'; setTimeout(() => loader.remove(), 300); } } // ============================================ // LAZY LOAD IMAGES // ============================================ function initLazyLoad() { const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.add('fade-in'); observer.unobserve(img); } }); }); document.querySelectorAll('img[data-src]').forEach(img => { imageObserver.observe(img); }); } // ============================================ // INITIALIZE ON DOM READY // ============================================ document.addEventListener('DOMContentLoaded', function() { // Initialize all features initScrollReveal(); initHorizontalScroll(); initSearch(); initLazyLoad(); updateCartCount(); // Add smooth scroll behavior document.documentElement.style.scrollBehavior = 'smooth'; console.log('🎨 Roz Skin App Initialized'); }); // ============================================ // UTILITY FUNCTIONS // ============================================ // Format price function formatPrice(price) { return new Intl.NumberFormat('en-EG', { style: 'currency', currency: 'EGP', minimumFractionDigits: 0 }).format(price); } // Format date function formatDate(date) { return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).format(new Date(date)); } // Debounce function function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Export functions for global use window.RozSkin = { showToast, addToCart, updateCartCount, showLoading, hideLoading, formatPrice, formatDate, debounce };