PageSourceSearch

https://binderim.com/js/marketplace/basket-offer.kctt405bvq.js

js binderim.com collected 2026-09-24 14:40:47 UTC 12,659 bytes, 304 lines download raw bytes

1// Sepet — Teklif Gönder akışı
2// Satıcının "Teklif Gönder" butonuna tıklandığında modal açar,
3// kullanıcı tutar girer, %X minimum doğrulamasını uygular, sonra
4// /Marketplace/CreateOffer endpoint'ine POST eder.
5
6(function () {
7    'use strict';
8
9    // BUG #3 FIX: CSRF token okuyucu (message-widget.js deseniyle birebir)
10    function getAntiForgeryToken() {
11        return document.querySelector('input[name="__RequestVerificationToken"]')?.value || '';
12    }
13
14    function formatTry(amount) {
15        if (typeof amount !== 'number' || isNaN(amount)) return '0,00';
16        try {
17            return window.BinderimFormat.formatNumber(amount, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
18        } catch (e) {
19            return amount.toFixed(2);
20        }
21    }
22
23    function showToast(message, type) {
24        if (window.toastr) {
25            (window.toastr[type] || window.toastr.info)(message);
26        } else {
27            // eslint-disable-next-line no-alert
28            alert(message);
29        }
30    }
31
32    function getSellerCartTotalFromDom(sellerId) {
33        // Satıcı grubundaki tüm basket-item'ları toplar
34        var group = document.querySelector('.basket-seller-group[data-seller-id="' + cssEscape(sellerId) + '"]');
35        if (!group) return 0;
36
37        var items = group.querySelectorAll('.basket-item');
38        var total = 0;
39        items.forEach(function (item) {
40            var price = parseFloat(item.getAttribute('data-price') || '0');
41            var qty = parseInt(item.getAttribute('data-quantity') || '0', 10);
42            if (!isNaN(price) && !isNaN(qty)) {
43                total += price * qty;
44            }
45        });
46        return total;
47    }
48
49    function cssEscape(str) {
50        if (typeof CSS !== 'undefined' && CSS.escape) {
51            return CSS.escape(str);
52        }
53        return String(str).replace(/[^a-zA-Z0-9_-]/g, function (ch) {
54            return '\\' + ch;
55        });
56    }
57
58    var modalEl = document.getElementById('basketOfferModal');
59    if (!modalEl || typeof bootstrap === 'undefined' || !bootstrap.Modal) {
60        return;
61    }
62
63    // ---- i18n (Issue #1625 B) ----------------------------------------------------
64    // Kullaniciya gorunen metinler artik bu dosyada SABIT DEGIL: Basket.cshtml basketOfferModal
65    // uzerine data-i18n-* attribute'lari basar, sunucu onlari Basket.resx/Basket.en.resx'ten
66    // doldurur. Ayni desen basket.js (#1625 B)/destek.js (#1613). Attribute yoksa (or. onbellekten
67    // gelen eski HTML) bugunku TR metne duseriz -- davranis degismez.
68    function t(name, fallback) {
69        var value = modalEl.getAttribute('data-i18n-' + name);
70        return (value === null || value === '') ? fallback : value;
71    }
72    /** '{0}'/'{1}'/'{2}' yer tutucularini argumanlarla degistirir (wallet-payout.js'teki coklu-arg
73     * fmt() ile ayni sozlesme). */
74    // QA (#1625 B): innerHTML'e gomulen ceviri degeri escape edilir (basket.js /
75    // marketplace-redesign.js ile BIREBIR ayni; cift kacis yok, getAttribute decode eder).
76    function escapeHtml(value) {
77        return String(value == null ? '' : value)
78            .replace(/&/g, '&')
79            .replace(/</g, '&lt;')
80            .replace(/>/g, '&gt;')
81            .replace(/"/g, '&quot;')
82            .replace(/'/g, '&#39;');
83    }
84
85    function fmt(template) {
86        var args = Array.prototype.slice.call(arguments, 1);
87        return String(template == null ? '' : template).replace(/\{(\d+)\}/g, function (match, index) {
88            var v = args[Number(index)];
89            return v === undefined ? match : v;
90        });
91    }
92
93    var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
94
95    var elSellerName = document.getElementById('basketOfferSellerName');
96    var elCartTotal = document.getElementById('basketOfferCartTotal');
97    var elMinimum = document.getElementById('basketOfferMinimum');
98    var elInfoText = document.getElementById('basketOfferInfoText');
99    var elAmount = document.getElementById('basketOfferAmount');
100    var elCurrency = document.getElementById('basketOfferCurrency');
101    var elNote = document.getElementById('basketOfferNote');
102    var elError = document.getElementById('basketOfferError');
103    var elSubmit = document.getElementById('basketOfferSubmitBtn');
104
105    var state = {
106        sellerId: '',
107        sellerName: '',
108        currency: 'TRY',
109        cartTotal: 0,
110        minPercentage: 70,
111        minAmount: 0
112    };
113
114    function setError(message) {
115        if (!elError) return;
116        if (message) {
117            elError.textContent = message;
118            elError.classList.remove('d-none');
119        } else {
120            elError.textContent = '';
121            elError.classList.add('d-none');
122        }
123    }
124
125    function openOfferModal(button) {
126        if (button.getAttribute('data-has-pending') === 'true') {
127            showToast(t('offer-already-pending', 'Bu satıcıya zaten aktif bir teklifiniz var.'), 'warning');
128            return;
129        }
130        if (button.getAttribute('data-has-accepted') === 'true') {
131            showToast(t('offer-already-accepted', 'Bu satıcı için kabul edilmiş bir teklifiniz var. Sepetiniz indirimli görünüyor.'), 'info');
132            return;
133        }
134
135        state.sellerId = button.getAttribute('data-seller-id') || '';
136        state.sellerName = button.getAttribute('data-seller-name') || t('default-seller-name', 'Satıcı');
137        state.currency = button.getAttribute('data-currency') || 'TRY';
138        state.minPercentage = parseFloat(button.getAttribute('data-min-percentage') || '70');
139        if (isNaN(state.minPercentage) || state.minPercentage <= 0) {
140            state.minPercentage = 70;
141        }
142
143        state.cartTotal = getSellerCartTotalFromDom(state.sellerId);
144        state.minAmount = Math.round(state.cartTotal * (state.minPercentage / 100) * 100) / 100;
145
146        if (elSellerName) elSellerName.textContent = state.sellerName;
147        if (elCartTotal) elCartTotal.textContent = formatTry(state.cartTotal) + ' ' + state.currency;
148        if (elMinimum) elMinimum.textContent = formatTry(state.minAmount) + ' ' + state.currency;
149        if (elCurrency) elCurrency.textContent = state.currency;
150        if (elInfoText) {
151            var infoPercentage = state.minPercentage % 1 === 0 ? state.minPercentage.toFixed(0) : state.minPercentage;
152            elInfoText.textContent = fmt(t('offer-info-template', 'Sepet toplamının %{0}\'inin altında teklif gönderilemez. Satıcı kabul ederse indirim sepetteki kartlara orantılı uygulanır.'), infoPercentage);
153        }
154        if (elAmount) {
155            elAmount.value = '';
156            elAmount.min = state.minAmount;
157            elAmount.max = state.cartTotal;
158        }
159        if (elNote) elNote.value = '';
160        setError('');
161
162        modal.show();
163    }
164
165    function validateAmount(amount) {
166        if (typeof amount !== 'number' || isNaN(amount) || amount <= 0) {
167            return t('offer-amount-required', 'Lütfen geçerli bir teklif tutarı girin.');
168        }
169        if (amount < state.minAmount) {
170            var minPercentage = state.minPercentage % 1 === 0 ? state.minPercentage.toFixed(0) : state.minPercentage;
171            return fmt(t('offer-minimum-template', 'Teklif en az {0} {1} olmalıdır (sepetin %{2}\'i).'), formatTry(state.minAmount), state.currency, minPercentage);
172        }
173        if (amount >= state.cartTotal) {
174            return t('offer-below-total', 'Teklif sepetin toplamından düşük olmalıdır.');
175        }
176        return null;
177    }
178
179    async function submitOffer() {
180        if (!state.sellerId) {
181            return;
182        }
183
184        var amount = window.BinderimFormat.parseInput(elAmount);
185
186        var err = validateAmount(amount);
187        if (err) {
188            setError(err);
189            return;
190        }
191
192        setError('');
193        elSubmit.disabled = true;
194        var originalText = elSubmit.innerHTML;
195        elSubmit.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> ' + escapeHtml(t('offer-submitting-button', 'Gönderiliyor...'));
196
197        try {
198            var response = await fetch('/Marketplace/CreateOffer', {
199                method: 'POST',
200                headers: {
201                    'Content-Type': 'application/json',
202                    'RequestVerificationToken': getAntiForgeryToken()
203                },
204                body: JSON.stringify({
205                    sellerId: state.sellerId,
206                    amount: amount,
207                    note: elNote ? elNote.value : null
208                })
209            });
210            var data = await response.json();
211
212            if (data && data.success) {
213                modal.hide();
214                showToast(data.message || t('offer-submitted-fallback', 'Teklifiniz iletildi.'), 'success');
215                // Butonu güncelle
216                var btn = document.querySelector('.btn-offer-seller[data-seller-id="' + cssEscape(state.sellerId) + '"]');
217                if (btn) {
218                    btn.setAttribute('data-has-pending', 'true');
219                    btn.classList.add('is-pending');
220                    var span = btn.querySelector('span');
221                    if (span) span.textContent = t('offer-pending-label', 'Teklif Beklemede');
222                }
223            } else {
224                setError((data && data.message) || t('offer-submit-failed-fallback', 'Teklif gönderilemedi.'));
225            }
226        } catch (ex) {
227            setError(t('offer-submit-error-fallback', 'Teklif gönderilirken bir hata oluştu.'));
228        } finally {
229            elSubmit.disabled = false;
230            elSubmit.innerHTML = originalText;
231        }
232    }
233
234    async function withdrawOffer(offerId) {
235        if (!offerId) return;
236        // eslint-disable-next-line no-alert
237        if (!window.confirm(t('offer-withdraw-confirm', 'Teklifinizi geri çekmek istediğinize emin misiniz?'))) return;
238
239        try {
240            var response = await fetch('/Marketplace/WithdrawOffer', {
241                method: 'POST',
242                headers: {
243                    'Content-Type': 'application/json',
244                    'RequestVerificationToken': getAntiForgeryToken()
245                },
246                body: JSON.stringify({ offerId: Number(offerId) })
247            });
248            var data = await response.json();
249
250            if (data && data.success) {
251                showToast(data.message || t('offer-withdrawn-fallback', 'Teklifiniz geri çekildi.'), 'success');
252                // Teklif butonunu "Teklif Gönder" durumuna geri al
253                var offerBtn = document.querySelector('.btn-offer-seller[data-pending-offer-id="' + offerId + '"]');
254                if (offerBtn) {
255                    offerBtn.setAttribute('data-has-pending', 'false');
256                    offerBtn.setAttribute('data-pending-offer-id', '');
257                    offerBtn.classList.remove('is-pending');
258                    var span = offerBtn.querySelector('span');
259                    if (span) span.textContent = t('offer-send-label', 'Teklif Gönder');
260                }
261                // Geri çek butonunu gizle
262                var withdrawBtn = document.querySelector('.btn-withdraw-offer[data-pending-offer-id="' + offerId + '"]');
263                if (withdrawBtn) withdrawBtn.remove();
264            } else {
265                showToast((data && data.message) || t('offer-withdraw-failed-fallback', 'Teklif geri çekilemedi.'), 'error');
266            }
267        } catch (ex) {
268            showToast(t('offer-withdraw-error-fallback', 'Teklif geri çekilirken bir hata oluştu.'), 'error');
269        }
270    }
271
272    // Sayfa içindeki tüm Teklif Gönder butonları için ortak handler
273    document.addEventListener('click', function (event) {
274        // Geri çek butonu
275        var withdrawBtn = event.target.closest('.btn-withdraw-offer');
276        if (withdrawBtn) {
277            event.preventDefault();
278            var offerId = withdrawBtn.getAttribute('data-pending-offer-id');
279            if (offerId) withdrawOffer(offerId);
280            return;
281        }
282
283        var btn = event.target.closest('.btn-offer-seller');
284        if (!btn) return;
285        event.preventDefault();
286        openOfferModal(btn);
287    });
288
289    if (elSubmit) {
290        elSubmit.addEventListener('click', submitOffer);
291    }
292
293    if (elAmount) {
294        elAmount.addEventListener('input', function () {
295            setError('');
296        });
297        elAmount.addEventListener('keydown', function (e) {
298            if (e.key === 'Enter') {
299                e.preventDefault();
300                submitOffer();
301            }
302        });
303    }
304})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.