1259 lines
44 KiB
JavaScript
1259 lines
44 KiB
JavaScript
// 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>';
|
||
});
|
||
});
|
||
}
|