Files
web-bakery/script.js
2025-09-27 23:46:54 +01:00

1017 lines
35 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ===== GLOBAL VARIABLES =====
let products = [];
let cart = JSON.parse(localStorage.getItem('sweetDreamsCart')) || [];
let filteredProducts = [];
// EmailJS Configuration
const emailJSConfig = {
serviceID: 'YOUR_SERVICE_ID', // Replace with actual EmailJS service ID
templateID: 'YOUR_TEMPLATE_ID', // Replace with actual EmailJS template ID
userID: 'YOUR_USER_ID' // Replace with actual EmailJS user ID
};
// ===== INITIALIZATION =====
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
});
function initializeApp() {
// Initialize EmailJS
if (typeof emailjs !== 'undefined') {
emailjs.init(emailJSConfig.userID);
}
// Load products
loadProducts();
// Initialize event listeners
initializeEventListeners();
// Update cart UI
updateCartUI();
// Initialize smooth scrolling
initializeSmoothScrolling();
// Initialize header scroll effect
initializeHeaderEffects();
}
// ===== PRODUCT LOADING =====
async function loadProducts() {
try {
showLoading();
const response = await fetch('cake_inventory.csv');
const csvText = await response.text();
products = parseCSV(csvText);
filteredProducts = [...products];
hideLoading();
renderProducts(filteredProducts);
} catch (error) {
console.error('Error loading products:', error);
hideLoading();
showError('Failed to load products. Using demo data.');
// Fallback to demo data if CSV fails to load
products = getDemoProducts();
filteredProducts = [...products];
renderProducts(filteredProducts);
}
}
function parseCSV(csvText) {
const lines = csvText.trim().split('\n');
const headers = lines[0].split(',');
const products = [];
for (let i = 1; i < lines.length; i++) {
const values = parseCSVLine(lines[i]);
const product = {};
headers.forEach((header, index) => {
const key = header.trim();
let value = values[index] ? values[index].trim() : '';
// Convert numeric fields
if (['id', 'price', 'size_inches', 'serves_people', 'weight_kg', 'stock_quantity'].includes(key)) {
value = parseFloat(value) || 0;
}
// Convert boolean fields
if (['featured', 'custom_available'].includes(key)) {
value = value.toLowerCase() === 'true';
}
product[key] = value;
});
products.push(product);
}
return products;
}
function parseCSVLine(line) {
const result = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
result.push(current);
current = '';
} else {
current += char;
}
}
result.push(current);
return result;
}
function getDemoProducts() {
// Fallback demo data in case CSV loading fails
return [
{
id: 1,
name: "Classic Vanilla Wedding Cake",
description: "Elegant three-tier vanilla sponge with buttercream roses and pearl details. Perfect centerpiece for your special day.",
price: 299.99,
category: "Wedding Cakes",
size_inches: 10,
serves_people: 50,
weight_kg: 3.5,
flavor: "Vanilla",
image_url: "https://images.unsplash.com/photo-1578985545062-69928b1d9587?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
image_alt: "Three-tier vanilla wedding cake with white buttercream roses",
stock_quantity: 2,
featured: true,
custom_available: true,
dietary_options: "gluten-free available, vegan available",
baker_name: "Sarah Mitchell",
occasion_type: "wedding"
},
{
id: 2,
name: "Chocolate Decadence Birthday Cake",
description: "Rich chocolate cake layered with dark chocolate ganache and fresh berries. A chocolate lover's dream.",
price: 89.99,
category: "Birthday Cakes",
size_inches: 8,
serves_people: 12,
weight_kg: 2.2,
flavor: "Dark Chocolate",
image_url: "https://images.unsplash.com/photo-1606313564200-e75d5e30476c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
image_alt: "Dark chocolate birthday cake with ganache and berries",
stock_quantity: 5,
featured: true,
custom_available: true,
dietary_options: "gluten-free available",
baker_name: "Sarah Mitchell",
occasion_type: "birthday"
}
];
}
// ===== PRODUCT RENDERING =====
function renderProducts(productsToRender) {
const grid = document.getElementById('products-grid');
const noResults = document.getElementById('no-results');
if (productsToRender.length === 0) {
grid.innerHTML = '';
noResults.style.display = 'block';
return;
}
noResults.style.display = 'none';
const html = productsToRender.map(product => createProductCard(product)).join('');
grid.innerHTML = html;
// Add event listeners to product cards
addProductEventListeners();
}
function createProductCard(product) {
const isOutOfStock = product.stock_quantity === 0;
const featuredBadge = product.featured ? '<div class="product__badge">Featured</div>' : '';
return `
<article class="product__card" data-product-id="${product.id}">
<div class="product__image">
<img src="${product.image_url}" alt="${product.image_alt}" loading="lazy"
onerror="this.src='https://images.unsplash.com/photo-1565958011703-44f9829ba187?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80'">
${featuredBadge}
</div>
<div class="product__content">
<div class="product__category">${product.category}</div>
<h3 class="product__name">${product.name}</h3>
<p class="product__description">${product.description}</p>
<div class="product__meta">
<span><i class="fas fa-users"></i> Serves ${product.serves_people}</span>
<span><i class="fas fa-ruler"></i> ${product.size_inches}"</span>
</div>
<div class="product__footer">
<div class="product__price">$${product.price.toFixed(2)}</div>
<button class="add-to-cart" data-product-id="${product.id}" ${isOutOfStock ? 'disabled' : ''}>
<i class="fas fa-cart-plus"></i>
${isOutOfStock ? 'Out of Stock' : 'Add to Cart'}
</button>
</div>
</div>
</article>
`;
}
function addProductEventListeners() {
// Product card click to show details
document.querySelectorAll('.product__card').forEach(card => {
card.addEventListener('click', function(e) {
if (!e.target.closest('.add-to-cart')) {
const productId = parseInt(this.dataset.productId);
showProductDetails(productId);
}
});
});
// Add to cart buttons
document.querySelectorAll('.add-to-cart').forEach(button => {
button.addEventListener('click', function(e) {
e.stopPropagation();
const productId = parseInt(this.dataset.productId);
addToCart(productId);
});
});
}
// ===== PRODUCT FILTERING & SEARCH =====
function initializeFiltering() {
const searchInput = document.getElementById('search-input');
const categoryFilter = document.getElementById('category-filter');
const sortSelect = document.getElementById('sort-select');
searchInput.addEventListener('input', debounce(filterProducts, 300));
categoryFilter.addEventListener('change', filterProducts);
sortSelect.addEventListener('change', filterProducts);
}
function filterProducts() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const category = document.getElementById('category-filter').value;
const sortBy = document.getElementById('sort-select').value;
// Filter by search term
filteredProducts = products.filter(product => {
const searchFields = [
product.name,
product.description,
product.flavor,
product.category,
product.occasion_type
].join(' ').toLowerCase();
return searchFields.includes(searchTerm);
});
// Filter by category
if (category !== 'all') {
filteredProducts = filteredProducts.filter(product => product.category === category);
}
// Sort products
filteredProducts.sort((a, b) => {
switch (sortBy) {
case 'price-low':
return a.price - b.price;
case 'price-high':
return b.price - a.price;
case 'name':
return a.name.localeCompare(b.name);
case 'featured':
default:
return (b.featured ? 1 : 0) - (a.featured ? 1 : 0);
}
});
renderProducts(filteredProducts);
}
// ===== SHOPPING CART =====
function addToCart(productId, quantity = 1) {
const product = products.find(p => p.id === productId);
if (!product) return;
if (product.stock_quantity === 0) {
showNotification('This item is currently out of stock', 'error');
return;
}
const existingItem = cart.find(item => item.id === productId);
if (existingItem) {
const newQuantity = existingItem.quantity + quantity;
if (newQuantity <= product.stock_quantity) {
existingItem.quantity = newQuantity;
showNotification(`Updated ${product.name} quantity in cart`, 'success');
} else {
showNotification(`Only ${product.stock_quantity} items available`, 'warning');
return;
}
} else {
cart.push({
id: product.id,
name: product.name,
price: product.price,
image_url: product.image_url,
quantity: quantity,
max_quantity: product.stock_quantity
});
showNotification(`Added ${product.name} to cart`, 'success');
}
saveCart();
updateCartUI();
}
function removeFromCart(productId) {
cart = cart.filter(item => item.id !== productId);
saveCart();
updateCartUI();
renderCartItems();
}
function updateCartQuantity(productId, quantity) {
const item = cart.find(item => item.id === productId);
if (!item) return;
if (quantity <= 0) {
removeFromCart(productId);
return;
}
if (quantity > item.max_quantity) {
showNotification(`Only ${item.max_quantity} items available`, 'warning');
return;
}
item.quantity = quantity;
saveCart();
updateCartUI();
renderCartItems();
}
function saveCart() {
localStorage.setItem('sweetDreamsCart', JSON.stringify(cart));
}
function updateCartUI() {
const cartCount = document.getElementById('cart-count');
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
cartCount.textContent = totalItems;
cartCount.style.display = totalItems > 0 ? 'flex' : 'none';
}
function getCartTotal() {
return cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
}
// ===== MODAL MANAGEMENT =====
function initializeModals() {
// Cart modal
const cartBtn = document.getElementById('cart-btn');
const cartModal = document.getElementById('cart-modal');
const cartClose = document.getElementById('cart-modal-close');
cartBtn.addEventListener('click', showCartModal);
cartClose.addEventListener('click', () => hideModal('cart-modal'));
// Product modal
const productModal = document.getElementById('product-modal');
const productClose = document.getElementById('product-modal-close');
productClose.addEventListener('click', () => hideModal('product-modal'));
// Checkout modal
const checkoutBtn = document.getElementById('checkout-btn');
const checkoutModal = document.getElementById('checkout-modal');
const checkoutClose = document.getElementById('checkout-modal-close');
checkoutBtn.addEventListener('click', showCheckoutModal);
checkoutClose.addEventListener('click', () => hideModal('checkout-modal'));
// Close modals on backdrop click
[cartModal, productModal, checkoutModal].forEach(modal => {
modal.addEventListener('click', function(e) {
if (e.target === this) {
hideModal(this.id);
}
});
});
// Close modals on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const activeModal = document.querySelector('.modal.active');
if (activeModal) {
hideModal(activeModal.id);
}
}
});
}
function showModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
function hideModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.remove('active');
document.body.style.overflow = '';
}
function showCartModal() {
renderCartItems();
showModal('cart-modal');
}
function renderCartItems() {
const cartItems = document.getElementById('cart-items');
const cartEmpty = document.getElementById('cart-empty');
const cartFooter = document.getElementById('cart-footer');
const cartTotal = document.getElementById('cart-total');
if (cart.length === 0) {
cartItems.innerHTML = '';
cartEmpty.style.display = 'block';
cartFooter.style.display = 'none';
return;
}
cartEmpty.style.display = 'none';
cartFooter.style.display = 'block';
const html = cart.map(item => `
<div class="cart__item" data-product-id="${item.id}">
<img src="${item.image_url}" alt="${item.name}" class="cart__item-image"
onerror="this.src='https://images.unsplash.com/photo-1565958011703-44f9829ba187?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80'">
<div class="cart__item-content">
<div class="cart__item-name">${item.name}</div>
<div class="cart__item-price">$${item.price.toFixed(2)} each</div>
<div class="cart__item-controls">
<button class="quantity-btn" onclick="updateCartQuantity(${item.id}, ${item.quantity - 1})">
<i class="fas fa-minus"></i>
</button>
<input type="number" class="quantity-input" value="${item.quantity}"
min="1" max="${item.max_quantity}"
onchange="updateCartQuantity(${item.id}, parseInt(this.value))">
<button class="quantity-btn" onclick="updateCartQuantity(${item.id}, ${item.quantity + 1})">
<i class="fas fa-plus"></i>
</button>
<button class="remove-item" onclick="removeFromCart(${item.id})">
<i class="fas fa-trash"></i> Remove
</button>
</div>
</div>
</div>
`).join('');
cartItems.innerHTML = html;
cartTotal.textContent = `$${getCartTotal().toFixed(2)}`;
}
function showProductDetails(productId) {
const product = products.find(p => p.id === productId);
if (!product) return;
const modalTitle = document.getElementById('product-modal-title');
const productDetail = document.getElementById('product-detail');
modalTitle.textContent = product.name;
const isOutOfStock = product.stock_quantity === 0;
productDetail.innerHTML = `
<div class="product__detail-content">
<div class="product__detail-image">
<img src="${product.image_url}" alt="${product.image_alt}"
onerror="this.src='https://images.unsplash.com/photo-1565958011703-44f9829ba187?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80'">
</div>
<div class="product__detail-info">
<div class="product__category">${product.category}</div>
<h3 class="product__name">${product.name}</h3>
<p class="product__description">${product.description}</p>
<div class="product__specs">
<h4>Specifications</h4>
<ul>
<li><strong>Size:</strong> ${product.size_inches}" diameter</li>
<li><strong>Serves:</strong> ${product.serves_people} people</li>
<li><strong>Weight:</strong> ${product.weight_kg} kg</li>
<li><strong>Flavor:</strong> ${product.flavor}</li>
<li><strong>Baker:</strong> ${product.baker_name}</li>
</ul>
</div>
${product.dietary_options && product.dietary_options !== 'none available' ? `
<div class="product__dietary">
<h4>Dietary Options</h4>
<p>${product.dietary_options}</p>
</div>
` : ''}
${product.custom_available ? `
<div class="product__custom">
<i class="fas fa-palette"></i>
<span>Custom designs available - contact us for personalization</span>
</div>
` : ''}
<div class="product__price-section">
<div class="product__price">$${product.price.toFixed(2)}</div>
<button class="btn btn--primary" onclick="addToCart(${product.id})" ${isOutOfStock ? 'disabled' : ''}>
<i class="fas fa-cart-plus"></i>
${isOutOfStock ? 'Out of Stock' : 'Add to Cart'}
</button>
</div>
<div class="product__stock">
${product.stock_quantity > 0 ?
`<i class="fas fa-check-circle"></i> ${product.stock_quantity} available` :
'<i class="fas fa-times-circle"></i> Currently out of stock'
}
</div>
</div>
</div>
`;
showModal('product-modal');
}
// ===== CHECKOUT =====
function showCheckoutModal() {
if (cart.length === 0) {
showNotification('Your cart is empty', 'warning');
return;
}
renderOrderSummary();
calculateOrderTotal();
hideModal('cart-modal');
showModal('checkout-modal');
}
function renderOrderSummary() {
const orderSummary = document.getElementById('order-summary');
const html = cart.map(item => `
<div class="order__item">
<span>${item.name} × ${item.quantity}</span>
<span>$${(item.price * item.quantity).toFixed(2)}</span>
</div>
`).join('');
orderSummary.innerHTML = html;
}
function calculateOrderTotal() {
const deliverySelect = document.getElementById('checkout-delivery');
const subtotal = getCartTotal();
const deliveryFee = deliverySelect.value === 'delivery' ? 15 : 0;
const total = subtotal + deliveryFee;
document.getElementById('order-subtotal').textContent = `$${subtotal.toFixed(2)}`;
document.getElementById('order-delivery').textContent = `$${deliveryFee.toFixed(2)}`;
document.getElementById('order-total').textContent = `$${total.toFixed(2)}`;
}
// ===== FORM HANDLING =====
function initializeForms() {
// Contact form
const contactForm = document.getElementById('contact-form');
contactForm.addEventListener('submit', handleContactForm);
// Checkout form
const checkoutForm = document.getElementById('checkout-form');
checkoutForm.addEventListener('submit', handleCheckoutForm);
// Newsletter form
const newsletterForm = document.getElementById('newsletter-form');
newsletterForm.addEventListener('submit', handleNewsletterForm);
// Delivery method change
const deliverySelect = document.getElementById('checkout-delivery');
deliverySelect.addEventListener('change', function() {
const deliveryAddress = document.getElementById('delivery-address');
const isDelivery = this.value === 'delivery';
deliveryAddress.style.display = isDelivery ? 'block' : 'none';
// Update required attributes
const addressInputs = deliveryAddress.querySelectorAll('input');
addressInputs.forEach(input => {
input.required = isDelivery;
});
calculateOrderTotal();
});
}
async function handleContactForm(e) {
e.preventDefault();
const form = e.target;
const submitBtn = form.querySelector('button[type="submit"]');
const formData = new FormData(form);
// Show loading state
submitBtn.classList.add('loading');
try {
// Prepare email data
const emailData = {
from_name: formData.get('name'),
from_email: formData.get('email'),
phone: formData.get('phone') || 'Not provided',
inquiry_type: formData.get('inquiry_type'),
message: formData.get('message')
};
// Send email via EmailJS
if (typeof emailjs !== 'undefined' && emailJSConfig.serviceID !== 'YOUR_SERVICE_ID') {
await emailjs.send(emailJSConfig.serviceID, emailJSConfig.templateID, emailData);
showNotification('Message sent successfully! We\'ll get back to you soon.', 'success');
} else {
// Demo mode - simulate success
await new Promise(resolve => setTimeout(resolve, 1000));
showNotification('Demo mode: Message would be sent via EmailJS', 'info');
console.log('Contact form data:', emailData);
}
form.reset();
} catch (error) {
console.error('Error sending message:', error);
showNotification('Failed to send message. Please try again.', 'error');
} finally {
submitBtn.classList.remove('loading');
}
}
async function handleCheckoutForm(e) {
e.preventDefault();
const form = e.target;
const submitBtn = form.querySelector('button[type="submit"]');
const formData = new FormData(form);
// Validate form
if (!validateCheckoutForm(formData)) {
return;
}
// Show loading state
submitBtn.classList.add('loading');
try {
// Prepare order data
const orderData = {
customer: {
firstName: formData.get('first_name'),
lastName: formData.get('last_name'),
email: formData.get('email'),
phone: formData.get('phone')
},
delivery: {
method: formData.get('delivery_method'),
address: formData.get('address') || 'Pickup at bakery',
city: formData.get('city') || '',
zip: formData.get('zip') || ''
},
event: {
date: formData.get('event_date'),
time: formData.get('event_time') || 'Flexible',
specialRequests: formData.get('special_requests') || 'None'
},
items: cart,
totals: {
subtotal: getCartTotal(),
delivery: formData.get('delivery_method') === 'delivery' ? 15 : 0,
total: getCartTotal() + (formData.get('delivery_method') === 'delivery' ? 15 : 0)
},
orderDate: new Date().toISOString()
};
// Send order via EmailJS
if (typeof emailjs !== 'undefined' && emailJSConfig.serviceID !== 'YOUR_SERVICE_ID') {
await sendOrderEmails(orderData);
showNotification('Order placed successfully! Check your email for confirmation.', 'success');
} else {
// Demo mode - simulate success
await new Promise(resolve => setTimeout(resolve, 2000));
showNotification('Demo mode: Order would be processed via EmailJS', 'info');
console.log('Order data:', orderData);
}
// Clear cart and close modal
cart = [];
saveCart();
updateCartUI();
hideModal('checkout-modal');
// Show success message
showOrderConfirmation(orderData);
} catch (error) {
console.error('Error processing order:', error);
showNotification('Failed to process order. Please try again.', 'error');
} finally {
submitBtn.classList.remove('loading');
}
}
async function handleNewsletterForm(e) {
e.preventDefault();
const form = e.target;
const email = form.querySelector('input[type="email"]').value;
try {
// In a real application, this would be sent to your email service
console.log('Newsletter subscription:', email);
showNotification('Successfully subscribed to newsletter!', 'success');
form.reset();
} catch (error) {
showNotification('Failed to subscribe. Please try again.', 'error');
}
}
function validateCheckoutForm(formData) {
const requiredFields = ['first_name', 'last_name', 'email', 'phone', 'delivery_method', 'event_date'];
for (const field of requiredFields) {
if (!formData.get(field)) {
showNotification(`Please fill in the ${field.replace('_', ' ')} field`, 'warning');
return false;
}
}
// Validate email
const email = formData.get('email');
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showNotification('Please enter a valid email address', 'warning');
return false;
}
// Validate event date (should be in the future)
const eventDate = new Date(formData.get('event_date'));
const today = new Date();
today.setHours(0, 0, 0, 0);
if (eventDate < today) {
showNotification('Event date must be in the future', 'warning');
return false;
}
return true;
}
// ===== EMAIL INTEGRATION =====
async function sendOrderEmails(orderData) {
// Prepare email templates
const customerEmailData = {
to_name: `${orderData.customer.firstName} ${orderData.customer.lastName}`,
to_email: orderData.customer.email,
order_number: generateOrderNumber(),
order_items: formatOrderItems(orderData.items),
order_total: orderData.totals.total.toFixed(2),
event_date: orderData.event.date,
delivery_method: orderData.delivery.method,
special_requests: orderData.event.specialRequests
};
const businessEmailData = {
customer_name: `${orderData.customer.firstName} ${orderData.customer.lastName}`,
customer_email: orderData.customer.email,
customer_phone: orderData.customer.phone,
order_details: JSON.stringify(orderData, null, 2)
};
// Send customer confirmation email
await emailjs.send(emailJSConfig.serviceID, 'customer_confirmation', customerEmailData);
// Send business notification email
await emailjs.send(emailJSConfig.serviceID, 'business_notification', businessEmailData);
}
function generateOrderNumber() {
const timestamp = Date.now().toString().slice(-6);
const random = Math.random().toString(36).substr(2, 3).toUpperCase();
return `SD${timestamp}${random}`;
}
function formatOrderItems(items) {
return items.map(item =>
`${item.name} (Quantity: ${item.quantity}) - $${(item.price * item.quantity).toFixed(2)}`
).join('\n');
}
// ===== NAVIGATION & UI =====
function initializeNavigation() {
const navToggle = document.getElementById('nav-toggle');
const navClose = document.getElementById('nav-close');
const navMenu = document.getElementById('nav-menu');
const navLinks = document.querySelectorAll('.nav__link');
// Mobile menu toggle
navToggle.addEventListener('click', () => {
navMenu.classList.add('show-menu');
});
navClose.addEventListener('click', () => {
navMenu.classList.remove('show-menu');
});
// Close menu on link click
navLinks.forEach(link => {
link.addEventListener('click', () => {
navMenu.classList.remove('show-menu');
});
});
// Active link highlighting
updateActiveLink();
window.addEventListener('scroll', updateActiveLink);
}
function updateActiveLink() {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav__link');
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop - 100;
const sectionHeight = section.offsetHeight;
if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active-link');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active-link');
}
});
}
function initializeSmoothScrolling() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const headerHeight = document.querySelector('.header').offsetHeight;
const targetPosition = target.offsetTop - headerHeight;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
}
function initializeHeaderEffects() {
const header = document.getElementById('header');
window.addEventListener('scroll', () => {
if (window.scrollY > 100) {
header.style.background = 'rgba(250, 248, 245, 0.98)';
header.style.boxShadow = '0 2px 20px rgba(61, 53, 48, 0.1)';
} else {
header.style.background = 'rgba(250, 248, 245, 0.95)';
header.style.boxShadow = 'none';
}
});
}
// ===== UTILITY FUNCTIONS =====
function showLoading() {
document.getElementById('products-loading').style.display = 'block';
}
function hideLoading() {
document.getElementById('products-loading').style.display = 'none';
}
function showNotification(message, type = 'info') {
const notification = document.getElementById('notification');
const icon = notification.querySelector('.notification__icon');
const messageElement = notification.querySelector('.notification__message');
// Set icon based on type
const icons = {
success: 'fas fa-check-circle',
error: 'fas fa-exclamation-circle',
warning: 'fas fa-exclamation-triangle',
info: 'fas fa-info-circle'
};
icon.className = `notification__icon ${icons[type]}`;
messageElement.textContent = message;
notification.className = `notification ${type} show`;
// Auto-hide after 5 seconds
setTimeout(() => {
notification.classList.remove('show');
}, 5000);
}
function showError(message) {
showNotification(message, 'error');
}
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function showOrderConfirmation(orderData) {
const confirmationModal = document.createElement('div');
confirmationModal.className = 'modal active';
confirmationModal.innerHTML = `
<div class="modal__content">
<div class="modal__header">
<h3 class="modal__title">Order Confirmed!</h3>
<button class="modal__close" onclick="this.closest('.modal').remove()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal__body">
<div class="order-confirmation">
<div class="confirmation__icon">
<i class="fas fa-check-circle"></i>
</div>
<h4>Thank you, ${orderData.customer.firstName}!</h4>
<p>Your order has been received and we'll contact you within 24 hours to:</p>
<ul>
<li>Confirm your order details</li>
<li>Discuss any customization requirements</li>
<li>Arrange payment and delivery/pickup</li>
<li>Finalize your cake design</li>
</ul>
<div class="order-summary">
<h5>Order Summary:</h5>
<p><strong>Event Date:</strong> ${orderData.event.date}</p>
<p><strong>Total:</strong> $${orderData.totals.total.toFixed(2)}</p>
<p><strong>Delivery:</strong> ${orderData.delivery.method === 'delivery' ? 'Home Delivery' : 'Pickup at Bakery'}</p>
</div>
<p><strong>What's next?</strong> Check your email for a detailed confirmation. We'll be in touch soon!</p>
</div>
</div>
</div>
`;
document.body.appendChild(confirmationModal);
// Auto-remove after user interaction
confirmationModal.addEventListener('click', function(e) {
if (e.target === this || e.target.closest('.modal__close')) {
this.remove();
}
});
}
// ===== EVENT LISTENERS INITIALIZATION =====
function initializeEventListeners() {
initializeNavigation();
initializeModals();
initializeForms();
initializeFiltering();
// Close notifications
document.addEventListener('click', function(e) {
if (e.target.closest('.notification')) {
e.target.closest('.notification').classList.remove('show');
}
});
}
// ===== ERROR HANDLING =====
window.addEventListener('error', function(e) {
console.error('Application error:', e.error);
showNotification('An unexpected error occurred. Please refresh the page.', 'error');
});
// ===== SERVICE WORKER (OPTIONAL) =====
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('ServiceWorker registration successful');
}, function(err) {
console.log('ServiceWorker registration failed');
});
});
}