PageSourceSearch

https://proparcel.com/static/js/app/ui/welcome_modal.js

js proparcel.com collected 2026-10-02 01:40:12 UTC 23,813 bytes, 559 lines download raw bytes

1/**
2 * ProParcel İlk Üyelik Hoşgeldin Modalı
3 * ======================================
4 *
5 * İlk üyelik sonrası kullanıcıya Tepe Coin kullanım bilgisi gösteren modal.
6 * window.PP_USER_HAS_SEEN_WELCOME === false ise otomatik açılır.
7 * Kapatılınca POST /api/auth/dismiss-welcome/ çağrılır.
8 * Ardından "Bedava Tepe Coin Kazan" modalı açılır (8 sn zorunlu, accounts_giftcoinreward).
9 */
10
11(function () {
12  'use strict';
13
14  var MODAL_ID = 'pp-welcome-modal';
15  var STYLE_ID = 'pp-welcome-modal-css';
16  var COIN_EARN_MODAL_ID = 'pp-coin-earn-modal';
17  var COIN_EARN_STYLE_ID = 'pp-coin-earn-modal-css';
18  var COIN_EARN_MIN_SECONDS = 8;
19
20  /* ─── CSS Injection ─── */
21
22  function injectStyles() {
23    if (document.getElementById(STYLE_ID)) return;
24    var css = document.createElement('style');
25    css.id = STYLE_ID;
26    css.textContent = [
27      /* Overlay */
28      '#' + MODAL_ID + ' {',
29      '  position:fixed; inset:0; z-index:5000000;',
30      '  display:none; align-items:center; justify-content:center;',
31      '  background:rgba(15,23,42,.5); backdrop-filter:blur(6px);',
32      '  padding:20px;',
33      '}',
34      '#' + MODAL_ID + '.is-open { display:flex; }',
35
36      /* Card */
37      '.' + MODAL_ID + '__card {',
38      '  background:#fff; border-radius:18px; width:min(520px,95vw);',
39      '  max-height:90vh; overflow:hidden;',
40      '  box-shadow:0 24px 64px rgba(0,0,0,.22);',
41      '  animation: ppWelcomeFadeIn .25s ease;',
42      '  display:flex; flex-direction:column;',
43      '}',
44      '@keyframes ppWelcomeFadeIn { from{opacity:0;transform:scale(.95) translateY(10px)} to{opacity:1;transform:scale(1) translateY(0)} }',
45
46      /* Header - Turuncu */
47      '.' + MODAL_ID + '__header {',
48      '  background:linear-gradient(135deg,#f97316 0%,#ea580c 100%);',
49      '  padding:20px 24px;',
50      '  display:flex; align-items:center; gap:12px;',
51      '  position:relative;',
52      '}',
53      '.' + MODAL_ID + '__header-icon {',
54      '  width:44px; height:44px; border-radius:12px;',
55      '  background:rgba(255,255,255,.2);',
56      '  display:flex; align-items:center; justify-content:center;',
57      '  font-size:20px; color:#fff; flex-shrink:0;',
58      '}',
59      '.' + MODAL_ID + '__header-text {',
60      '  flex:1;',
61      '}',
62      '.' + MODAL_ID + '__header-title {',
63      '  font-size:20px; font-weight:800; color:#fff;',
64      '  letter-spacing:1px;',
65      '}',
66      '.' + MODAL_ID + '__header-subtitle {',
67      '  font-size:13px; color:rgba(255,255,255,.85); margin-top:2px;',
68      '}',
69      '.' + MODAL_ID + '__close-btn {',
70      '  position:absolute; top:12px; right:12px;',
71      '  width:32px; height:32px; border-radius:50%;',
72      '  background:rgba(255,255,255,.2); border:none; cursor:pointer;',
73      '  display:flex; align-items:center; justify-content:center;',
74      '  color:#fff; font-size:16px; transition:background .15s;',
75      '}',
76      '.' + MODAL_ID + '__close-btn:hover { background:rgba(255,255,255,.35); }',
77
78      /* Body */
79      '.' + MODAL_ID + '__body {',
80      '  padding:20px 24px; overflow-y:auto; flex:1;',
81      '}',
82
83      /* Warning text */
84      '.' + MODAL_ID + '__warning {',
85      '  background:#fff7ed; border:1px solid #fed7aa; border-radius:12px;',
86      '  padding:14px 16px; margin-bottom:18px;',
87      '  font-size:14px; color:#9a3412; line-height:1.6; font-weight:500;',
88      '  display:flex; align-items:flex-start; gap:10px;',
89      '}',
90      '.' + MODAL_ID + '__warning-icon {',
91      '  font-size:18px; color:#f97316; flex-shrink:0; margin-top:1px;',
92      '}',
93
94      /* Packages grid */
95      '.' + MODAL_ID + '__packages-title {',
96      '  font-size:13px; font-weight:700; color:#64748b;',
97      '  text-transform:uppercase; letter-spacing:.5px;',
98      '  margin-bottom:12px;',
99      '}',
100      '.' + MODAL_ID + '__grid {',
101      '  display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr));',
102      '  gap:10px; margin-bottom:18px;',
103      '}',
104      '.' + MODAL_ID + '__pkg {',
105      '  background:#f8fafc; border:1px solid #e2e8f0; border-radius:12px;',
106      '  padding:14px 16px; display:flex; align-items:center; gap:12px;',
107      '  transition:border-color .15s, box-shadow .15s;',
108      '}',
109      '.' + MODAL_ID + '__pkg:hover {',
110      '  border-color:#f97316; box-shadow:0 2px 8px rgba(249,115,22,.12);',
111      '}',
112      '.' + MODAL_ID + '__pkg-icon {',
113      '  width:40px; height:40px; border-radius:10px;',
114      '  background:linear-gradient(135deg,#eff6ff,#dbeafe);',
115      '  display:flex; align-items:center; justify-content:center;',
116      '  font-size:16px; color:#1e40af; flex-shrink:0;',
117      '}',
118      '.' + MODAL_ID + '__pkg-info { flex:1; min-width:0; }',
119      '.' + MODAL_ID + '__pkg-name {',
120      '  font-size:13px; font-weight:600; color:#1e293b;',
121      '  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;',
122      '}',
123      '.' + MODAL_ID + '__pkg-cost {',
124      '  font-size:12px; color:#f97316; font-weight:700; margin-top:2px;',
125      '}',
126
127      /* Footer */
128      '.' + MODAL_ID + '__footer {',
129      '  padding:16px 24px 20px; border-top:1px solid #f1f5f9;',
130      '  background:#fafafa;',
131      '}',
132      '.' + MODAL_ID + '__footer-text {',
133      '  font-size:13px; color:#64748b; line-height:1.5;',
134      '  text-align:center; margin-bottom:14px;',
135      '}',
136      '.' + MODAL_ID + '__btn {',
137      '  display:block; width:100%; padding:13px 24px;',
138      '  border-radius:12px; font-size:15px; font-weight:700;',
139      '  cursor:pointer; border:none; font-family:inherit;',
140      '  background:linear-gradient(135deg,#f97316 0%,#ea580c 100%);',
141      '  color:#fff; transition:all .15s ease; text-align:center;',
142      '}',
143      '.' + MODAL_ID + '__btn:hover { box-shadow:0 4px 16px rgba(249,115,22,.35); transform:translateY(-1px); }',
144
145      /* Loading skeleton */
146      '.' + MODAL_ID + '__loading {',
147      '  text-align:center; padding:30px 0; color:#94a3b8; font-size:13px;',
148      '}',
149
150      /* Responsive */
151      '@media (max-width:480px) {',
152      '  .' + MODAL_ID + '__grid { grid-template-columns:1fr; }',
153      '  .' + MODAL_ID + '__header { padding:16px 18px; }',
154      '  .' + MODAL_ID + '__body { padding:16px 18px; }',
155      '  .' + MODAL_ID + '__footer { padding:14px 18px 18px; }',
156      '}',
157      '',
158      '/* ─── Coin Earn Modal ─── */',
159      '#' + COIN_EARN_MODAL_ID + ' {',
160      '  position:fixed; inset:0; z-index:5000001;',
161      '  display:none; align-items:center; justify-content:center;',
162      '  background:rgba(15,23,42,.5); backdrop-filter:blur(6px);',
163      '  padding:20px;',
164      '}',
165      '#' + COIN_EARN_MODAL_ID + '.is-open { display:flex; }',
166      '.' + COIN_EARN_MODAL_ID + '__card {',
167      '  background:#fff; border-radius:18px; width:min(560px,95vw);',
168      '  max-height:90vh; overflow:hidden;',
169      '  box-shadow:0 24px 64px rgba(0,0,0,.22);',
170      '  animation: ppWelcomeFadeIn .25s ease;',
171      '  display:flex; flex-direction:column;',
172      '}',
173      '.' + COIN_EARN_MODAL_ID + '__header {',
174      '  background:linear-gradient(135deg,#16a34a 0%,#15803d 100%);',
175      '  padding:20px 24px;',
176      '  display:flex; align-items:center; gap:12px;',
177      '  position:relative;',
178      '}',
179      '.' + COIN_EARN_MODAL_ID + '__header-icon {',
180      '  width:44px; height:44px; border-radius:12px;',
181      '  background:rgba(255,255,255,.2);',
182      '  display:flex; align-items:center; justify-content:center;',
183      '  font-size:20px; color:#fff; flex-shrink:0;',
184      '}',
185      '.' + COIN_EARN_MODAL_ID + '__header-title { font-size:20px; font-weight:800; color:#fff; }',
186      '.' + COIN_EARN_MODAL_ID + '__close-btn {',
187      '  position:absolute; top:12px; right:12px;',
188      '  width:32px; height:32px; border-radius:50%;',
189      '  background:rgba(255,255,255,.2); border:none; cursor:pointer;',
190      '  display:flex; align-items:center; justify-content:center;',
191      '  color:#fff; font-size:16px; transition:background .15s;',
192      '}',
193      '.' + COIN_EARN_MODAL_ID + '__close-btn:hover { background:rgba(255,255,255,.35); }',
194      '.' + COIN_EARN_MODAL_ID + '__close-btn:disabled {',
195      '  opacity:0.6; cursor:not-allowed; pointer-events:none;',
196      '}',
197      '.' + COIN_EARN_MODAL_ID + '__body { padding:20px 24px; overflow-y:auto; flex:1; }',
198      '.' + COIN_EARN_MODAL_ID + '__subtitle {',
199      '  font-size:14px; color:#64748b; line-height:1.5; margin-bottom:18px; text-align:center;',
200      '}',
201      '.' + COIN_EARN_MODAL_ID + '__grid {',
202      '  display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr));',
203      '  gap:12px; margin-bottom:8px;',
204      '}',
205      '.' + COIN_EARN_MODAL_ID + '__card-item {',
206      '  background:#f8fafc; border:1px solid #e2e8f0; border-radius:12px;',
207      '  padding:16px; display:flex; align-items:flex-start; gap:12px;',
208      '  transition:border-color .15s, box-shadow .15s;',
209      '}',
210      '.' + COIN_EARN_MODAL_ID + '__card-item:hover {',
211      '  border-color:#16a34a; box-shadow:0 2px 8px rgba(22,163,74,.12);',
212      '}',
213      '.' + COIN_EARN_MODAL_ID + '__card-icon {',
214      '  width:40px; height:40px; border-radius:10px;',
215      '  background:linear-gradient(135deg,#dcfce7,#bbf7d0);',
216      '  display:flex; align-items:center; justify-content:center;',
217      '  font-size:16px; color:#15803d; flex-shrink:0;',
218      '}',
219      '.' + COIN_EARN_MODAL_ID + '__card-info { flex:1; min-width:0; }',
220      '.' + COIN_EARN_MODAL_ID + '__card-name {',
221      '  font-size:14px; font-weight:600; color:#1e293b; margin-bottom:4px;',
222      '}',
223      '.' + COIN_EARN_MODAL_ID + '__card-cost { font-size:13px; color:#16a34a; font-weight:700; }',
224      '.' + COIN_EARN_MODAL_ID + '__card-desc { font-size:12px; color:#64748b; margin-top:6px; line-height:1.4; }',
225      '.' + COIN_EARN_MODAL_ID + '__footer {',
226      '  padding:16px 24px 20px; border-top:1px solid #f1f5f9;',
227      '  background:#fafafa; display:flex; align-items:center; justify-content:space-between; gap:12px;',
228      '}',
229      '.' + COIN_EARN_MODAL_ID + '__countdown {',
230      '  font-size:13px; color:#64748b; font-weight:600;',
231      '}',
232      '.' + COIN_EARN_MODAL_ID + '__btn {',
233      '  padding:12px 24px; border-radius:12px; font-size:15px; font-weight:700;',
234      '  cursor:pointer; border:none; font-family:inherit;',
235      '  background:linear-gradient(135deg,#16a34a 0%,#15803d 100%);',
236      '  color:#fff; transition:all .15s ease;',
237      '}',
238      '.' + COIN_EARN_MODAL_ID + '__btn:hover:not(:disabled) { box-shadow:0 4px 16px rgba(22,163,74,.35); }',
239      '.' + COIN_EARN_MODAL_ID + '__btn:disabled { opacity:0.6; cursor:not-allowed; }',
240      '@media (max-width:480px) { .' + COIN_EARN_MODAL_ID + '__grid { grid-template-columns:1fr; } }'
241    ].join('\n');
242    document.head.appendChild(css);
243  }
244
245  /* ─── Modal DOM ─── */
246
247  function buildModal() {
248    injectStyles();
249    if (document.getElementById(MODAL_ID)) return;
250
251    var overlay = document.createElement('div');
252    overlay.id = MODAL_ID;
253    overlay.innerHTML = [
254      '<div class="' + MODAL_ID + '__card">',
255      '  <div class="' + MODAL_ID + '__header">',
256      '    <div class="' + MODAL_ID + '__header-icon"><i class="fas fa-exclamation-triangle"></i></div>',
257      '    <div class="' + MODAL_ID + '__header-text">',
258      '      <div class="' + MODAL_ID + '__header-title">DİKKAT</div>',
259      '      <div class="' + MODAL_ID + '__header-subtitle">İlk Üyelik Tepe Coin Bilgilendirmesi</div>',
260      '    </div>',
261      '    <button class="' + MODAL_ID + '__close-btn" id="pp-welcome-close-x"><i class="fas fa-times"></i></button>',
262      '  </div>',
263      '  <div class="' + MODAL_ID + '__body">',
264      '    <div class="' + MODAL_ID + '__warning">',
265      '      <span class="' + MODAL_ID + '__warning-icon"><i class="fas fa-info-circle"></i></span>',
266      '      <span>Lütfen ilk üyeliğinize özel ücretsiz <strong>Tepe Coin</strong>\'lerinizi dikkatli kullanın.</span>',
267      '    </div>',
268      '    <div class="' + MODAL_ID + '__packages-title">Kullanım Paketleri</div>',
269      '    <div class="' + MODAL_ID + '__grid" id="pp-welcome-packages">',
270      '      <div class="' + MODAL_ID + '__loading"><i class="fas fa-spinner fa-spin"></i> Yükleniyor...</div>',
271      '    </div>',
272      '  </div>',
273      '  <div class="' + MODAL_ID + '__footer">',
274      '    <div class="' + MODAL_ID + '__footer-text">Bunları değerlendirdikten sonra kalan coinlerinizi istediğiniz gibi kullanabilirsiniz.</div>',
275      '    <button class="' + MODAL_ID + '__btn" id="pp-welcome-close-btn">Anladım</button>',
276      '  </div>',
277      '</div>'
278    ].join('');
279
280    document.body.appendChild(overlay);
281
282    /* Event listeners */
283    document.getElementById('pp-welcome-close-btn').addEventListener('click', closeModal);
284    document.getElementById('pp-welcome-close-x').addEventListener('click', closeModal);
285    overlay.addEventListener('click', function (e) {
286      if (e.target === overlay) closeModal();
287    });
288    document.addEventListener('keydown', function (e) {
289      if (e.key === 'Escape' && overlay.classList.contains('is-open')) closeModal();
290    });
291  }
292
293  /* ─── Coin Earn Modal ─── */
294
295  var coinEarnCountdownInterval = null;
296  var coinEarnSecondsLeft = 0;
297
298  function buildCoinEarnModal() {
299    injectStyles();
300    if (document.getElementById(COIN_EARN_MODAL_ID)) return;
301
302    var overlay = document.createElement('div');
303    overlay.id = COIN_EARN_MODAL_ID;
304    overlay.innerHTML = [
305      '<div class="' + COIN_EARN_MODAL_ID + '__card">',
306      '  <div class="' + COIN_EARN_MODAL_ID + '__header">',
307      '    <div class="' + COIN_EARN_MODAL_ID + '__header-icon"><i class="fas fa-gift"></i></div>',
308      '    <div class="' + COIN_EARN_MODAL_ID + '__header-text">',
309      '      <div class="' + COIN_EARN_MODAL_ID + '__header-title">Bedava Tepe Coin Kazan</div>',
310      '    </div>',
311      '    <button class="' + COIN_EARN_MODAL_ID + '__close-btn" id="pp-coin-earn-close-x" disabled><i class="fas fa-times"></i></button>',
312      '  </div>',
313      '  <div class="' + COIN_EARN_MODAL_ID + '__body">',
314      '    <p class="' + COIN_EARN_MODAL_ID + '__subtitle">Programı ücretsiz kullanmaya devam etmek için coin kazanma yöntemlerini kullanabilirsiniz.</p>',
315      '    <div class="' + COIN_EARN_MODAL_ID + '__grid" id="pp-coin-earn-cards">',
316      '      <div class="' + MODAL_ID + '__loading"><i class="fas fa-spinner fa-spin"></i> Yükleniyor...</div>',
317      '    </div>',
318      '  </div>',
319      '  <div class="' + COIN_EARN_MODAL_ID + '__footer">',
320      '    <span class="' + COIN_EARN_MODAL_ID + '__countdown" id="pp-coin-earn-countdown">Kapatmak için 8 saniye bekleyin</span>',
321      '    <button class="' + COIN_EARN_MODAL_ID + '__btn" id="pp-coin-earn-close-btn" disabled>Kapat</button>',
322      '  </div>',
323      '</div>'
324    ].join('');
325
326    document.body.appendChild(overlay);
327
328    document.getElementById('pp-coin-earn-close-btn').addEventListener('click', closeCoinEarnModal);
329    document.getElementById('pp-coin-earn-close-x').addEventListener('click', closeCoinEarnModal);
330    overlay.addEventListener('click', function (e) {
331      if (e.target === overlay && coinEarnSecondsLeft <= 0) closeCoinEarnModal();
332    });
333    document.addEventListener('keydown', function coinEarnKeyHandler(e) {
334      if (e.key === 'Escape' && overlay.classList.contains('is-open') && coinEarnSecondsLeft <= 0) {
335        closeCoinEarnModal();
336      }
337    });
338  }
339
340  function loadGiftRewards() {
341    var container = document.getElementById('pp-coin-earn-cards');
342    if (!container) return;
343
344    fetch('/api/credit/gift-rewards/')
345      .then(function (r) { return r.json(); })
346      .then(function (data) {
347        var items = data.items || [];
348        renderGiftRewardCards(container, items);
349      })
350      .catch(function () {
351        container.innerHTML = '<div style="color:#94a3b8;font-size:13px;text-align:center;">Coin kazanma yöntemleri yüklenemedi.</div>';
352      });
353  }
354
355  function renderGiftRewardCards(container, items) {
356    if (!items.length) {
357      container.innerHTML = '<div style="color:#94a3b8;font-size:13px;">Henüz coin kazanma yöntemi tanımlanmamış.</div>';
358      return;
359    }
360    var html = items.map(function (item) {
361      var faClass = item.icon_fa || 'fa-gift';
362      var badge = item.is_coming_soon ? ' <span style="font-size:11px;color:#94a3b8;font-weight:500;">(Yakında)</span>' : '';
363      var cardId = (item.event_type && /^[a-z0-9_]+$/i.test(item.event_type)) ? item.event_type : '';
364      var idAttr = cardId ? ' id="' + escapeHtml(cardId) + '"' : '';
365      return [
366        '<div class="' + COIN_EARN_MODAL_ID + '__card-item"' + idAttr + '>',
367        '  <div class="' + COIN_EARN_MODAL_ID + '__card-icon"><i class="fas ' + faClass + '"></i></div>',
368        '  <div class="' + COIN_EARN_MODAL_ID + '__card-info">',
369        '    <div class="' + COIN_EARN_MODAL_ID + '__card-name">' + escapeHtml(item.display_name) + badge + '</div>',
370        '    <div class="' + COIN_EARN_MODAL_ID + '__card-cost">' + item.credits + ' Coin</div>',
371        (item.description ? '<div class="' + COIN_EARN_MODAL_ID + '__card-desc">' + escapeHtml(item.description) + '</div>' : ''),
372        '  </div>',
373        '</div>'
374      ].join('');
375    }).join('');
376    container.innerHTML = html;
377  }
378
379  function openCoinEarnModal() {
380    buildCoinEarnModal();
381    loadGiftRewards();
382    var el = document.getElementById(COIN_EARN_MODAL_ID);
383    if (el) el.classList.add('is-open');
384    document.body.style.overflow = 'hidden';
385
386    coinEarnSecondsLeft = COIN_EARN_MIN_SECONDS;
387    var countdownEl = document.getElementById('pp-coin-earn-countdown');
388    var closeBtn = document.getElementById('pp-coin-earn-close-btn');
389    var closeX = document.getElementById('pp-coin-earn-close-x');
390
391    function updateCountdown() {
392      if (coinEarnSecondsLeft > 0) {
393        countdownEl.textContent = 'Kapatmak için ' + coinEarnSecondsLeft + ' saniye bekleyin';
394        closeBtn.textContent = 'Kapat (' + coinEarnSecondsLeft + ')';
395        closeBtn.disabled = true;
396        closeX.disabled = true;
397        coinEarnSecondsLeft--;
398      } else {
399        clearInterval(coinEarnCountdownInterval);
400        coinEarnCountdownInterval = null;
401        countdownEl.textContent = '';
402        closeBtn.textContent = 'Kapat';
403        closeBtn.disabled = false;
404        closeX.disabled = false;
405      }
406    }
407
408    if (coinEarnCountdownInterval) clearInterval(coinEarnCountdownInterval);
409    updateCountdown();
410    coinEarnCountdownInterval = setInterval(updateCountdown, 1000);
411  }
412
413  function closeCoinEarnModal() {
414    if (coinEarnCountdownInterval) {
415      clearInterval(coinEarnCountdownInterval);
416      coinEarnCountdownInterval = null;
417    }
418    var el = document.getElementById(COIN_EARN_MODAL_ID);
419    if (el) el.classList.remove('is-open');
420    document.body.style.overflow = '';
421    try {
422      if (window.PPAppTourOverlay && typeof window.PPAppTourOverlay.maybeShow === 'function') {
423        setTimeout(function () { window.PPAppTourOverlay.maybeShow(); }, 300);
424      }
425    } catch (_) {}
426  }
427
428  /* ─── Load Packages ─── */
429
430  function loadPackages() {
431    var container = document.getElementById('pp-welcome-packages');
432    if (!container) return;
433
434    fetch('/api/credit/costs/')
435      .then(function (r) { return r.json(); })
436      .then(function (data) {
437        var items = data.items || [];
438        if (!items.length) {
439          /* API yanıtı: items boşsa costs/action_types objelerinden kart üret */
440          var costs = data.costs || {};
441          var names = data.action_types || {};
442          Object.keys(costs).forEach(function (key) {
443            items.push({
444              action_type: key,
445              display_name: names[key] || key,
446              credits: costs[key],
447              icon_fa: 'fa-circle',
448              description: ''
449            });
450          });
451        }
452        renderPackages(container, items);
453      })
454      .catch(function () {
455        container.innerHTML = '<div style="color:#94a3b8;font-size:13px;text-align:center;">Paket bilgileri yüklenemedi.</div>';
456      });
457  }
458
459  function renderPackages(container, items) {
460    if (!items.length) {
461      container.innerHTML = '<div style="color:#94a3b8;font-size:13px;">Henüz paket tanımlanmamış.</div>';
462      return;
463    }
464    var html = items.map(function (item) {
465      var faClass = item.icon_fa || 'fa-circle';
466      return [
467        '<div class="' + MODAL_ID + '__pkg">',
468        '  <div class="' + MODAL_ID + '__pkg-icon"><i class="fas ' + faClass + '"></i></div>',
469        '  <div class="' + MODAL_ID + '__pkg-info">',
470        '    <div class="' + MODAL_ID + '__pkg-name">' + escapeHtml(item.display_name) + '</div>',
471        '    <div class="' + MODAL_ID + '__pkg-cost">' + item.credits + ' Tepe Coin</div>',
472        '  </div>',
473        '</div>'
474      ].join('');
475    }).join('');
476    container.innerHTML = html;
477  }
478
479  function escapeHtml(str) {
480    var div = document.createElement('div');
481    div.textContent = str || '';
482    return div.innerHTML;
483  }
484
485  /* ─── Open / Close ─── */
486
487  function openModal() {
488    buildModal();
489    loadPackages();
490    var el = document.getElementById(MODAL_ID);
491    if (el) el.classList.add('is-open');
492    document.body.style.overflow = 'hidden';
493  }
494
495  function closeModal() {
496    var el = document.getElementById(MODAL_ID);
497    if (el) el.classList.remove('is-open');
498    dismissWelcome();
499    /* Hoşgeldin modalı kapandıktan sonra "Bedava Tepe Coin Kazan" modalını aç */
500    try {
501      openCoinEarnModal();
502    } catch (err) {
503      console.error('[PPWelcomeModal] Coin earn modal açılamadı:', err);
504      document.body.style.overflow = '';
505    }
506  }
507
508  function dismissWelcome() {
509    window.PP_USER_HAS_SEEN_WELCOME = true;
510
511    /* CSRF token al */
512    var csrfToken = '';
513    var csrfMeta = document.querySelector('meta[name="csrf-token"]');
514    if (csrfMeta) {
515      csrfToken = csrfMeta.getAttribute('content') || '';
516    }
517    if (!csrfToken) {
518      var csrfCookie = document.cookie.split(';').find(function (c) {
519        return c.trim().startsWith('csrftoken=');
520      });
521      if (csrfCookie) csrfToken = csrfCookie.split('=')[1] || '';
522    }
523
524    fetch('/api/auth/dismiss-welcome/', {
525      method: 'POST',
526      headers: {
527        'Content-Type': 'application/json',
528        'X-CSRFToken': csrfToken
529      },
530      credentials: 'same-origin'
531    }).catch(function () {
532      /* Sessiz hata - kritik değil */
533    });
534  }
535
536  /* ─── Auto-trigger ─── */
537
538  function maybeShowWelcome() {
539    if (!window.PP_USER_AUTHENTICATED) return;
540    if (window.PP_USER_HAS_SEEN_WELCOME === true) return;
541    /* Kısa gecikme ile göster (sayfa yüklenmesini engelleme) */
542    setTimeout(openModal, 800);
543  }
544
545  /* Public API */
546  window.PPWelcomeModal = {
547    open: openModal,
548    close: closeModal,
549    check: maybeShowWelcome,
550    openCoinEarn: openCoinEarnModal
551  };
552
553  /* Sayfa yüklendiğinde otomatik kontrol */
554  if (document.readyState === 'loading') {
555    document.addEventListener('DOMContentLoaded', maybeShowWelcome);
556  } else {
557    setTimeout(maybeShowWelcome, 400);
558  }
559})();

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.