// Cart/Index.cshtml için modern ve güvenli sepet scripti document.addEventListener('DOMContentLoaded', () => { if (!window.location.pathname.startsWith('/Cart')) return; // X-CSRF-TOKEN meta ve header ile fetch const csrfToken = document.querySelector('meta[name="X-CSRF-TOKEN"]')?.content; const format = v => new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(v); async function postJson(url, data) { const res = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken }, body: JSON.stringify(data) }); return res.ok ? res.json() : null; } // Adet azalt/çoğalt document.querySelectorAll('.btn-decr, .btn-incr').forEach(btn => { btn.addEventListener('click', async () => { const key = btn.dataset.key; const [id, varId] = key.split('-'); const qtyEl = document.getElementById(`qty-${key}`); const delta = btn.classList.contains('btn-incr') ? 1 : -1; const newQty = parseInt(qtyEl.value) + delta; if (newQty < 1) return; const json = await postJson('/Cart/ChangeQuantity', { UrunId: +id, VariationId: +varId, Adet: newQty }); if (!json?.success) { console.warn('Adet güncellenemedi:', json?.error); return; } qtyEl.value = json.newQty; document.getElementById(`line-total-${key}`).textContent = json.lineTotalText ?? format(json.lineTotal); if (json.cartTotal !== undefined) { document.getElementById('cart-total').textContent = json.cartTotalText ?? format(json.cartTotal); } if (window.updateCartCountUI) window.updateCartCountUI(json.itemCount); }); }); // Ürünü sil document.querySelectorAll('.btn-remove').forEach(btn => { btn.addEventListener('click', async () => { // Bu işlevi Cart/Index.cshtml'deki Bootstrap modal ile yönetiyoruz. Buradaki native confirm kaldırıldı. // Kod burada boş bırakıldı veya sadece modal tetikleniyor olabilir. // ... const key = btn.dataset.key; const [id, varId] = key.split('-'); const json = await postJson('/Cart/RemoveItem', { UrunId: +id, VariationId: (varId && +varId > 0) ? +varId : null }); if (!json?.success) { console.warn('Silinemedi:', json?.error); return; } document.getElementById(`row-${key}`)?.remove(); if (json.cartTotal !== undefined) { document.getElementById('cart-total').textContent = json.cartTotalText ?? format(json.cartTotal); } if (window.updateCartCountUI) window.updateCartCountUI(json.itemCount); }); }); }); // window.removeItem fonksiyonu kaldırıldı. Sepet silme işlemi sadece Cart/Index.cshtml içindeki event listener ile çalışacak. (function(){ function updateCartBadge() { fetch('/Cart/Count') .then(r => r.json()) .then(data => { const count = data.cartCount ?? data.itemCount ?? 0; const badge = document.querySelector('a.nav-link[href*="/Cart"] .badge'); if (badge) badge.textContent = count; }) .catch(console.error); } window.updateCartBadge = updateCartBadge; document.addEventListener('DOMContentLoaded', () => { updateCartBadge(); }); }()); // Toastr konumunu sola almak için ayar if (window.toastr) { toastr.options.positionClass = 'toast-top-center'; } // SignalR ile anlık bildirim (devre dışı bırakıldı — push gönderimi yerine e-posta kuyruğu kullanılacak) if (window.signalR) { // keep the client-side handlers for graceful re-enable, but do not start the connection // const connection = new signalR.HubConnectionBuilder().withUrl("/hubs/notifications").build(); // connection.on("ReceiveNotification", data => { toastr.info(data.message, data.title); }); // connection.start().catch(err => console.error(err)); console.info('SignalR client disabled by server-side configuration'); } // Bildirim dropdown tıklama davranışı $('.notif-item').on('click', function() { var id = $(this).data('id'); var $item = $(this).closest('li'); $.post('/Notifications/MarkAsRead', { id: id }, function(res) { if (res.success || res.ok) { $item.remove(); var $badge = $('.badge-notify'); var count = parseInt($badge.text() || '0') - 1; if (count > 0) { $badge.text(count); } else { $badge.remove(); } $('#allNotifBtn').show(); } }); }); // Modal ve ürün ekleme fonksiyonları const addCartModalEl = document.getElementById('addCartModal'); let currentProductId = null; window.showAddToCartModal = async function(productId, variantsJson) { // productId may be undefined for some legacy callers; pick a sensible fallback after we parse variants // If caller didn't include variants JSON, fetch from server let variantsRaw = []; try { variantsRaw = JSON.parse(variantsJson || '[]'); } catch(e) { variantsRaw = []; } try { console && console.debug && console.debug('[AddCartModal] .btn-add-cart clicked', { pid: productId, variantsJson: variantsJson }); } catch(e) {} if ((!variantsRaw || variantsRaw.length === 0) && productId) { try { console.debug('[AddCartModal] fetching variants for product', productId); const resp = await fetch(`/Urunler/GetVaryasyonlar?urunId=${encodeURIComponent(productId)}`, { credentials: 'same-origin' }); if (resp.ok) { variantsRaw = await resp.json(); console.debug('[AddCartModal] fetched variants', variantsRaw); } else { console.debug('[AddCartModal] failed to fetch variants', resp.status); } } catch (err) { console.debug('[AddCartModal] fetch error', err); } } // normalize keys (server may return camelCase or lowercase) const variants = (variantsRaw || []).map(v => ({ Id: v.Id ?? v.id ?? v.ID, Uzunluk: v.Uzunluk ?? v.uzunluk ?? v.Uzunluk ?? '', Olcu: v.Olcu ?? v.olcu ?? v.Olcu ?? '', Fiyat: (v.Fiyat ?? v.fiyat) ?? 0, BayiFiyat: (v.BayiFiyat ?? v.bayiFiyat) ?? 0 })); // determine an effective product id: prefer provided id, otherwise use first variant's Id const effectiveProductId = productId || (variants && variants.length ? (variants[0].Id ?? variants[0].id) : undefined); currentProductId = effectiveProductId; // if there are no variants and caller provided a product-level price in data attribute, use it try { if ((!variants || variants.length === 0) && productId) { var opener = document.querySelector('.btn-add-cart[data-product-id="' + productId + '"]'); if (opener && opener.getAttribute) { var pp = opener.getAttribute('data-product-price'); if (pp) { // show price in modal if variant price area exists try { var priceEl = document.getElementById('variantPriceInfo'); if (priceEl) { var n = parseFloat(pp); if (!isNaN(n) && n > 0) { priceEl.textContent = new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(n); priceEl.style.display = 'block'; } } } catch(e) {} } } } } catch(e) {} // keep a lookup map of variants for reliable price access during selection handlers if (!window._currentVariants) window._currentVariants = {}; try { window._currentVariants[String(effectiveProductId)] = variants; } catch(e) { window._currentVariants = window._currentVariants || {}; } console.debug('[AddCartModal] currentVariants for', effectiveProductId, window._currentVariants[String(effectiveProductId)]); // If there are no variants, we still show the modal (hidden variant group) so the user can // confirm quantity and see the product-level price; do not auto-submit from here. // Support two modal variants used in the app: // - Shared modal: #addCartModal with #cartVariant, #cartQuantity, #variantPriceInfo // - Legacy page modal: #adetModal with #modalVaryasyon, #modalAdet, #modalUnitPrice, #varyasyonFiyat const selGroup = document.getElementById('variantGroup'); const sel = document.getElementById('cartVariant'); const legacySel = document.getElementById('modalVaryasyon'); const legacyQty = document.getElementById('modalAdet'); const legacyUnitPrice = document.getElementById('modalUnitPrice'); const legacyVaryasyonFiyat = document.getElementById('varyasyonFiyat'); // populate shared select if present if (sel) { sel.innerHTML = ''; } // populate legacy select if present if (legacySel) { legacySel.innerHTML = ''; } if (variants.length) { variants.forEach(v => { const measure = (v.Uzunluk && v.Uzunluk.trim()) ? v.Uzunluk.trim() : (v.Olcu && v.Olcu.trim()) ? v.Olcu.trim() : '–'; const price = (v.BayiFiyat ?? v.Fiyat ?? 0); // Show only measure text in option; price will be shown below when user selects a variant const label = measure; if (sel) { const opt = document.createElement('option'); opt.value = v.Id; opt.text = label; opt.dataset.price = price; opt.dataset.fiyat = v.Fiyat ?? 0; opt.dataset.bayifiyat = v.BayiFiyat ?? 0; sel.append(opt); } if (legacySel) { const opt2 = document.createElement('option'); opt2.value = v.Id; opt2.text = (v.Uzunluk && v.Uzunluk.trim()) ? v.Uzunluk : (v.Olcu ?? 'Varyant'); opt2.dataset.fiyat = v.Fiyat ?? 0; opt2.dataset.bayifiyat = v.BayiFiyat ?? 0; legacySel.append(opt2); } }); if (selGroup && sel) selGroup.classList.remove('d-none'); if (legacySel) legacySel.disabled = false; } else { if (selGroup && sel) selGroup.classList.add('d-none'); if (legacySel) legacySel.disabled = true; } const priceInfoEl = document.getElementById('variantPriceInfo'); if (priceInfoEl) priceInfoEl.style.display = 'none'; // Her çağrıda modal instance'ı güncelle let modalInstance = window.addCartModal; if (!modalInstance || typeof modalInstance.show !== 'function') { modalInstance = bootstrap.Modal.getOrCreateInstance(addCartModalEl); window.addCartModal = modalInstance; } // Also initialize legacy modal instance if present const adagModalEl = document.getElementById('adetModal'); if (adagModalEl && window.bootstrap && bootstrap.Modal) { bootstrap.Modal.getOrCreateInstance(adagModalEl); } // store modal-scoped data so the confirm handler in the partial can read them try { // use jQuery if present for consistent `.data()` semantics used elsewhere if (window.jQuery && $(addCartModalEl)) { $(addCartModalEl).data('product-id', effectiveProductId); $(addCartModalEl).data('variants', variants); } else { // fallback to dataset (stringified) addCartModalEl.dataset.productId = effectiveProductId; addCartModalEl.dataset.variants = JSON.stringify(variants || []); } } catch (e) { console && console.debug && console.debug('[AddCartModal] set modal data error', e); } modalInstance.show(); try { console && console.debug && console.debug('[AddCartModal] shown for', effectiveProductId, { variantsLength: variants.length }); } catch(e) {} // set hidden modal inputs so the server receives UrunId even if client posts form-encoded try { const modalUrunIdEl = document.getElementById('modalUrunId'); if (modalUrunIdEl) modalUrunIdEl.value = effectiveProductId || ''; const modalVarEl = document.getElementById('modalVariationId'); if (modalVarEl) modalVarEl.value = ''; const modalOlcuHidden = document.getElementById('modalOlcuHidden'); if (modalOlcuHidden) modalOlcuHidden.value = ''; const modalUzunlukHidden = document.getElementById('modalUzunlukHidden'); if (modalUzunlukHidden) modalUzunlukHidden.value = ''; } catch(e) { console && console.debug && console.debug('[AddCartModal] set hidden inputs error', e); } // ensure currentProductId stored as string for lookups try { currentProductId = String(effectiveProductId || ''); addCartModalEl && (addCartModalEl.dataset.productId = String(effectiveProductId || '')); } catch(e) {} }; // Note: add-to-cart button handling is implemented in the modal partial (_CartModalAndOffcanvas.cshtml) // to avoid duplicate handlers and jQuery/vanilla conflicts. Do not add another global delegation here. // Confirm button handler is implemented inside the modal partial to keep all modal logic together. // Update displayed variant price when user changes selection inside modal const variantSelect = document.getElementById('cartVariant'); const variantPriceInfo = document.getElementById('variantPriceInfo'); if (variantSelect && variantPriceInfo) { variantSelect.addEventListener('change', async () => { const opt = variantSelect.options[variantSelect.selectedIndex]; if (!opt) { variantPriceInfo.style.display = 'none'; return; } const varId = opt.value; let displayPrice = 0; try { console && console.debug && console.debug('[AddCartModal] variant change', { currentProductId, varId, optDataset: opt.dataset }); } catch(e) {} // try lookup from stored variants first try { const map = window._currentVariants && window._currentVariants[String(currentProductId)]; if (map && map.length) { const found = map.find(v => String(v.Id) === String(varId)); if (found) { displayPrice = Number(found.BayiFiyat ?? found.Fiyat ?? 0) || 0; } } } catch (e) { /* ignore */ } // fallback to dataset if lookup failed if (!displayPrice) { const bayif = parseFloat(opt.dataset.bayifiyat || opt.dataset.price || 0) || 0; const fiyat = parseFloat(opt.dataset.fiyat || 0) || 0; displayPrice = (bayif > 0 ? bayif : fiyat) || 0; } // If still zero, ask server for variant's price (handles computed bayi price) if (!displayPrice && varId) { try { const resp = await fetch(`/Cart/VariantPrice?variationId=${encodeURIComponent(varId)}`, { credentials: 'same-origin' }); if (resp.ok) { const j = await resp.json(); // prefer display text if provided, otherwise numeric price if (j && (j.display || j.price !== undefined)) { if (j.display) { variantPriceInfo.textContent = j.display; variantPriceInfo.style.display = 'block'; return; } displayPrice = Number(j.price || 0) || 0; } } } catch (err) { console.debug('[AddCartModal] VariantPrice fetch error', err); } } if (displayPrice > 0) { variantPriceInfo.textContent = new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(displayPrice); variantPriceInfo.style.display = 'block'; variantPriceInfo.classList.add('text-success'); try { console && console.debug && console.debug('[AddCartModal] variant price resolved locally', { currentProductId, varId, displayPrice }); } catch(e) {} } else { variantPriceInfo.style.display = 'none'; variantPriceInfo.classList.remove('text-success'); try { console && console.debug && console.debug('[AddCartModal] variant price not found locally; varId, will try server if present', { currentProductId, varId }); } catch(e) {} } }); } // Legacy modal handlers removed: we rely on shared modal (#cartVariant) behavior only. // Confirm add-to-cart button handler (centralized here so partial no longer contains inline AJAX) try { // jQuery delegation if available if (window.jQuery) { $(document).on('click', '#confirmAddCart', async function (e) { e.preventDefault(); e.stopPropagation(); if (window._addToCartInFlight) { console && console.debug && console.debug('[AddCartModal] add in flight, ignoring click'); return; } window._addToCartInFlight = true; try { console && console.debug && console.debug('[AddCartModal] confirm clicked'); const modalEl = document.getElementById('addCartModal'); const $modal = $(modalEl); const variants = $modal.data('variants') || (modalEl && modalEl.dataset && modalEl.dataset.variants ? JSON.parse(modalEl.dataset.variants) : []) || []; const _productIdRaw = $modal.data('product-id') || (modalEl && modalEl.dataset && modalEl.dataset.productId) || document.getElementById('modalUrunId')?.value || null; const productId = (_productIdRaw !== null && _productIdRaw !== undefined && _productIdRaw !== '') ? (isNaN(Number(_productIdRaw)) ? null : Number(_productIdRaw)) : null; const varEl = document.getElementById('cartVariant'); const variationId = parseInt((varEl && varEl.value) || (document.getElementById('modalVariationId')?.value || '0')) || 0; const adet = parseInt(document.getElementById('cartQuantity')?.value || '1') || 1; if (variants && variants.length && variationId === 0) { alert('Lütfen bir uzunluk seçiniz.'); return; } const selOpt = varEl && varEl.selectedOptions && varEl.selectedOptions[0]; const uzun = selOpt ? (selOpt.textContent || selOpt.innerText || '').trim() : ''; try { document.getElementById('modalUrunId') && (document.getElementById('modalUrunId').value = productId || ''); } catch(e){} try { document.getElementById('modalVariationId') && (document.getElementById('modalVariationId').value = variationId || ''); } catch(e){} try { document.getElementById('modalUzunlukHidden') && (document.getElementById('modalUzunlukHidden').value = uzun || ''); } catch(e){} const payload = { VariationId: variationId || null, UrunId: productId || null, Adet: adet || 1, Olcu: null, Uzunluk: uzun || null }; const xsrf = document.querySelector('meta[name="X-CSRF-TOKEN"]')?.content || ''; const resp = await fetch('/Cart/AddToCart', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': xsrf }, body: JSON.stringify(payload) }); if (!resp.ok) { const txt = await resp.text(); let parsed = null; try { parsed = JSON.parse(txt); } catch(e) {} throw new Error(parsed?.error || parsed?.message || txt || ('HTTP ' + resp.status)); } const j = await resp.json(); if (j && j.success) { try { if (j.itemCount !== undefined) { $('.cart-count').text(j.itemCount); if (window.updateCartCountUI) window.updateCartCountUI(j.itemCount); } } catch(e){} try { var bs = bootstrap.Modal.getInstance(document.getElementById('addCartModal')) || bootstrap.Modal.getOrCreateInstance(document.getElementById('addCartModal')); if (bs) bs.hide(); bootstrap.Modal.getOrCreateInstance(document.getElementById('addedSuccessModal')).show(); } catch(e){} try { if (typeof variantPriceInfo !== 'undefined' && variantPriceInfo) variantPriceInfo.classList.add('text-success'); } catch(e){} } else { alert((j && (j.error || j.message)) || 'Sepete eklenemedi.'); } } catch (err) { console && console.error && console.error('[AddCartModal] add error', err); alert(err && err.message ? err.message : 'Sepete ekleme başarısız.'); } finally { window._addToCartInFlight = false; } }); } // Native fallback if jQuery isn't available or delegation failed // Use a single-flight guard to avoid double submissions when both capture and bubble handlers exist. try { document.addEventListener('click', function (e) { try { var btn = e.target && (e.target.closest ? e.target.closest('#confirmAddCart') : null); if (!btn) return; // prevent other handlers from running for this click e.preventDefault(); e.stopPropagation(); // If jQuery exists, do not trigger it — run native flow to avoid double-calls (async function(){ try { if (window._addToCartInFlight) { console && console.debug && console.debug('[AddCartModal] add in flight, ignoring native click'); return; } window._addToCartInFlight = true; console && console.debug && console.debug('[AddCartModal][native] confirm clicked'); const modalEl = document.getElementById('addCartModal'); const variants = modalEl && modalEl.dataset && modalEl.dataset.variants ? JSON.parse(modalEl.dataset.variants) : (window._currentVariants && window._currentVariants[String(modalEl && modalEl.dataset && modalEl.dataset.productId)]) || []; const _productIdRaw = (modalEl && modalEl.dataset && modalEl.dataset.productId) || document.getElementById('modalUrunId')?.value || null; const productId = (_productIdRaw !== null && _productIdRaw !== undefined && _productIdRaw !== '') ? (isNaN(Number(_productIdRaw)) ? null : Number(_productIdRaw)) : null; const varEl = document.getElementById('cartVariant'); const variationId = parseInt((varEl && varEl.value) || (document.getElementById('modalVariationId')?.value || '0')) || 0; const adet = parseInt(document.getElementById('cartQuantity')?.value || '1') || 1; if (variants && variants.length && variationId === 0) { alert('Lütfen bir uzunluk seçiniz.'); window._addToCartInFlight = false; return; } const selOpt = varEl && varEl.selectedOptions && varEl.selectedOptions[0]; const uzun = selOpt ? (selOpt.textContent || selOpt.innerText || '').trim() : ''; try { document.getElementById('modalUrunId') && (document.getElementById('modalUrunId').value = productId || ''); } catch(e){} try { document.getElementById('modalVariationId') && (document.getElementById('modalVariationId').value = variationId || ''); } catch(e){} try { document.getElementById('modalUzunlukHidden') && (document.getElementById('modalUzunlukHidden').value = uzun || ''); } catch(e){} const payload = { VariationId: variationId || null, UrunId: productId || null, Adet: adet || 1, Olcu: null, Uzunluk: uzun || null }; const xsrf = document.querySelector('meta[name="X-CSRF-TOKEN"]')?.content || ''; const resp = await fetch('/Cart/AddToCart', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': xsrf }, body: JSON.stringify(payload) }); if (!resp.ok) { const txt = await resp.text(); let parsed=null; try{parsed=JSON.parse(txt);}catch{} throw new Error(parsed?.error||parsed?.message||txt||('HTTP '+resp.status)); } const j = await resp.json(); if (j && j.success) { try { var bs = bootstrap.Modal.getInstance(document.getElementById('addCartModal')) || bootstrap.Modal.getOrCreateInstance(document.getElementById('addCartModal')); if (bs) bs.hide(); bootstrap.Modal.getOrCreateInstance(document.getElementById('addedSuccessModal')).show(); } catch(e){} try { document.querySelectorAll('.cart-count').forEach(el=>el.textContent = j.itemCount); if (window.updateCartCountUI) window.updateCartCountUI(j.itemCount); } catch(e){} } else { alert((j && (j.error||j.message)) || 'Sepete eklenemedi.'); } } catch(err) { console && console.error && console.error('[AddCartModal][native] add error', err); alert(err && err.message ? err.message : 'Sepete ekleme başarısız.'); } finally { window._addToCartInFlight = false; } })(); } catch(inner) { /* swallow */ } }, true); } catch(e) { /* ignore */ } } catch(e) { /* ignore if modal elements not present */ } // Delegate click on .btn-add-cart to open the shared add-to-cart modal. // Some pages (like Views/Urunler/Detay.cshtml) render a simple button with data attributes // but don't call showAddToCartModal directly. This ensures the modal opens and modal // gets the correct product-id / variants data for the confirm handler. try { $(document).on('click', '.btn-add-cart', function (e) { e.preventDefault(); var $btn = $(this); var pid = $btn.data('product-id') || $btn.attr('data-product-id') || null; var variantsAttr = $btn.data('variants') || $btn.attr('data-variants') || null; // if variantsAttr is a string, pass it through; if it's an object/array, stringify var variantsJson = null; if (variantsAttr) { if (typeof variantsAttr === 'string') variantsJson = variantsAttr; else variantsJson = JSON.stringify(variantsAttr); } // call the modal opener which will populate selects and show the modal try { window.showAddToCartModal(pid, variantsJson); } catch (err) { console && console.error && console.error('[AddCartModal] opener error', err); } // also ensure the modal element has data set (some confirm handlers read from it) try { var $m = $('#addCartModal'); if ($m && $m.length) { $m.data('product-id', pid); if (variantsAttr) { var parsed = null; try { parsed = typeof variantsAttr === 'string' ? JSON.parse(variantsAttr) : variantsAttr; } catch(e) { parsed = variantsAttr; } $m.data('variants', parsed || []); } } } catch(e) {} // If the opener sits inside a form that posts to /Cart/AddToCart (detay form), sync the form's hidden inputs too try { var $form = $btn.closest('form'); if ($form && $form.length && $form.attr('action') && $form.attr('action').toLowerCase().indexOf('/cart/addtocart') !== -1) { // Set inputs by id (created in Detay.cshtml) or by name fallback var setInput = function(selectorOrName, value){ var $el = $form.find(selectorOrName); if (!$el || !$el.length) $el = $form.find('[name="' + selectorOrName.replace(/^#/, '') + '"]'); if ($el && $el.length) $el.val(value == null ? '' : String(value)); }; setInput('#detayUrunId', pid || ''); setInput('#detayVariationId', '0'); setInput('#detayAdet', '1'); setInput('#detayOlcu', ''); setInput('#detayUzunluk', ''); } } catch(e) {} }); } catch(e) { /* ignore if jQuery not available at this point */ } // Native delegation fallback: ensure modal opens even if jQuery binding failed or jQuery errored. try { document.addEventListener('click', function (e) { try { var tgt = e.target || null; if (!tgt) return; var btn = (tgt.closest && tgt.closest('.btn-add-cart')) ? tgt.closest('.btn-add-cart') : null; if (!btn) return; // Avoid double-handling if jQuery already handled it and called preventDefault // We'll still call showAddToCartModal to be safe. var pid = btn.getAttribute('data-product-id') || (btn.dataset && btn.dataset.productId) || null; var variants = btn.getAttribute('data-variants') || (btn.dataset && btn.dataset.variants) || null; try { console && console.debug && console.debug('[AddCartModal][native-delegate] click', { pid: pid, variants: variants }); } catch(e) {} try { if (typeof window.showAddToCartModal === 'function') { window.showAddToCartModal(pid, variants); } else { console && console.debug && console.debug('[AddCartModal][native-delegate] showAddToCartModal not defined'); } } catch(err) { console && console.error && console.error('[AddCartModal][native-delegate] opener error', err); } } catch(inner) { /* swallow */ } }, true); } catch(e) { /* ignore */ } // Tekil badge güncelleme kurgusu // ...existing code...