Spaces:
Running
Running
| // PalletPal Pro - Main JavaScript | |
| // Product data | |
| const products = [ | |
| { | |
| id: 1, | |
| name: "Standard 48x40\" New", | |
| category: "new", | |
| price: 12.99, | |
| image: "http://static.photos/industry/640x480/1", | |
| description: "Industry standard GMA pallet, heat-treated hardwood construction.", | |
| specs: ["48\" x 40\" x 6\"", "2,500 lbs capacity", "ISPM-15 certified"] | |
| }, | |
| { | |
| id: 2, | |
| name: "Heavy Duty Block Pallet", | |
| category: "new", | |
| price: 24.99, | |
| image: "http://static.photos/industry/640x480/2", | |
| description: "Four-way entry block pallet for heavy loads and rack storage.", | |
| specs: ["48\" x 40\" x 6.5\"", "5,000 lbs capacity", "4-way entry"] | |
| }, | |
| { | |
| id: 3, | |
| name: "Premium Used Grade A", | |
| category: "used", | |
| price: 6.99, | |
| image: "http://static.photos/industry/640x480/3", | |
| description: "Top-quality refurbished pallets, repaired to like-new condition.", | |
| specs: ["48\" x 40\" x 6\"", "2,200 lbs capacity", "Heat treated"] | |
| }, | |
| { | |
| id: 4, | |
| name: "Export/Heat Treated", | |
| category: "export", | |
| price: 15.99, | |
| image: "http://static.photos/industry/640x480/4", | |
| description: "ISPM-15 compliant pallets for international shipping.", | |
| specs: ["48\" x 40\" x 6\"", "IPPC marked", "Global compliant"] | |
| }, | |
| { | |
| id: 5, | |
| name: "Custom Size Pallet", | |
| category: "custom", | |
| price: 35.00, | |
| image: "http://static.photos/craft/640x480/5", | |
| description: "Built to your exact specifications and requirements.", | |
| specs: ["Any size up to 96\"", "Custom capacity", "Your choice of wood"] | |
| }, | |
| { | |
| id: 6, | |
| name: "Lightweight Economy", | |
| category: "new", | |
| price: 8.99, | |
| image: "http://static.photos/industry/640x480/6", | |
| description: "Cost-effective solution for light to medium loads.", | |
| specs: ["48\" x 40\" x 5.5\"", "1,500 lbs capacity", "Softwood construction"] | |
| } | |
| ]; | |
| // Testimonials data | |
| const testimonials = [ | |
| { | |
| id: 1, | |
| name: "Robert Martinez", | |
| company: "FastTrack Logistics", | |
| role: "Operations Director", | |
| image: "http://static.photos/people/200x200/101", | |
| content: "PalletPal Pro has been our go-to supplier for 5 years. Their quality is consistent, delivery is always on time, and their team genuinely cares about our success.", | |
| rating: 5 | |
| }, | |
| { | |
| id: 2, | |
| name: "Jennifer Walsh", | |
| company: "Midwest Manufacturing", | |
| role: "Supply Chain Manager", | |
| image: "http://static.photos/people/200x200/102", | |
| content: "The B2B program saved us 30% on pallet costs while eliminating stockouts. Their just-in-time delivery model transformed our warehouse operations.", | |
| rating: 5 | |
| }, | |
| { | |
| id: 3, | |
| name: "David Kim", | |
| company: "GreenGrocer Distributors", | |
| role: "CEO", | |
| image: "http://static.photos/people/200x200/103", | |
| content: "As a company focused on sustainability, we love that PalletPal Pro shares our values. Their recycled pallet program aligns perfectly with our mission.", | |
| rating: 5 | |
| } | |
| ]; | |
| // Pricing configuration | |
| const pricing = { | |
| standard: { base: 12.99, multiplier: 1 }, | |
| heavy: { base: 24.99, multiplier: 1 }, | |
| custom: { base: 35.00, multiplier: 1.2 }, | |
| used: { base: 6.99, multiplier: 1 }, | |
| export: { base: 15.99, multiplier: 1 } | |
| }; | |
| const locationMultipliers = { | |
| local: 1, | |
| regional: 1.15, | |
| national: 1.35 | |
| }; | |
| // DOM Ready | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize Feather icons | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| // Load products on index page | |
| loadFeaturedProducts(); | |
| // Load all products on products page | |
| loadAllProducts(); | |
| // Load testimonials | |
| loadTestimonials(); | |
| // Setup quote calculator | |
| setupQuoteCalculator(); | |
| // Setup product filters | |
| setupProductFilters(); | |
| // Setup forms | |
| setupForms(); | |
| // Setup scroll animations | |
| setupScrollAnimations(); | |
| }); | |
| // Load featured products (homepage) | |
| function loadFeaturedProducts() { | |
| const grid = document.getElementById('product-grid'); | |
| if (!grid) return; | |
| const featuredProducts = products.slice(0, 3); | |
| grid.innerHTML = featuredProducts.map(product => createProductCard(product)).join(''); | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| } | |
| // Load all products (products page) | |
| function loadAllProducts() { | |
| const grid = document.getElementById('all-products-grid'); | |
| if (!grid) return; | |
| grid.innerHTML = products.map(product => createProductCard(product)).join(''); | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| } | |
| // Create product card HTML | |
| function createProductCard(product) { | |
| return ` | |
| <div class="bg-white dark:bg-wood-800 rounded-2xl overflow-hidden shadow-lg border border-wood-100 dark:border-wood-700 hover:shadow-2xl transition-all duration-300 group card-lift" data-category="${product.category}"> | |
| <div class="relative h-56 overflow-hidden img-zoom"> | |
| <img src="${product.image}" alt="${product.name}" class="w-full h-full object-cover"> | |
| <div class="absolute top-4 left-4"> | |
| <span class="px-3 py-1 bg-primary-600 text-white text-xs font-semibold rounded-full uppercase tracking-wide">${product.category}</span> | |
| </div> | |
| </div> | |
| <div class="p-6"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <h3 class="font-display text-xl font-bold">${product.name}</h3> | |
| <span class="text-2xl font-bold text-primary-600">${product.price.toFixed(2)}</span> | |
| </div> | |
| <p class="text-wood-600 dark:text-wood-400 text-sm mb-4">${product.description}</p> | |
| <ul class="space-y-2 mb-6"> | |
| ${product.specs.map(spec => ` | |
| <li class="flex items-center gap-2 text-sm text-wood-500 dark:text-wood-400"> | |
| <i data-feather="check" class="w-4 h-4 text-primary-500"></i> | |
| ${spec} | |
| </li> | |
| `).join('')} | |
| </ul> | |
| <a href="contact.html" class="w-full py-3 bg-wood-100 dark:bg-wood-700 hover:bg-primary-600 hover:text-white dark:hover:bg-primary-600 text-wood-800 dark:text-wood-100 rounded-xl font-semibold transition-all duration-300 flex items-center justify-center gap-2"> | |
| <i data-feather="shopping-cart" class="w-4 h-4"></i> | |
| Request Quote | |
| </a> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| // Load testimonials | |
| function loadTestimonials() { | |
| const grid = document.getElementById('testimonials-grid'); | |
| if (!grid) return; | |
| grid.innerHTML = testimonials.map(t => ` | |
| <div class="bg-white dark:bg-wood-800 rounded-2xl p-8 shadow-lg border border-wood-100 dark:border-wood-700"> | |
| <div class="flex gap-1 mb-4"> | |
| ${Array(t.rating).fill('<i data-feather="star" class="w-5 h-5 text-secondary-500 fill-current"></i>').join('')} | |
| </div> | |
| <p class="text-wood-600 dark:text-wood-300 mb-6 leading-relaxed">"${t.content}"</p> | |
| <div class="flex items-center gap-4"> | |
| <img src="${t.image}" alt="${t.name}" class="w-12 h-12 rounded-full object-cover"> | |
| <div> | |
| <div class="font-semibold">${t.name}</div> | |
| <div class="text-sm text-wood-500 dark:text-wood-400">${t.role}, ${t.company}</div> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| } | |
| // Setup quote calculator | |
| function setupQuoteCalculator() { | |
| const form = document.getElementById('quote-form'); | |
| const result = document.getElementById('quote-result'); | |
| const priceEl = document.getElementById('quote-price'); | |
| if (!form) return; | |
| form.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const type = document.getElementById('pallet-type').value; | |
| const quantity = parseInt(document.getElementById('quantity').value) || 1; | |
| const location = document.getElementById('location').value; | |
| const basePrice = pricing[type].base; | |
| const locationMult = locationMultipliers[location]; | |
| const volumeDiscount = quantity >= 500 ? 0.85 : quantity >= 100 ? 0.9 : 1; | |
| const total = basePrice * quantity * locationMult * volumeDiscount; | |
| priceEl.textContent = ' + total.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); | |
| result.classList.remove('hidden'); | |
| // Animate the price | |
| animateValue(priceEl, 0, total, 500); | |
| }); | |
| } | |
| // Animate number value | |
| function animateValue(element, start, end, duration) { | |
| const range = end - start; | |
| const increment = end > start ? 1 : -1; | |
| const stepTime = Math.abs(Math.floor(duration / (range / 100))); | |
| let current = start; | |
| const timer = setInterval(() => { | |
| current += range / 20; | |
| if ((increment > 0 && current >= end) || (increment < 0 && current <= end)) { | |
| current = end; | |
| clearInterval(timer); | |
| } | |
| element.textContent = ' + current.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); | |
| }, stepTime); | |
| } | |
| // Submit quote request | |
| function submitQuote() { | |
| const type = document.getElementById('pallet-type').value; | |
| const quantity = document.getElementById('quantity').value; | |
| // Store in sessionStorage for contact form | |
| sessionStorage.setItem('quoteType', type); | |
| sessionStorage.setItem('quoteQuantity', quantity); | |
| window.location.href = 'contact.html?subject=quote'; | |
| } | |
| // Setup product filters | |
| function setupProductFilters() { | |
| const buttons = document.querySelectorAll('.filter-btn'); | |
| const cards = document.querySelectorAll('[data-category]'); | |
| if (!buttons.length) return; | |
| buttons.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| // Update active state | |
| buttons.forEach(b => { | |
| b.classList.remove('active', 'bg-primary-600', 'text-white'); | |
| b.classList.add('bg-white', 'dark:bg-wood-800'); | |
| }); | |
| btn.classList.add('active', 'bg-primary-600', 'text-white'); | |
| btn.classList.remove('bg-white', 'dark:bg-wood-800'); | |
| // Filter cards | |
| const filter = btn.dataset.filter; | |
| cards.forEach(card => { | |
| if (filter === 'all' || card.dataset.category === filter) { | |
| card.style.display = 'block'; | |
| card.classList.add('animate-scale-in'); | |
| } else { | |
| card.style.display = 'none'; | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| // Setup forms | |
| function setupForms() { | |
| // Contact form | |
| const contactForm = document.getElementById('contact-form'); | |
| if (contactForm) { | |
| // Pre-fill if coming from quote | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const subject = urlParams.get('subject'); | |
| if (subject === 'quote') { | |
| const subjectSelect = contactForm.querySelector('select'); | |
| if (subjectSelect) subjectSelect.value = 'Request Quote'; | |
| } | |
| contactForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| showToast('Message sent successfully! We\'ll get back to you within 2 hours.'); | |
| this.reset(); | |
| }); | |
| } | |
| // B2B form | |
| const b2bForm = document.getElementById('b2b-form'); | |
| if (b2bForm) { | |
| b2bForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| showToast('Consultation request submitted! Our B2B team will contact you shortly.'); | |
| this.reset(); | |
| }); | |
| } | |
| } | |
| // Show toast notification | |
| function showToast(message) { | |
| // Remove existing toast | |
| const existing = document.querySelector('.toast'); | |
| if (existing) existing.remove(); | |
| const toast = document.createElement('div'); | |
| toast.className = 'toast'; | |
| toast.innerHTML = ` | |
| <div class="flex items-center gap-3"> | |
| <i data-feather="check-circle" class="w-5 h-5 text-primary-600"></i> | |
| <span>${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(toast); | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| setTimeout(() => toast.classList.add('show'), 10); | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| setTimeout(() => toast.remove(), 300); | |
| }, 4000); | |
| } | |
| // Setup scroll animations | |
| function setupScrollAnimations() { | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-slide-up'); | |
| observer.unobserve(entry.target); | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe sections for animation | |
| document.querySelectorAll('section > div').forEach(el => { | |
| el.style.opacity = '0'; | |
| el.style.transform = 'translateY(30px)'; | |
| observer.observe(el); | |
| }); | |
| } | |
| // Add CSS animation classes dynamically | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| .animate-slide-up { | |
| animation: slideUp 0.6s ease-out forwards; | |
| } | |
| @keyframes slideUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |