Files
web-pottery/script.js
2025-09-27 01:46:37 +01:00

1259 lines
44 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 filteredProducts = [];
let cart = [];
let currentCategory = 'all';
// EmailJS configuration
const EMAILJS_CONFIG = {
PUBLIC_KEY: 'WyyhSl4fWpg-8i8F3', // Replace with your EmailJS public key
SERVICE_ID: 'service_hlwvn2h', // Replace with your EmailJS service ID
TEMPLATE_ID: 'template_qf9hvwa' // Replace with your EmailJS template ID
};
// DOM elements
const productsGrid = document.getElementById('products-grid');
const cartBtn = document.getElementById('cart-btn');
const cartCount = document.getElementById('cart-count');
const cartModal = document.getElementById('cart-modal');
const checkoutModal = document.getElementById('checkout-modal');
const productModal = document.getElementById('product-modal');
const cartItems = document.getElementById('cart-items');
const cartTotal = document.getElementById('cart-total');
const searchInput = document.getElementById('search-input');
const searchBtn = document.getElementById('search-btn');
const sortSelect = document.getElementById('sort-select');
const filterBtns = document.querySelectorAll('.filter-btn');
const loading = document.getElementById('loading');
const noProducts = document.getElementById('no-products');
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
// Initialize EmailJS
initializeEmailJS();
loadProducts();
initializeEventListeners();
loadCartFromStorage();
});
// Initialize EmailJS
function initializeEmailJS() {
if (typeof emailjs !== 'undefined') {
emailjs.init(EMAILJS_CONFIG.PUBLIC_KEY);
console.log('EmailJS initialized successfully');
} else {
console.warn('EmailJS not loaded. Email functionality will be simulated.');
}
}
// Load products from CSV
async function loadProducts() {
try {
loading.style.display = 'block';
const response = await fetch('pottery_inventory.csv');
const csvText = await response.text();
products = parseCSV(csvText);
filteredProducts = [...products];
displayProducts();
loading.style.display = 'none';
} catch (error) {
console.error('Error loading products:', error);
loading.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Error loading products. Please refresh the page.';
}
}
// Parse CSV data
function parseCSV(csvText) {
const lines = csvText.trim().split('\n');
const headers = lines[0].split(',').map(header => header.replace(/"/g, ''));
const products = [];
for (let i = 1; i < lines.length; i++) {
const values = parseCSVLine(lines[i]);
if (values.length === headers.length) {
const product = {};
headers.forEach((header, index) => {
product[header] = values[index];
});
// Convert numeric fields
product.price = parseFloat(product.price);
product.stock_quantity = parseInt(product.stock_quantity);
product.dimensions_height_cm = parseFloat(product.dimensions_height_cm);
product.dimensions_width_cm = parseFloat(product.dimensions_width_cm);
product.dimensions_depth_cm = parseFloat(product.dimensions_depth_cm);
product.weight_kg = parseFloat(product.weight_kg);
product.featured = product.featured.toLowerCase() === 'true';
product.glazed = product.glazed.toLowerCase() === 'true';
product.dishwasher_safe = product.dishwasher_safe.toLowerCase() === 'true';
product.microwave_safe = product.microwave_safe.toLowerCase() === 'true';
product.handmade = product.handmade.toLowerCase() === 'true';
products.push(product);
}
}
return products;
}
// Parse CSV line handling quoted fields
function parseCSVLine(line) {
const result = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"' && (i === 0 || line[i-1] === ',')) {
inQuotes = true;
} else if (char === '"' && inQuotes && (i === line.length - 1 || line[i+1] === ',')) {
inQuotes = false;
} else if (char === ',' && !inQuotes) {
result.push(current.trim());
current = '';
} else {
current += char;
}
}
result.push(current.trim());
return result;
}
// Display products in the grid
function displayProducts() {
if (filteredProducts.length === 0) {
productsGrid.style.display = 'none';
noProducts.style.display = 'block';
return;
}
productsGrid.style.display = 'grid';
noProducts.style.display = 'none';
productsGrid.innerHTML = filteredProducts.map(product => createProductCard(product)).join('');
// Add event listeners to product cards and buttons
document.querySelectorAll('.product-card').forEach(card => {
card.addEventListener('click', (e) => {
if (!e.target.classList.contains('add-to-cart')) {
const productId = card.dataset.productId;
showProductDetails(productId);
}
});
});
document.querySelectorAll('.add-to-cart').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const productId = btn.dataset.productId;
addToCart(productId);
});
});
}
// Create product card HTML
function createProductCard(product) {
const isLowStock = product.stock_quantity <= 5;
const isOutOfStock = product.stock_quantity === 0;
return `
<div class="product-card" data-product-id="${product.id}">
<div class="product-image">
${product.featured ? '<div class="featured-badge">Featured</div>' : ''}
<i class="fas fa-circle" style="color: ${getColorByCategory(product.category)}"></i>
</div>
<div class="product-info">
<h3 class="product-name">${product.name}</h3>
<p class="product-description">${product.description}</p>
<div class="product-details">
<span class="product-price">$${product.price.toFixed(2)}</span>
<span class="product-stock ${isLowStock ? 'stock-low' : ''}">
${isOutOfStock ? 'Out of Stock' : `${product.stock_quantity} in stock`}
</span>
</div>
<div class="product-meta">
<span><i class="fas fa-tag"></i> ${product.category}</span>
<span><i class="fas fa-palette"></i> ${product.color}</span>
</div>
<button class="add-to-cart" data-product-id="${product.id}" ${isOutOfStock ? 'disabled' : ''}>
${isOutOfStock ? 'Out of Stock' : 'Add to Cart'}
</button>
</div>
</div>
`;
}
// Get color based on category for visual variety
function getColorByCategory(category) {
const colors = {
'Mugs': '#8B4513',
'Bowls': '#CD853F',
'Vases': '#D2691E',
'Plant Pots': '#A0522D',
'Tea Sets': '#8FBC8F',
'Plates': '#DEB887',
'Pitchers': '#4682B4',
'Serving Dishes': '#9370DB'
};
return colors[category] || '#8B4513';
}
// Show product details modal
function showProductDetails(productId) {
const product = products.find(p => p.id === productId);
if (!product) return;
const modalContent = `
<div class="product-details-grid">
<div class="product-details-image">
<i class="fas fa-circle" style="color: ${getColorByCategory(product.category)}"></i>
</div>
<div class="product-details-info">
<h3>${product.name}</h3>
<p class="product-details-price">$${product.price.toFixed(2)}</p>
<p>${product.description}</p>
<div class="product-specs">
<h4>Specifications</h4>
<div class="specs-grid">
<div class="spec-item">
<span>Material:</span>
<span>${product.material}</span>
</div>
<div class="spec-item">
<span>Color:</span>
<span>${product.color}</span>
</div>
<div class="spec-item">
<span>Height:</span>
<span>${product.dimensions_height_cm} cm</span>
</div>
<div class="spec-item">
<span>Width:</span>
<span>${product.dimensions_width_cm} cm</span>
</div>
<div class="spec-item">
<span>Depth:</span>
<span>${product.dimensions_depth_cm} cm</span>
</div>
<div class="spec-item">
<span>Weight:</span>
<span>${product.weight_kg} kg</span>
</div>
<div class="spec-item">
<span>Artist:</span>
<span>${product.artist}</span>
</div>
<div class="spec-item">
<span>SKU:</span>
<span>${product.sku}</span>
</div>
</div>
</div>
<div class="product-features">
<h4>Features</h4>
<ul style="list-style: none; padding: 0;">
${product.handmade ? '<li><i class="fas fa-hand-paper"></i> Handmade</li>' : ''}
${product.glazed ? '<li><i class="fas fa-sparkles"></i> Glazed finish</li>' : ''}
${product.dishwasher_safe ? '<li><i class="fas fa-utensils"></i> Dishwasher safe</li>' : ''}
${product.microwave_safe ? '<li><i class="fas fa-microwave"></i> Microwave safe</li>' : ''}
</ul>
</div>
<button class="add-to-cart" data-product-id="${product.id}" ${product.stock_quantity === 0 ? 'disabled' : ''}>
${product.stock_quantity === 0 ? 'Out of Stock' : 'Add to Cart - $' + product.price.toFixed(2)}
</button>
</div>
</div>
`;
document.getElementById('product-details-content').innerHTML = modalContent;
productModal.style.display = 'block';
// Add event listener to the add to cart button in modal
const addToCartBtn = productModal.querySelector('.add-to-cart');
if (addToCartBtn && !addToCartBtn.disabled) {
addToCartBtn.addEventListener('click', () => {
addToCart(productId);
productModal.style.display = 'none';
});
}
}
// Add product to cart
function addToCart(productId) {
const product = products.find(p => p.id === productId);
if (!product || product.stock_quantity === 0) return;
const existingItem = cart.find(item => item.id === productId);
if (existingItem) {
if (existingItem.quantity < product.stock_quantity) {
existingItem.quantity++;
} else {
showNotification('Maximum stock quantity reached', 'warning');
return;
}
} else {
cart.push({
id: product.id,
name: product.name,
price: product.price,
quantity: 1,
category: product.category,
color: product.color
});
}
updateCartUI();
saveCartToStorage();
showNotification('Added to cart!', 'success');
}
// Remove product from cart
function removeFromCart(productId) {
cart = cart.filter(item => item.id !== productId);
updateCartUI();
saveCartToStorage();
}
// Update cart quantity
function updateCartQuantity(productId, newQuantity) {
const product = products.find(p => p.id === productId);
const cartItem = cart.find(item => item.id === productId);
if (!cartItem || !product) return;
if (newQuantity <= 0) {
removeFromCart(productId);
return;
}
if (newQuantity > product.stock_quantity) {
showNotification('Maximum stock quantity reached', 'warning');
return;
}
cartItem.quantity = newQuantity;
updateCartUI();
saveCartToStorage();
}
// Update cart UI
function updateCartUI() {
const itemCount = cart.reduce((total, item) => total + item.quantity, 0);
const totalPrice = cart.reduce((total, item) => total + (item.price * item.quantity), 0);
cartCount.textContent = itemCount;
cartTotal.textContent = totalPrice.toFixed(2);
// Update cart items display
if (cart.length === 0) {
cartItems.innerHTML = '<p style="text-align: center; padding: 2rem; color: #666;">Your cart is empty</p>';
} else {
cartItems.innerHTML = cart.map(item => createCartItemHTML(item)).join('');
// Add event listeners to cart item controls
addCartEventListeners();
}
}
// Create cart item HTML
function createCartItemHTML(item) {
return `
<div class="cart-item" data-product-id="${item.id}">
<div class="cart-item-image">
<i class="fas fa-circle" style="color: ${getColorByCategory(item.category)}"></i>
</div>
<div class="cart-item-details">
<div class="cart-item-name">${item.name}</div>
<div class="cart-item-price">$${item.price.toFixed(2)} each</div>
</div>
<div class="quantity-controls">
<button class="quantity-btn decrease" data-product-id="${item.id}">-</button>
<span class="quantity">${item.quantity}</span>
<button class="quantity-btn increase" data-product-id="${item.id}">+</button>
</div>
<button class="remove-item" data-product-id="${item.id}">
<i class="fas fa-trash"></i>
</button>
</div>
`;
}
// Add event listeners to cart controls
function addCartEventListeners() {
document.querySelectorAll('.quantity-btn.decrease').forEach(btn => {
btn.addEventListener('click', () => {
const productId = btn.dataset.productId;
const cartItem = cart.find(item => item.id === productId);
updateCartQuantity(productId, cartItem.quantity - 1);
});
});
document.querySelectorAll('.quantity-btn.increase').forEach(btn => {
btn.addEventListener('click', () => {
const productId = btn.dataset.productId;
const cartItem = cart.find(item => item.id === productId);
updateCartQuantity(productId, cartItem.quantity + 1);
});
});
document.querySelectorAll('.remove-item').forEach(btn => {
btn.addEventListener('click', () => {
const productId = btn.dataset.productId;
removeFromCart(productId);
});
});
}
// Filter products by category
function filterByCategory(category) {
currentCategory = category;
applyFilters();
// Update active filter button
filterBtns.forEach(btn => btn.classList.remove('active'));
document.querySelector(`[data-category="${category}"]`).classList.add('active');
}
// Search products
function searchProducts(searchTerm) {
const term = searchTerm.toLowerCase();
filteredProducts = products.filter(product =>
product.name.toLowerCase().includes(term) ||
product.description.toLowerCase().includes(term) ||
product.category.toLowerCase().includes(term) ||
product.material.toLowerCase().includes(term) ||
product.color.toLowerCase().includes(term) ||
product.artist.toLowerCase().includes(term)
);
applyFilters();
}
// Apply all active filters
function applyFilters() {
let filtered = [...products];
// Apply category filter
if (currentCategory !== 'all') {
filtered = filtered.filter(product => product.category === currentCategory);
}
// Apply search filter
const searchTerm = searchInput.value.toLowerCase();
if (searchTerm) {
filtered = filtered.filter(product =>
product.name.toLowerCase().includes(searchTerm) ||
product.description.toLowerCase().includes(searchTerm) ||
product.category.toLowerCase().includes(searchTerm) ||
product.material.toLowerCase().includes(searchTerm) ||
product.color.toLowerCase().includes(searchTerm) ||
product.artist.toLowerCase().includes(searchTerm)
);
}
filteredProducts = filtered;
sortProducts();
displayProducts();
}
// Sort products
function sortProducts() {
const sortValue = sortSelect.value;
switch (sortValue) {
case 'price-low':
filteredProducts.sort((a, b) => a.price - b.price);
break;
case 'price-high':
filteredProducts.sort((a, b) => b.price - a.price);
break;
case 'newest':
filteredProducts.sort((a, b) => new Date(b.date_created) - new Date(a.date_created));
break;
case 'name':
default:
filteredProducts.sort((a, b) => a.name.localeCompare(b.name));
break;
}
}
// Initialize event listeners
function initializeEventListeners() {
// Cart modal
cartBtn.addEventListener('click', () => {
cartModal.style.display = 'block';
});
// Close modals
document.querySelectorAll('.close').forEach(closeBtn => {
closeBtn.addEventListener('click', (e) => {
e.target.closest('.modal').style.display = 'none';
});
});
// Close modal when clicking outside
window.addEventListener('click', (e) => {
if (e.target.classList.contains('modal')) {
e.target.style.display = 'none';
}
});
// Filter buttons
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterByCategory(btn.dataset.category);
});
});
// Search functionality
searchBtn.addEventListener('click', () => {
applyFilters();
});
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
applyFilters();
}
});
searchInput.addEventListener('input', () => {
if (searchInput.value === '') {
applyFilters();
}
});
// Sort functionality
sortSelect.addEventListener('change', () => {
sortProducts();
displayProducts();
});
// Clear cart
document.getElementById('clear-cart').addEventListener('click', () => {
if (confirm('Are you sure you want to clear your cart?')) {
cart = [];
updateCartUI();
saveCartToStorage();
showNotification('Cart cleared', 'info');
}
});
// Checkout
document.getElementById('checkout').addEventListener('click', () => {
if (cart.length === 0) {
showNotification('Your cart is empty', 'warning');
return;
}
showCheckoutModal();
});
// Back to cart from checkout
document.getElementById('back-to-cart').addEventListener('click', () => {
checkoutModal.style.display = 'none';
cartModal.style.display = 'block';
});
// Checkout form submission
document.getElementById('checkout-form').addEventListener('submit', (e) => {
e.preventDefault();
handleCheckoutSubmission();
});
// Billing address toggle
document.getElementById('same-as-shipping').addEventListener('change', (e) => {
const billingAddress = document.getElementById('billing-address');
if (e.target.checked) {
billingAddress.style.display = 'none';
billingAddress.required = false;
} else {
billingAddress.style.display = 'block';
billingAddress.required = true;
}
});
// Contact form
document.getElementById('contact-form').addEventListener('submit', (e) => {
e.preventDefault();
handleContactFormSubmission(e.target);
});
// Newsletter form
document.querySelector('.newsletter-form').addEventListener('submit', (e) => {
e.preventDefault();
handleNewsletterSubmission(e.target);
});
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// Save cart to localStorage
function saveCartToStorage() {
localStorage.setItem('pottery-cart', JSON.stringify(cart));
}
// Load cart from localStorage
function loadCartFromStorage() {
const savedCart = localStorage.getItem('pottery-cart');
if (savedCart) {
cart = JSON.parse(savedCart);
updateCartUI();
}
}
// Show notification
function showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background: ${type === 'success' ? '#27ae60' : type === 'warning' ? '#f39c12' : type === 'error' ? '#e74c3c' : '#3498db'};
color: white;
padding: 1rem 1.5rem;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
z-index: 3000;
animation: slideInRight 0.3s ease;
max-width: 300px;
word-wrap: break-word;
`;
notification.textContent = message;
document.body.appendChild(notification);
// Add slide in animation
const style = document.createElement('style');
style.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Remove notification after 3 seconds
setTimeout(() => {
notification.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
// Header scroll effect
window.addEventListener('scroll', () => {
const header = document.querySelector('.header');
if (window.scrollY > 100) {
header.style.background = 'rgba(255, 255, 255, 0.95)';
header.style.backdropFilter = 'blur(10px)';
} else {
header.style.background = '#ffffff';
header.style.backdropFilter = 'none';
}
});
// Show checkout modal with order summary
function showCheckoutModal() {
cartModal.style.display = 'none';
populateCheckoutSummary();
checkoutModal.style.display = 'block';
}
// Populate checkout order summary
function populateCheckoutSummary() {
const checkoutItems = document.getElementById('checkout-items');
const checkoutTotal = document.getElementById('checkout-total');
const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
checkoutItems.innerHTML = cart.map(item => `
<div class="checkout-item">
<div class="checkout-item-details">
<div class="checkout-item-name">${item.name}</div>
<div class="checkout-item-meta">Qty: ${item.quantity} × $${item.price.toFixed(2)}</div>
</div>
<div class="checkout-item-price">$${(item.price * item.quantity).toFixed(2)}</div>
</div>
`).join('');
checkoutTotal.textContent = total.toFixed(2);
}
// Handle checkout form submission
function handleCheckoutSubmission() {
const formData = new FormData(document.getElementById('checkout-form'));
const customerData = Object.fromEntries(formData.entries());
// Validate required fields
const requiredFields = ['customerName', 'customerEmail', 'customerPhone', 'shippingAddress'];
const missingFields = requiredFields.filter(field => !customerData[field] || customerData[field].trim() === '');
if (missingFields.length > 0) {
showNotification('Please fill in all required fields', 'warning');
return;
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(customerData.customerEmail)) {
showNotification('Please enter a valid email address', 'warning');
return;
}
// Create order data
const orderData = {
orderId: generateOrderId(),
customer: customerData,
items: cart.map(item => ({
...item,
subtotal: item.price * item.quantity
})),
total: cart.reduce((sum, item) => sum + (item.price * item.quantity), 0),
orderDate: new Date().toISOString(),
status: 'pending_payment'
};
// Simulate email submission
sendOrderEmail(orderData);
}
// Generate unique order ID
function generateOrderId() {
const date = new Date();
const dateStr = date.getFullYear().toString() +
(date.getMonth() + 1).toString().padStart(2, '0') +
date.getDate().toString().padStart(2, '0');
const timeStr = date.getHours().toString().padStart(2, '0') +
date.getMinutes().toString().padStart(2, '0') +
date.getSeconds().toString().padStart(2, '0');
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
return `POT-${dateStr}-${timeStr}-${random}`;
}
// Simulate sending order email
function sendOrderEmail(orderData) {
// Show loading state
const submitBtn = document.querySelector('#checkout-form button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = 'Submitting Order...';
// Prepare email data for EmailJS
const emailData = prepareEmailData(orderData);
// Check if EmailJS is available
if (typeof emailjs !== 'undefined' && EMAILJS_CONFIG.PUBLIC_KEY !== 'YOUR_EMAILJS_PUBLIC_KEY') {
// Send real email using EmailJS
sendRealEmail(emailData, orderData, submitBtn, originalText);
} else {
// Fallback to simulation for demo
sendSimulatedEmail(emailData, orderData, submitBtn, originalText);
}
}
// Send real email using EmailJS
function sendRealEmail(emailData, orderData, submitBtn, originalText) {
emailjs.send(EMAILJS_CONFIG.SERVICE_ID, EMAILJS_CONFIG.TEMPLATE_ID, emailData)
.then(function(response) {
console.log('Email sent successfully:', response);
// Save order to localStorage
saveOrderToStorage(orderData);
// Show success message
showOrderConfirmation(orderData, true);
// Clear cart and close modals
clearCartAndCloseModals();
showNotification('Order submitted successfully! Confirmation email sent.', 'success');
})
.catch(function(error) {
console.error('Email sending failed:', error);
showNotification('Order submitted but email failed to send. We have your order details.', 'warning');
// Still save the order locally and show confirmation
saveOrderToStorage(orderData);
showOrderConfirmation(orderData, false);
clearCartAndCloseModals();
})
.finally(function() {
submitBtn.disabled = false;
submitBtn.textContent = originalText;
});
}
// Send simulated email (for demo when EmailJS not configured)
function sendSimulatedEmail(emailData, orderData, submitBtn, originalText) {
setTimeout(() => {
try {
console.log('Simulated Email Data:', emailData);
console.log('Order Data:', orderData);
// Save order to localStorage
saveOrderToStorage(orderData);
// Show success message
showOrderConfirmation(orderData, false);
// Clear cart and close modals
clearCartAndCloseModals();
showNotification('Order submitted successfully! (Demo mode - configure EmailJS for real emails)', 'success');
} catch (error) {
console.error('Error submitting order:', error);
showNotification('Error submitting order. Please try again.', 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = originalText;
}
}, 2000);
}
// Prepare email data for EmailJS template
function prepareEmailData(orderData) {
const { customer, items, total, orderId, orderDate } = orderData;
// Format items for email template
const itemsList = items.map(item =>
`${item.name} - ${item.category} (${item.color}) - Qty: ${item.quantity} × $${item.price.toFixed(2)} = $${item.subtotal.toFixed(2)}`
).join('\n');
const itemsTable = items.map(item =>
`<tr>
<td>${item.name}</td>
<td>${item.category}</td>
<td>${item.color}</td>
<td>$${item.price.toFixed(2)}</td>
<td>${item.quantity}</td>
<td>$${item.subtotal.toFixed(2)}</td>
</tr>`
).join('');
return {
// Basic order info
order_id: orderId,
order_date: new Date(orderDate).toLocaleString(),
order_total: total.toFixed(2),
// Customer information
customer_name: customer.customerName,
customer_email: customer.customerEmail,
customer_phone: customer.customerPhone,
preferred_contact: customer.preferredContact,
// Addresses
shipping_address: customer.shippingAddress,
billing_address: customer.billingAddress || customer.shippingAddress,
// Payment and notes
payment_preference: customer.paymentPreference,
order_notes: customer.orderNotes || 'None',
// Items (formatted for template)
items_list: itemsList,
items_table: itemsTable,
items_count: items.length,
// For customer copy
to_email: customer.customerEmail,
// Business email (you can set this in EmailJS template or here)
business_email: 'orders@artisanpottery.com',
// Additional formatted data
formatted_date: new Date(orderDate).toLocaleDateString(),
formatted_time: new Date(orderDate).toLocaleTimeString()
};
}
// Clear cart and close modals helper function
function clearCartAndCloseModals() {
cart = [];
updateCartUI();
saveCartToStorage();
checkoutModal.style.display = 'none';
// Reset form
document.getElementById('checkout-form').reset();
document.getElementById('billing-address').style.display = 'none';
}
// Generate email content for the order
function generateOrderEmailContent(orderData) {
const { customer, items, total, orderId, orderDate } = orderData;
return {
to: 'orders@artisanpottery.com', // Your business email
cc: customer.customerEmail, // Send copy to customer
subject: `New Pottery Order #${orderId} - ${customer.customerName}`,
htmlContent: `
<h2>New Order Received - Artisan Pottery Studio</h2>
<h3>Order Details</h3>
<p><strong>Order ID:</strong> ${orderId}</p>
<p><strong>Order Date:</strong> ${new Date(orderDate).toLocaleString()}</p>
<p><strong>Total Amount:</strong> $${total.toFixed(2)}</p>
<h3>Customer Information</h3>
<p><strong>Name:</strong> ${customer.customerName}</p>
<p><strong>Email:</strong> ${customer.customerEmail}</p>
<p><strong>Phone:</strong> ${customer.customerPhone}</p>
<p><strong>Preferred Contact:</strong> ${customer.preferredContact}</p>
<h3>Shipping Address</h3>
<p>${customer.shippingAddress.replace(/\n/g, '<br>')}</p>
${customer.billingAddress && !customer.sameAsShipping ? `
<h3>Billing Address</h3>
<p>${customer.billingAddress.replace(/\n/g, '<br>')}</p>
` : ''}
<h3>Order Items</h3>
<table border="1" cellpadding="10" cellspacing="0" style="border-collapse: collapse; width: 100%;">
<thead>
<tr style="background-color: #8B4513; color: white;">
<th>Item</th>
<th>Category</th>
<th>Color</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
${items.map(item => `
<tr>
<td>${item.name}</td>
<td>${item.category}</td>
<td>${item.color}</td>
<td>$${item.price.toFixed(2)}</td>
<td>${item.quantity}</td>
<td>$${item.subtotal.toFixed(2)}</td>
</tr>
`).join('')}
</tbody>
<tfoot>
<tr style="background-color: #f9f9f9; font-weight: bold;">
<td colspan="5">Total</td>
<td>$${total.toFixed(2)}</td>
</tr>
</tfoot>
</table>
<h3>Payment Information</h3>
<p><strong>Preferred Payment Method:</strong> ${customer.paymentPreference}</p>
${customer.orderNotes ? `
<h3>Special Instructions</h3>
<p>${customer.orderNotes.replace(/\n/g, '<br>')}</p>
` : ''}
<hr>
<p><strong>Next Steps:</strong></p>
<ol>
<li>Contact customer within 24 hours to confirm order</li>
<li>Arrange secure payment processing</li>
<li>Confirm shipping details and timeline</li>
<li>Update order status in system</li>
</ol>
<p>This order requires payment confirmation before processing.</p>
<p>Customer copy has been sent to: ${customer.customerEmail}</p>
`,
textContent: `
New Order - Artisan Pottery Studio
Order ID: ${orderId}
Customer: ${customer.customerName}
Email: ${customer.customerEmail}
Phone: ${customer.customerPhone}
Items:
${items.map(item => `- ${item.name} (${item.quantity}x) - $${item.subtotal.toFixed(2)}`).join('\n')}
Total: $${total.toFixed(2)}
Shipping Address:
${customer.shippingAddress}
Payment Method: ${customer.paymentPreference}
${customer.orderNotes ? `Notes: ${customer.orderNotes}` : ''}
`
};
}
// Save order to localStorage (for demo purposes)
function saveOrderToStorage(orderData) {
const orders = JSON.parse(localStorage.getItem('pottery-orders') || '[]');
orders.push(orderData);
localStorage.setItem('pottery-orders', JSON.stringify(orders));
}
// Show order confirmation
function showOrderConfirmation(orderData, emailSent = true) {
const { orderId, customer, total } = orderData;
// Create confirmation modal content
const confirmationHTML = `
<div class="order-confirmation">
<div class="confirmation-icon">
<i class="fas fa-check-circle"></i>
</div>
<h3>Order Submitted Successfully!</h3>
<div class="confirmation-details">
<p><strong>Order ID:</strong> ${orderId}</p>
<p><strong>Total:</strong> $${total.toFixed(2)}</p>
<p><strong>Customer:</strong> ${customer.customerName}</p>
<p><strong>Email:</strong> ${customer.customerEmail}</p>
</div>
<div class="confirmation-message">
<p>Thank you for your order! We've received your pottery order and will contact you within 24 hours at <strong>${customer.customerEmail}</strong> or <strong>${customer.customerPhone}</strong> to arrange secure payment.</p>
${emailSent ?
'<p><i class="fas fa-envelope"></i> A confirmation email has been sent to both you and our team.</p>' :
'<p><i class="fas fa-info-circle"></i> Your order has been saved. We will contact you soon to confirm.</p>'
}
<p><strong>Next Steps:</strong></p>
<ul style="text-align: left; display: inline-block;">
<li>We'll contact you to confirm your order</li>
<li>Arrange secure payment processing</li>
<li>Confirm shipping details and timeline</li>
<li>Begin crafting your beautiful pottery pieces</li>
</ul>
</div>
<button class="btn-primary" onclick="this.closest('.modal').style.display='none'">Close</button>
</div>
`;
// Create and show confirmation modal
const confirmationModal = document.createElement('div');
confirmationModal.className = 'modal';
confirmationModal.style.display = 'block';
confirmationModal.innerHTML = `
<div class="modal-content" style="max-width: 500px; text-align: center;">
${confirmationHTML}
</div>
`;
// Add styles for confirmation
const style = document.createElement('style');
style.textContent = `
.order-confirmation {
padding: 2rem;
}
.confirmation-icon {
font-size: 4rem;
color: #27ae60;
margin-bottom: 1rem;
}
.confirmation-details {
background: #f8f9fa;
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
}
.confirmation-details p {
margin: 0.5rem 0;
}
.confirmation-message {
text-align: left;
line-height: 1.6;
margin: 1.5rem 0;
}
.confirmation-message ul {
margin: 1rem 0;
}
.confirmation-message li {
margin: 0.5rem 0;
}
`;
document.head.appendChild(style);
document.body.appendChild(confirmationModal);
// Remove modal when clicking outside
confirmationModal.addEventListener('click', (e) => {
if (e.target === confirmationModal) {
confirmationModal.remove();
}
});
// Auto-remove after 30 seconds
setTimeout(() => {
if (confirmationModal.parentNode) {
confirmationModal.remove();
}
}, 30000);
}
// Handle contact form submission
function handleContactFormSubmission(form) {
const formData = new FormData(form);
const contactData = Object.fromEntries(formData.entries());
// Validate required fields
if (!contactData.name || !contactData.email || !contactData.message) {
showNotification('Please fill in all fields', 'warning');
return;
}
// Show loading state
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
// Prepare contact email data
const emailData = {
from_name: contactData.name,
from_email: contactData.email,
message: contactData.message,
contact_date: new Date().toLocaleString(),
to_email: 'info@artisanpottery.com'
};
// Send email using EmailJS (you'll need a separate template for contact forms)
if (typeof emailjs !== 'undefined' && EMAILJS_CONFIG.PUBLIC_KEY !== 'YOUR_EMAILJS_PUBLIC_KEY') {
// Use a different template ID for contact forms
const contactTemplateId = 'YOUR_CONTACT_TEMPLATE_ID';
emailjs.send(EMAILJS_CONFIG.SERVICE_ID, contactTemplateId, emailData)
.then(function(response) {
console.log('Contact email sent successfully:', response);
showNotification('Message sent! We will get back to you soon.', 'success');
form.reset();
})
.catch(function(error) {
console.error('Contact email failed:', error);
showNotification('Failed to send message. Please try again or contact us directly.', 'error');
})
.finally(function() {
submitBtn.disabled = false;
submitBtn.textContent = originalText;
});
} else {
// Fallback simulation
setTimeout(() => {
console.log('Contact form data:', emailData);
showNotification('Message sent! We will get back to you soon. (Demo mode)', 'success');
form.reset();
submitBtn.disabled = false;
submitBtn.textContent = originalText;
}, 1000);
}
}
// Handle newsletter submission
function handleNewsletterSubmission(form) {
const formData = new FormData(form);
const email = formData.get('email');
if (!email) {
showNotification('Please enter your email address', 'warning');
return;
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showNotification('Please enter a valid email address', 'warning');
return;
}
// Show loading state
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = 'Subscribing...';
// Prepare newsletter email data
const emailData = {
subscriber_email: email,
subscribe_date: new Date().toLocaleString(),
to_email: 'newsletter@artisanpottery.com'
};
// Send email using EmailJS (you'll need a separate template for newsletter)
if (typeof emailjs !== 'undefined' && EMAILJS_CONFIG.PUBLIC_KEY !== 'YOUR_EMAILJS_PUBLIC_KEY') {
// Use a different template ID for newsletter subscriptions
const newsletterTemplateId = 'YOUR_NEWSLETTER_TEMPLATE_ID';
emailjs.send(EMAILJS_CONFIG.SERVICE_ID, newsletterTemplateId, emailData)
.then(function(response) {
console.log('Newsletter subscription sent successfully:', response);
showNotification('Thank you for subscribing to our newsletter!', 'success');
form.reset();
})
.catch(function(error) {
console.error('Newsletter subscription failed:', error);
showNotification('Failed to subscribe. Please try again.', 'error');
})
.finally(function() {
submitBtn.disabled = false;
submitBtn.textContent = originalText;
});
} else {
// Fallback simulation
setTimeout(() => {
console.log('Newsletter subscription:', emailData);
showNotification('Thank you for subscribing! (Demo mode)', 'success');
form.reset();
submitBtn.disabled = false;
submitBtn.textContent = originalText;
}, 1000);
}
}
// Loading animation for images (if we had real images)
function handleImageLoad() {
document.querySelectorAll('.product-image img').forEach(img => {
img.addEventListener('load', function() {
this.style.opacity = '1';
});
img.addEventListener('error', function() {
this.style.display = 'none';
this.parentElement.innerHTML = '<i class="fas fa-image"></i>';
});
});
}