nodebb-plugin-niki-loyalty 1.5.0 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/niki-admin.txt CHANGED
@@ -1,6 +1,3 @@
1
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
2
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/locale/tr.min.js"></script>
3
-
4
1
  <div class="niki-dashboard">
5
2
  <!-- Yükleniyor -->
6
3
  <div id="niki-loader" class="niki-loader">
@@ -968,22 +965,71 @@
968
965
 
969
966
  <script>
970
967
  (function () {
971
- let g_users = [];
972
- let g_targetUid = 0;
973
- moment.locale('tr');
968
+ // jQuery hazır olana kadar bekle
969
+ function waitAndInit() {
970
+ console.log('[Niki-Admin] waitAndInit çağrıldı, jQuery var mı:', typeof $ !== 'undefined', 'pathname:', window.location.pathname);
971
+ if (typeof $ === 'undefined' || typeof jQuery === 'undefined') {
972
+ console.log('[Niki-Admin] jQuery henüz yok, bekleniyor...');
973
+ setTimeout(waitAndInit, 100);
974
+ return;
975
+ }
976
+ // Sadece admin sayfasında çalış
977
+ if (window.location.pathname.indexOf('niki-admin') === -1) {
978
+ console.log('[Niki-Admin] Admin sayfasında değiliz, iptal.');
979
+ return;
980
+ }
981
+ if (!$('.niki-dashboard').length) {
982
+ console.log('[Niki-Admin] .niki-dashboard DOM bulunamadı, iptal.');
983
+ return;
984
+ }
985
+ console.log('[Niki-Admin] Tüm kontroller OK, başlatılıyor...');
986
+ initAdminModule();
987
+ }
988
+
989
+ function initAdminModule() {
990
+ console.log('[Niki-Admin] initAdminModule başladı');
991
+ // Moment.js'i dinamik yükle (CDN script tag'ları AJAX navigasyonda çalışmıyor)
992
+ function loadMoment(callback) {
993
+ if (typeof moment !== 'undefined') {
994
+ moment.locale('tr');
995
+ return callback();
996
+ }
997
+ var s = document.createElement('script');
998
+ s.src = 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment-with-locales.min.js';
999
+ s.onload = function () {
1000
+ moment.locale('tr');
1001
+ callback();
1002
+ };
1003
+ document.head.appendChild(s);
1004
+ }
1005
+
1006
+ var g_users = [];
1007
+ var g_targetUid = 0;
974
1008
 
975
- // JSON parse helper - string ise parse et, değilse aynen döndür
976
1009
  function safeParseMaybeJson(item) {
977
1010
  if (!item) return null;
978
1011
  if (typeof item === 'object') return item;
979
- try {
980
- return JSON.parse(item);
981
- } catch (e) {
982
- return null;
983
- }
1012
+ try { return JSON.parse(item); } catch (e) { return null; }
1013
+ }
1014
+
1015
+ function escapeHtml(text) {
1016
+ if (!text) return '';
1017
+ var div = document.createElement('div');
1018
+ div.textContent = text;
1019
+ return div.innerHTML;
1020
+ }
1021
+
1022
+ // Zaman formatlama - moment varsa kullan, yoksa basit format
1023
+ function formatTime(ts) {
1024
+ if (typeof moment !== 'undefined') return moment(ts).format('DD MMM YYYY, HH:mm');
1025
+ return new Date(ts).toLocaleString('tr-TR');
1026
+ }
1027
+
1028
+ function formatFromNow(ts) {
1029
+ if (typeof moment !== 'undefined') return moment(ts).fromNow();
1030
+ return new Date(ts).toLocaleDateString('tr-TR');
984
1031
  }
985
1032
 
986
- // Global functions
987
1033
  window.closeManageModal = function () { $('#modal-manage').fadeOut(200); };
988
1034
  window.closeDetailModal = function () { $('#modal-detail').fadeOut(200); };
989
1035
 
@@ -1001,12 +1047,10 @@
1001
1047
  $('#detail-body').html('<div class="niki-loader" style="padding:40px;"><div class="spinner"></div></div>');
1002
1048
 
1003
1049
  socket.emit('plugins.niki.getUserDetail', { uid: uid }, function (err, data) {
1004
- console.log('[Niki-Admin] getUserDetail response:', err, data);
1005
1050
  if (err) {
1006
1051
  $('#detail-body').html('<div class="detail-empty"><i class="fa fa-exclamation-circle"></i>Hata: ' + (err.message || 'Bilinmeyen hata') + '</div>');
1007
1052
  return;
1008
1053
  }
1009
- // Data string olarak gelebilir, parse et
1010
1054
  var parsedData = safeParseMaybeJson(data);
1011
1055
  if (!parsedData) {
1012
1056
  $('#detail-body').html('<div class="detail-empty"><i class="fa fa-exclamation-circle"></i>Veri parse edilemedi</div>');
@@ -1017,27 +1061,20 @@
1017
1061
  };
1018
1062
 
1019
1063
  function renderDetailModal(data) {
1020
- console.log('[Niki-Admin] renderDetailModal data:', data);
1021
-
1022
- // user ve stats da string olarak gelebilir
1023
1064
  var u = safeParseMaybeJson(data.user) || data.user || {};
1024
1065
  var stats = safeParseMaybeJson(data.stats) || data.stats || {};
1025
1066
  var rp = (typeof config !== 'undefined' && config.relative_path) ? config.relative_path : '';
1026
1067
 
1027
- console.log('[Niki-Admin] Parsed user:', u);
1028
- console.log('[Niki-Admin] Parsed stats:', stats);
1029
-
1030
- let avatarHtml = '';
1068
+ var avatarHtml = '';
1031
1069
  if (u.picture) {
1032
1070
  avatarHtml = '<img src="' + u.picture + '" class="detail-avatar">';
1033
1071
  } else {
1034
- const letter = u.username ? u.username[0].toUpperCase() : '?';
1072
+ var letter = u.username ? u.username[0].toUpperCase() : '?';
1035
1073
  avatarHtml = '<div class="detail-letter-avatar" style="background:' + (u.iconBg || '#555') + '">' + letter + '</div>';
1036
1074
  }
1037
1075
 
1038
- const dailyCap = stats.dailyCap || 35;
1039
- const dailyPercent = Math.min(100, ((stats.todayScore || 0) / dailyCap) * 100);
1040
-
1076
+ var dailyCap = stats.dailyCap || 35;
1077
+ var dailyPercent = Math.min(100, ((stats.todayScore || 0) / dailyCap) * 100);
1041
1078
  var kasaLength = (data.kasaHistory && Array.isArray(data.kasaHistory)) ? data.kasaHistory.length : 0;
1042
1079
  var todayScoreVal = stats.todayScore || 0;
1043
1080
  var totalEarnedVal = Math.floor(stats.totalEarned || 0).toLocaleString('tr-TR');
@@ -1045,7 +1082,7 @@
1045
1082
  var pointsVal = Math.floor(u.points || 0).toLocaleString('tr-TR');
1046
1083
  var usernameVal = escapeHtml(u.username || 'Bilinmeyen');
1047
1084
  var emailVal = escapeHtml(u.email) || 'E-posta yok';
1048
- var lastOnlineVal = u.lastonline ? moment(u.lastonline).fromNow() : 'Bilinmiyor';
1085
+ var lastOnlineVal = u.lastonline ? formatFromNow(u.lastonline) : 'Bilinmiyor';
1049
1086
  var dailyPercentFixed = dailyPercent.toFixed(0);
1050
1087
 
1051
1088
  var html = '<div class="detail-header">' +
@@ -1062,35 +1099,16 @@
1062
1099
  '</div>' +
1063
1100
 
1064
1101
  '<div class="detail-stats">' +
1065
- '<div class="detail-stat">' +
1066
- '<div class="detail-stat-val green">+' + totalEarnedVal + '</div>' +
1067
- '<div class="detail-stat-lbl">Kazanılan</div>' +
1068
- '</div>' +
1069
- '<div class="detail-stat">' +
1070
- '<div class="detail-stat-val red">-' + totalSpentVal + '</div>' +
1071
- '<div class="detail-stat-lbl">Harcanan</div>' +
1072
- '</div>' +
1073
- '<div class="detail-stat">' +
1074
- '<div class="detail-stat-val">' + todayScoreVal + '</div>' +
1075
- '<div class="detail-stat-lbl">Bugün</div>' +
1076
- '</div>' +
1077
- '<div class="detail-stat">' +
1078
- '<div class="detail-stat-val">' + kasaLength + '</div>' +
1079
- '<div class="detail-stat-lbl">Ödül Kullanımı</div>' +
1080
- '</div>' +
1102
+ '<div class="detail-stat"><div class="detail-stat-val green">+' + totalEarnedVal + '</div><div class="detail-stat-lbl">Kazanılan</div></div>' +
1103
+ '<div class="detail-stat"><div class="detail-stat-val red">-' + totalSpentVal + '</div><div class="detail-stat-lbl">Harcanan</div></div>' +
1104
+ '<div class="detail-stat"><div class="detail-stat-val">' + todayScoreVal + '</div><div class="detail-stat-lbl">Bugün</div></div>' +
1105
+ '<div class="detail-stat"><div class="detail-stat-val">' + kasaLength + '</div><div class="detail-stat-lbl">Ödül Kullanımı</div></div>' +
1081
1106
  '</div>' +
1082
1107
 
1083
1108
  '<div class="detail-section">' +
1084
1109
  '<div class="detail-section-title"><i class="fa fa-line-chart"></i> Günlük İlerleme</div>' +
1085
- '<div class="daily-progress">' +
1086
- '<div class="daily-progress-bar">' +
1087
- '<div class="daily-progress-fill" style="width: ' + dailyPercent + '%"></div>' +
1088
- '</div>' +
1089
- '<div class="daily-progress-label">' +
1090
- '<span>' + todayScoreVal + ' / ' + dailyCap + ' puan</span>' +
1091
- '<span>' + dailyPercentFixed + '%</span>' +
1092
- '</div>' +
1093
- '</div>' +
1110
+ '<div class="daily-progress"><div class="daily-progress-bar"><div class="daily-progress-fill" style="width: ' + dailyPercent + '%"></div></div>' +
1111
+ '<div class="daily-progress-label"><span>' + todayScoreVal + ' / ' + dailyCap + ' puan</span><span>' + dailyPercentFixed + '%</span></div></div>' +
1094
1112
  '</div>' +
1095
1113
 
1096
1114
  '<div class="detail-section">' +
@@ -1104,8 +1122,6 @@
1104
1122
  '</div>';
1105
1123
 
1106
1124
  $('#detail-body').html(html);
1107
-
1108
- // İlk sekmeyi göster
1109
1125
  window.currentDetailData = data;
1110
1126
  switchHistoryTab('earn');
1111
1127
  }
@@ -1115,11 +1131,11 @@
1115
1131
  if (el) $(el).addClass('active');
1116
1132
  else $('.detail-tab').first().addClass('active');
1117
1133
 
1118
- const data = window.currentDetailData;
1134
+ var data = window.currentDetailData;
1119
1135
  if (!data) return;
1120
1136
 
1121
- const container = $('#history-container');
1122
- let items = [];
1137
+ var container = $('#history-container');
1138
+ var items = [];
1123
1139
 
1124
1140
  if (type === 'earn') {
1125
1141
  items = (data.earnHistory || []).map(safeParseMaybeJson).filter(Boolean);
@@ -1127,12 +1143,7 @@
1127
1143
  items = (data.spendHistory || []).map(safeParseMaybeJson).filter(Boolean);
1128
1144
  } else if (type === 'kasa') {
1129
1145
  items = (data.kasaHistory || []).map(safeParseMaybeJson).filter(Boolean).map(function (k) {
1130
- return {
1131
- type: 'spend',
1132
- ts: k.ts,
1133
- amt: k.amt,
1134
- txt: k.reward || 'Ödül Kullanımı'
1135
- };
1146
+ return { type: 'spend', ts: k.ts, amt: k.amt, txt: k.reward || 'Ödül Kullanımı' };
1136
1147
  });
1137
1148
  }
1138
1149
 
@@ -1148,37 +1159,30 @@
1148
1159
  var icon = isEarn ? 'fa-arrow-up' : 'fa-arrow-down';
1149
1160
  var valClass = isEarn ? 'earn' : 'spend';
1150
1161
  var sign = isEarn ? '+' : '-';
1151
- var descText = escapeHtml(item.txt || 'İşlem');
1152
- var timeText = moment(item.ts).format('DD MMM YYYY, HH:mm');
1153
- var amtVal = item.amt || 0;
1154
1162
 
1155
1163
  html += '<div class="detail-history-item">' +
1156
1164
  '<div class="detail-history-icon ' + iconClass + '"><i class="fa ' + icon + '"></i></div>' +
1157
1165
  '<div class="detail-history-text">' +
1158
- '<div class="detail-history-desc">' + descText + '</div>' +
1159
- '<div class="detail-history-time">' + timeText + '</div>' +
1166
+ '<div class="detail-history-desc">' + escapeHtml(item.txt || 'İşlem') + '</div>' +
1167
+ '<div class="detail-history-time">' + formatTime(item.ts) + '</div>' +
1160
1168
  '</div>' +
1161
- '<div class="detail-history-val ' + valClass + '">' + sign + amtVal + '</div>' +
1169
+ '<div class="detail-history-val ' + valClass + '">' + sign + (item.amt || 0) + '</div>' +
1162
1170
  '</div>';
1163
1171
  });
1164
-
1165
1172
  container.html(html);
1166
1173
  };
1167
1174
 
1168
1175
  window.submitManagePoints = function () {
1169
- const amt = parseFloat($('#manage-amount').val());
1170
- const reason = $('#manage-reason').val().trim();
1171
- const action = $('input[name="manage_action"]:checked').val();
1172
- const $err = $('#manage-error');
1176
+ var amt = parseFloat($('#manage-amount').val());
1177
+ var reason = $('#manage-reason').val().trim();
1178
+ var action = $('input[name="manage_action"]:checked').val();
1179
+ var $err = $('#manage-error');
1173
1180
 
1174
1181
  if (!amt || amt <= 0) { $err.text('Geçerli bir miktar giriniz.').show(); return; }
1175
1182
  if (!reason) { $err.text('Sebep girmek zorunludur.').show(); return; }
1176
1183
 
1177
1184
  socket.emit('plugins.niki.managePoints', {
1178
- targetUid: g_targetUid,
1179
- amount: amt,
1180
- action: action,
1181
- reason: reason
1185
+ targetUid: g_targetUid, amount: amt, action: action, reason: reason
1182
1186
  }, function (err, result) {
1183
1187
  if (err) {
1184
1188
  $err.text(err.message || 'Hata oluştu.').show();
@@ -1190,29 +1194,16 @@
1190
1194
  });
1191
1195
  };
1192
1196
 
1193
- var _initRetries = 0;
1194
1197
  function initNikiAdmin() {
1195
- if (typeof socket === 'undefined' || typeof app === 'undefined' || typeof app.user === 'undefined') {
1196
- _initRetries++;
1197
- if (_initRetries > 25) {
1198
- console.error('[Niki-Admin] Socket/app 5 saniye içinde hazır olmadı, yükleme iptal.');
1199
- $('#niki-loader').hide();
1200
- $('.niki-dashboard').append('<div style="text-align:center; padding:40px; color:#ef5350;">Bağlantı kurulamadı. Sayfayı yenileyin.</div>');
1201
- return;
1202
- }
1203
- setTimeout(initNikiAdmin, 200);
1204
- return;
1205
- }
1206
- _initRetries = 0;
1207
-
1208
- const $loader = $('#niki-loader');
1209
- const $content = $('#niki-content');
1198
+ console.log('[Niki-Admin] initNikiAdmin başladı, socket var mı:', typeof socket !== 'undefined', 'app var mı:', typeof app !== 'undefined');
1199
+ var $loader = $('#niki-loader');
1200
+ var $content = $('#niki-content');
1210
1201
  $loader.show(); $content.hide();
1211
1202
 
1212
- // Önce istatistikleri al
1213
- console.log('[Niki-Admin] getStats emit ediliyor...');
1203
+ // İstatistikleri ve kullanıcıları paralel çek
1204
+ console.log('[Niki-Admin] getStats çağrılıyor...');
1214
1205
  socket.emit('plugins.niki.getStats', {}, function (err, stats) {
1215
- console.log('[Niki-Admin] getStats sonuç:', err, stats);
1206
+ console.log('[Niki-Admin] getStats sonuç:', err ? 'HATA: ' + err.message : 'OK', stats);
1216
1207
  if (!err && stats) {
1217
1208
  $('#val-users').text(stats.usersWithPoints || 0);
1218
1209
  $('#val-points').text(Number(stats.totalPoints || 0).toLocaleString('tr-TR'));
@@ -1223,87 +1214,73 @@
1223
1214
  }
1224
1215
  });
1225
1216
 
1226
- console.log('[Niki-Admin] getUsers emit ediliyor...');
1217
+ console.log('[Niki-Admin] getUsers çağrılıyor...');
1227
1218
  socket.emit('plugins.niki.getUsers', {}, function (err, users) {
1228
- console.log('[Niki-Admin] getUsers sonuç:', err, users);
1219
+ console.log('[Niki-Admin] getUsers sonuç:', err ? 'HATA: ' + err.message : 'OK, kullanıcı sayısı: ' + (users ? users.length : 0));
1229
1220
  $loader.hide();
1230
1221
  if (err) {
1231
- console.error('[Niki-Admin] getUsers HATA:', err);
1232
1222
  $('.niki-dashboard').html('<div style="color:#d32f2f; text-align:center; padding:40px;">HATA: ' + (err.message || 'Yetkisiz') + '</div>');
1233
1223
  return;
1234
1224
  }
1235
1225
  if (!users) users = [];
1236
- console.log('[Niki-Admin] ✅ ' + users.length + ' kullanıcı yüklendi');
1237
1226
  g_users = users;
1238
1227
  $content.fadeIn(300);
1239
1228
  renderList(users);
1240
1229
 
1241
1230
  $('#nk-search-input').off('keyup').on('keyup', function () {
1242
- const val = $(this).val().toLowerCase();
1243
- const filtered = users.filter(function (u) {
1231
+ var val = $(this).val().toLowerCase();
1232
+ renderList(users.filter(function (u) {
1244
1233
  return u.username.toLowerCase().indexOf(val) > -1;
1245
- });
1246
- renderList(filtered);
1234
+ }));
1247
1235
  });
1248
1236
  });
1249
1237
  }
1250
1238
 
1251
1239
  function renderList(list) {
1252
- const $container = $('#nk-list-body');
1240
+ var $container = $('#nk-list-body');
1253
1241
  $container.empty();
1254
1242
  if (list.length === 0) {
1255
- $container.html('<div style="text-align:center; padding:30px; color:#444;">Sonuç bulunamadı.</div>'); return;
1243
+ $container.html('<div style="text-align:center; padding:30px; color:#444;">Sonuç bulunamadı.</div>');
1244
+ return;
1256
1245
  }
1257
- const rp = config.relative_path || '';
1246
+ var rp = (typeof config !== 'undefined' && config.relative_path) ? config.relative_path : '';
1258
1247
 
1259
1248
  $.each(list, function (i, u) {
1260
- const row = $('<div class="nk-row"></div>');
1249
+ var row = $('<div class="nk-row"></div>');
1261
1250
  row.append('<div class="nk-idx">' + (i + 1) + '</div>');
1262
1251
 
1263
- const info = $('<div class="nk-user-info"></div>');
1264
- if (u.picture) { info.append('<img src="' + u.picture + '" class="nk-avatar">'); }
1265
- else {
1266
- const letter = (u.username && u.username[0]) ? u.username[0].toUpperCase() : '?';
1267
- const bg = u.iconBg || '#555';
1268
- info.append('<div class="nk-letter-avatar" style="background:' + bg + '">' + letter + '</div>');
1252
+ var info = $('<div class="nk-user-info"></div>');
1253
+ if (u.picture) {
1254
+ info.append('<img src="' + u.picture + '" class="nk-avatar">');
1255
+ } else {
1256
+ var letter = (u.username && u.username[0]) ? u.username[0].toUpperCase() : '?';
1257
+ info.append('<div class="nk-letter-avatar" style="background:' + (u.iconBg || '#555') + '">' + letter + '</div>');
1269
1258
  }
1270
- const link = $('<a class="nk-username" target="_blank"></a>').attr('href', rp + '/user/' + u.userslug).text(u.username);
1271
- info.append(link);
1259
+ info.append($('<a class="nk-username" target="_blank"></a>').attr('href', rp + '/user/' + u.userslug).text(u.username));
1272
1260
  row.append(info);
1273
1261
 
1274
- const pText = Math.floor(u.points || 0).toLocaleString('tr-TR');
1275
- row.append('<div class="nk-points">' + pText + '</div>');
1276
-
1277
- // Butonlar
1278
- const btnCol = $('<div class="nk-actions"></div>');
1262
+ row.append('<div class="nk-points">' + Math.floor(u.points || 0).toLocaleString('tr-TR') + '</div>');
1279
1263
 
1280
- // Detay Butonu
1281
- const btnDetail = $('<button class="nk-btn-action info" title="Detay"><i class="fa fa-eye"></i></button>');
1282
- btnDetail.click(function () { openDetailModal(u.uid); });
1283
- btnCol.append(btnDetail);
1284
-
1285
- // Düzenle Butonu
1286
- const btnManage = $('<button class="nk-btn-action" title="Puan Düzenle"><i class="fa fa-cog"></i></button>');
1287
- btnManage.click(function () { openManageModal(u.uid, u.username); });
1288
- btnCol.append(btnManage);
1264
+ var btnCol = $('<div class="nk-actions"></div>');
1265
+ $('<button class="nk-btn-action info" title="Detay"><i class="fa fa-eye"></i></button>')
1266
+ .click(function () { openDetailModal(u.uid); }).appendTo(btnCol);
1267
+ $('<button class="nk-btn-action" title="Puan Düzenle"><i class="fa fa-cog"></i></button>')
1268
+ .click(function () { openManageModal(u.uid, u.username); }).appendTo(btnCol);
1289
1269
 
1290
1270
  row.append(btnCol);
1291
1271
  $container.append(row);
1292
1272
  });
1293
1273
  }
1294
1274
 
1295
- function escapeHtml(text) {
1296
- if (!text) return '';
1297
- const div = document.createElement('div');
1298
- div.textContent = text;
1299
- return div.innerHTML;
1300
- }
1301
-
1302
- // Bu script widget HTML'i içinde olduğundan, çalıştığında DOM zaten hazır.
1303
- // action:ajaxify.end widget'lardan ÖNCE tetiklendiği için kullanılmamalı.
1304
- // Ayrıca her widget yüklemesinde yeni listener eklenmesi sorun yaratır.
1305
- if ($('.niki-dashboard').length) {
1275
+ // Hemen başlat - widget yüklendiğinde socket/app zaten hazır
1276
+ console.log('[Niki-Admin] Moment yükleniyor...');
1277
+ loadMoment(function () {
1278
+ console.log('[Niki-Admin] Moment hazır, initNikiAdmin çağrılıyor...');
1306
1279
  initNikiAdmin();
1307
- }
1280
+ });
1281
+ } // end initAdminModule
1282
+
1283
+ // Başlat
1284
+ waitAndInit();
1308
1285
  })();
1309
1286
  </script>
package/niki-kasa.txt CHANGED
@@ -1,7 +1,3 @@
1
- <script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>
2
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
3
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/locale/tr.min.js"></script>
4
-
5
1
  <style>
6
2
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap');
7
3
 
@@ -590,7 +586,7 @@
590
586
  <button class="btn-hist" id="btn-open-history" onclick="niki_openHistory()"><i class="fa fa-history"></i>
591
587
  İŞLEM GEÇMİŞİ</button>
592
588
  </div>
593
- <p style="color:#666; font-size:20px; margin-top:20px;">Eğer butonlar çalışmazsa sayfayı yenileyiniz.</p>
589
+ <p id="niki-loading-hint" style="color:#666; font-size:13px; margin-top:20px;"></p>
594
590
  </div>
595
591
 
596
592
  <div id="camera-ui" class="camera-ui">
@@ -647,40 +643,70 @@
647
643
  (function () {
648
644
  'use strict';
649
645
 
650
- // NOT: Sayfa ilk açıldığında zorla yenileme (reload) mantığı KALDIRILDI.
651
- // Kullanıcı geri bildirimine göre bu durum butonların çalışmamasına yol açıyordu.
646
+ // jQuery hazır olana kadar bekle
647
+ function waitAndInit() {
648
+ if (typeof $ === 'undefined' || typeof jQuery === 'undefined') {
649
+ setTimeout(waitAndInit, 100);
650
+ return;
651
+ }
652
+ // Sadece kasa sayfasında çalış
653
+ if (window.location.pathname.indexOf('niki-kasa') === -1) {
654
+ return;
655
+ }
656
+ if (!$('#niki-pos-root').length) {
657
+ return;
658
+ }
659
+ initKasaModule();
660
+ }
652
661
 
653
- // niki-kasa'ya SPA geçişini engelleyip tam yükleme (hard redirect) yapma mantığını da
654
- // kullanıcı "sonra hamburgerden basınca düzeliyor" dediği için şimdilik devre dışı bırakıyorum
655
- // veya sadece bu blok kalsın ama otomatik window.reload() olmasın.
662
+ function initKasaModule() {
663
+ // Kütüphaneleri dinamik yükle (CDN script tag'ları AJAX navigasyonda çalışmıyor)
664
+ function loadScript(url, check, callback) {
665
+ if (check()) return callback();
666
+ var s = document.createElement('script');
667
+ s.src = url;
668
+ s.onload = callback;
669
+ s.onerror = function () { console.error('[Niki-Kasa] Script yüklenemedi:', url); };
670
+ document.head.appendChild(s);
671
+ }
656
672
 
657
- // Eğer SPA geçişlerinde sorun olursa burası tekrar açılabilir:
658
- /*
659
- $(window).on('action:ajaxify.start', function (e, data) {
660
- if (data.url === 'niki-kasa') {
661
- // Gerekirse buraya hard redirect eklenebilir
662
- }
663
- });
664
- */
673
+ function loadDeps(callback) {
674
+ loadScript('https://unpkg.com/html5-qrcode', function () { return typeof Html5Qrcode !== 'undefined'; }, function () {
675
+ loadScript('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment-with-locales.min.js', function () { return typeof moment !== 'undefined'; }, function () {
676
+ moment.locale('tr');
677
+ callback();
678
+ });
679
+ });
680
+ }
665
681
 
666
682
  var html5QrCode = null;
667
683
  var isProcessing = false;
668
684
  var sessionTimer = null;
669
685
  var allHistoryData = [];
670
- moment.locale('tr');
686
+ var depsReady = false;
671
687
 
672
688
  // ------------------------------------------------------------------
673
689
  // GLOBAL FONKSİYONLAR (Inline OnClick için)
690
+ // Deps yüklenmeden çağrılırsa önce yükle sonra çalıştır
674
691
  // ------------------------------------------------------------------
675
- window.niki_initSession = initSession;
692
+ function withDeps(fn) {
693
+ return function () {
694
+ if (depsReady) return fn.apply(this, arguments);
695
+ loadDeps(function () { depsReady = true; fn(); });
696
+ };
697
+ }
698
+ window.niki_initSession = withDeps(initSession);
676
699
  window.niki_stopSession = stopSession;
677
700
  window.niki_resumeScan = resumeScan;
678
- window.niki_openHistory = openHistory;
701
+ window.niki_openHistory = withDeps(openHistory);
679
702
  window.niki_closeHistory = closeHistory;
680
703
  window.niki_applyHistFilter = applyHistFilter;
681
704
  window.niki_resetHistFilter = resetHistFilter;
682
705
  window.niki_exportHistCSV = exportHistCSV;
683
706
 
707
+ // Deps'leri sayfa açılır açılmaz arka planda yüklemeye başla
708
+ loadDeps(function () { depsReady = true; console.log('[Niki-Kasa] Kütüphaneler hazır.'); });
709
+
684
710
  function lockBody() { document.body.classList.add('niki-pos-lock'); }
685
711
  function unlockBody() { document.body.classList.remove('niki-pos-lock'); }
686
712
 
@@ -948,7 +974,7 @@
948
974
  if (!item || typeof item !== 'object') return;
949
975
 
950
976
  const cust = item.cust || 'Bilinmeyen';
951
- const ts = item.ts ? moment(item.ts).fromNow() : '—';
977
+ const ts = item.ts ? (typeof moment !== 'undefined' ? moment(item.ts).fromNow() : new Date(item.ts).toLocaleString('tr-TR')) : '—';
952
978
  const uid = item.cuid;
953
979
  const reward = item.reward || 'İşlem';
954
980
 
@@ -1002,5 +1028,9 @@
1002
1028
  closeHistory();
1003
1029
  });
1004
1030
 
1031
+ } // end initKasaModule
1032
+
1033
+ // Başlat
1034
+ waitAndInit();
1005
1035
  })();
1006
1036
  </script>