nodebb-plugin-niki-loyalty 1.5.0 → 1.5.1
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/library.js +57 -45
- package/niki-admin.txt +120 -143
- package/niki-kasa.txt +52 -22
- package/niki-prize.txt +200 -0
- package/niki-wallet.txt +415 -0
- package/package.json +1 -1
- package/static/lib/client.js +33 -16
- package/yeedek +313 -0
package/library.js
CHANGED
|
@@ -6,8 +6,12 @@ const routeHelpers = require.main.require('./src/controllers/helpers');
|
|
|
6
6
|
const nconf = require.main.require('nconf');
|
|
7
7
|
const socketHelpers = require.main.require('./src/socket.io/index');
|
|
8
8
|
const SocketPlugins = require.main.require('./src/socket.io/plugins');
|
|
9
|
+
const groups = require.main.require('./src/groups');
|
|
9
10
|
const Plugin = {};
|
|
10
11
|
|
|
12
|
+
// Ödül kullanabilecek gruplar
|
|
13
|
+
const WALLET_GROUPS = ['Premium', 'Lite', 'VIP'];
|
|
14
|
+
|
|
11
15
|
// =========================
|
|
12
16
|
// ⚙️ AYARLAR & KURALLAR (GAME LOGIC)
|
|
13
17
|
// =========================
|
|
@@ -293,33 +297,6 @@ Plugin.init = async function (params) {
|
|
|
293
297
|
const router = params.router;
|
|
294
298
|
const middleware = params.middleware;
|
|
295
299
|
|
|
296
|
-
// 🔄 1 SEFERLİK MİGRASYON: 180+ puanlı herkesten 50 puan düş
|
|
297
|
-
try {
|
|
298
|
-
const migrationKey = 'niki:migration:deduct50_from_180plus_v1';
|
|
299
|
-
const alreadyDone = await db.get(migrationKey);
|
|
300
|
-
if (!alreadyDone) {
|
|
301
|
-
console.log('[Niki-Loyalty] Migration başlıyor: 180+ puanlı kullanıcılardan 50 puan düşülecek...');
|
|
302
|
-
const allUids = await db.getSortedSetRange('users:joindate', 0, -1);
|
|
303
|
-
if (allUids && allUids.length > 0) {
|
|
304
|
-
const usersData = await user.getUsersFields(allUids, ['uid', 'niki_points', 'username']);
|
|
305
|
-
let affected = 0;
|
|
306
|
-
for (const u of usersData) {
|
|
307
|
-
const pts = parseFloat(u.niki_points || 0);
|
|
308
|
-
if (pts >= 180) {
|
|
309
|
-
await user.decrementUserFieldBy(u.uid, 'niki_points', 50);
|
|
310
|
-
await addUserLog(u.uid, 'admin_adjust', 50, 'Sistem düzenlemesi: -50 puan (dengeleme)');
|
|
311
|
-
affected++;
|
|
312
|
-
console.log(`[Niki-Loyalty] Migration: ${u.username} (UID:${u.uid}) ${pts} → ${pts - 50} puan`);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
console.log(`[Niki-Loyalty] Migration tamamlandı. Etkilenen kullanıcı: ${affected}`);
|
|
316
|
-
}
|
|
317
|
-
await db.set(migrationKey, Date.now().toString());
|
|
318
|
-
}
|
|
319
|
-
} catch (migErr) {
|
|
320
|
-
console.error('[Niki-Loyalty] Migration hatası:', migErr);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
300
|
// 1) HEARTBEAT (Artık "Okuma" Puanı veriyor)
|
|
324
301
|
// Client-side script her 30-60 saniyede bir bu adrese istek atmalıdır.
|
|
325
302
|
router.post('/api/niki-loyalty/heartbeat', middleware.ensureLoggedIn, async (req, res) => {
|
|
@@ -368,13 +345,18 @@ Plugin.init = async function (params) {
|
|
|
368
345
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
369
346
|
|
|
370
347
|
// Veritabanından verileri çek
|
|
371
|
-
const [userData, dailyData, actionCounts, historyRaw] = await Promise.all([
|
|
348
|
+
const [userData, dailyData, actionCounts, historyRaw, memberChecks] = await Promise.all([
|
|
372
349
|
user.getUserFields(uid, ['niki_points']),
|
|
373
350
|
db.getObject(`niki:daily:${uid}:${today}`),
|
|
374
|
-
db.getObject(`niki:daily:${uid}:${today}:counts`),
|
|
351
|
+
db.getObject(`niki:daily:${uid}:${today}:counts`),
|
|
375
352
|
db.getListRange(`niki:activity:${uid}`, 0, -1),
|
|
353
|
+
Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g))),
|
|
376
354
|
]);
|
|
377
355
|
|
|
356
|
+
// Kullanıcı WALLET_GROUPS'dan herhangi birinde mi?
|
|
357
|
+
const canRedeem = memberChecks.some(Boolean);
|
|
358
|
+
const userGroup = canRedeem ? WALLET_GROUPS[memberChecks.indexOf(true)] : null;
|
|
359
|
+
|
|
378
360
|
const dailyScore = parseFloat(dailyData?.score || 0);
|
|
379
361
|
let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
|
|
380
362
|
if (dailyPercent > 100) dailyPercent = 100;
|
|
@@ -386,9 +368,13 @@ Plugin.init = async function (params) {
|
|
|
386
368
|
dailyScore,
|
|
387
369
|
dailyCap: SETTINGS.dailyCap,
|
|
388
370
|
dailyPercent,
|
|
389
|
-
counts: actionCounts || {},
|
|
371
|
+
counts: actionCounts || {},
|
|
372
|
+
actions: ACTIONS,
|
|
390
373
|
history,
|
|
391
374
|
rewards: REWARDS,
|
|
375
|
+
canRedeem,
|
|
376
|
+
userGroup,
|
|
377
|
+
walletGroups: WALLET_GROUPS,
|
|
392
378
|
});
|
|
393
379
|
} catch (err) {
|
|
394
380
|
return res.status(500).json({ points: 0, history: [] });
|
|
@@ -508,43 +494,63 @@ Plugin.init = async function (params) {
|
|
|
508
494
|
router.post('/api/niki-loyalty/generate-qr', middleware.ensureLoggedIn, async (req, res) => {
|
|
509
495
|
try {
|
|
510
496
|
const uid = req.uid;
|
|
497
|
+
|
|
498
|
+
// Grup kontrolü
|
|
499
|
+
const memberChecks = await Promise.all(WALLET_GROUPS.map(g => groups.isMember(uid, g)));
|
|
500
|
+
if (!memberChecks.some(Boolean)) {
|
|
501
|
+
return res.json({ success: false, message: 'Ödül kullanmak için Premium, Lite veya VIP grubuna katılmalısın.' });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const rewardIndex = parseInt(req.body.rewardIndex, 10);
|
|
511
505
|
const points = parseFloat((await user.getUserField(uid, 'niki_points')) || 0);
|
|
512
|
-
const minCost = REWARDS[REWARDS.length - 1].cost; // En ucuz ödül
|
|
513
506
|
|
|
514
|
-
|
|
515
|
-
|
|
507
|
+
// Seçilen ödülü bul
|
|
508
|
+
if (isNaN(rewardIndex) || rewardIndex < 0 || rewardIndex >= REWARDS.length) {
|
|
509
|
+
return res.json({ success: false, message: 'Geçersiz ödül seçimi.' });
|
|
510
|
+
}
|
|
511
|
+
const selectedReward = REWARDS[rewardIndex];
|
|
512
|
+
|
|
513
|
+
if (!TEST_MODE_UNLIMITED && points < selectedReward.cost) {
|
|
514
|
+
return res.json({ success: false, message: `Yetersiz Puan. ${selectedReward.name} için ${selectedReward.cost} puan gerekli.` });
|
|
516
515
|
}
|
|
517
516
|
const token = crypto.randomBytes(16).toString('hex');
|
|
518
|
-
|
|
517
|
+
// Token'a kullanıcı ve seçilen ödül bilgisini kaydet
|
|
518
|
+
await db.setObject(`niki:qr:${token}`, { uid: String(uid), rewardIndex: String(rewardIndex) });
|
|
519
519
|
await db.expire(`niki:qr:${token}`, 120); // 2 dakika geçerli
|
|
520
|
-
return res.json({ success: true, token });
|
|
520
|
+
return res.json({ success: true, token, rewardName: selectedReward.name, rewardCost: selectedReward.cost });
|
|
521
521
|
} catch (e) { return res.status(500).json({ success: false }); }
|
|
522
522
|
});
|
|
523
523
|
|
|
524
524
|
// 5) QR TARATMA (Kasa İşlemi)
|
|
525
525
|
router.post('/api/niki-loyalty/scan-qr', middleware.ensureLoggedIn, async (req, res) => {
|
|
526
|
-
// ... (Mevcut kodunun aynısı)
|
|
527
526
|
try {
|
|
528
527
|
const token = req.body.token;
|
|
529
528
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
530
529
|
const isMod = await user.isGlobalModerator(req.uid);
|
|
531
530
|
if (!isAdmin && !isMod) return res.status(403).json({ success: false, message: 'Yetkisiz' });
|
|
532
531
|
|
|
533
|
-
const
|
|
534
|
-
if (!
|
|
532
|
+
const qrData = await db.getObject(`niki:qr:${token}`);
|
|
533
|
+
if (!qrData || !qrData.uid) return res.json({ success: false, message: 'Geçersiz Kod' });
|
|
535
534
|
|
|
535
|
+
const custUid = qrData.uid;
|
|
536
|
+
const rewardIndex = parseInt(qrData.rewardIndex, 10);
|
|
536
537
|
const pts = parseFloat(await user.getUserField(custUid, 'niki_points') || 0);
|
|
537
538
|
|
|
538
539
|
let selectedReward = null;
|
|
539
540
|
if (!TEST_MODE_UNLIMITED) {
|
|
540
|
-
|
|
541
|
-
|
|
541
|
+
// Token'daki ödül index'ini kullan
|
|
542
|
+
if (!isNaN(rewardIndex) && rewardIndex >= 0 && rewardIndex < REWARDS.length) {
|
|
543
|
+
selectedReward = REWARDS[rewardIndex];
|
|
544
|
+
} else {
|
|
545
|
+
// Fallback: en yüksek ödülü seç
|
|
546
|
+
for (const r of REWARDS) {
|
|
547
|
+
if (pts >= r.cost) { selectedReward = r; break; }
|
|
548
|
+
}
|
|
542
549
|
}
|
|
543
550
|
if (!selectedReward) return res.json({ success: false, message: 'Puan Yetersiz' });
|
|
544
551
|
} else { selectedReward = REWARDS[0]; }
|
|
545
552
|
|
|
546
553
|
if (!TEST_MODE_UNLIMITED) {
|
|
547
|
-
// Negatif bakiye kontrolü - önce kontrol, sonra düş
|
|
548
554
|
if (pts < selectedReward.cost) {
|
|
549
555
|
await db.delete(`niki:qr:${token}`);
|
|
550
556
|
return res.json({ success: false, message: 'Puan yetersiz, işlem iptal edildi.' });
|
|
@@ -650,15 +656,21 @@ Plugin.socketScanQR = async function (socket, data) {
|
|
|
650
656
|
const token = data.token;
|
|
651
657
|
if (!token) throw new Error('Geçersiz Token');
|
|
652
658
|
|
|
653
|
-
const
|
|
654
|
-
if (!
|
|
659
|
+
const qrData = await db.getObject(`niki:qr:${token}`);
|
|
660
|
+
if (!qrData || !qrData.uid) throw new Error('QR Kod Geçersiz veya Süresi Dolmuş');
|
|
655
661
|
|
|
662
|
+
const custUid = qrData.uid;
|
|
663
|
+
const rewardIndex = parseInt(qrData.rewardIndex, 10);
|
|
656
664
|
const pts = parseFloat((await user.getUserField(custUid, 'niki_points')) || 0);
|
|
657
665
|
|
|
658
666
|
let selectedReward = null;
|
|
659
667
|
if (!TEST_MODE_UNLIMITED) {
|
|
660
|
-
|
|
661
|
-
|
|
668
|
+
if (!isNaN(rewardIndex) && rewardIndex >= 0 && rewardIndex < REWARDS.length) {
|
|
669
|
+
selectedReward = REWARDS[rewardIndex];
|
|
670
|
+
} else {
|
|
671
|
+
for (const r of REWARDS) {
|
|
672
|
+
if (pts >= r.cost) { selectedReward = r; break; }
|
|
673
|
+
}
|
|
662
674
|
}
|
|
663
675
|
if (!selectedReward) throw new Error('Puan Yetersiz');
|
|
664
676
|
} else { selectedReward = REWARDS[0]; }
|
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
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1039
|
-
|
|
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 ?
|
|
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
|
|
1067
|
-
'<div class="detail-stat-lbl">
|
|
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-
|
|
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
|
-
|
|
1134
|
+
var data = window.currentDetailData;
|
|
1119
1135
|
if (!data) return;
|
|
1120
1136
|
|
|
1121
|
-
|
|
1122
|
-
|
|
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">' +
|
|
1159
|
-
'<div class="detail-history-time">' +
|
|
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 +
|
|
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
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
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
|
-
//
|
|
1213
|
-
console.log('[Niki-Admin] getStats
|
|
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
|
|
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
|
-
|
|
1243
|
-
|
|
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
|
-
|
|
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>');
|
|
1243
|
+
$container.html('<div style="text-align:center; padding:30px; color:#444;">Sonuç bulunamadı.</div>');
|
|
1244
|
+
return;
|
|
1256
1245
|
}
|
|
1257
|
-
|
|
1246
|
+
var rp = (typeof config !== 'undefined' && config.relative_path) ? config.relative_path : '';
|
|
1258
1247
|
|
|
1259
1248
|
$.each(list, function (i, u) {
|
|
1260
|
-
|
|
1249
|
+
var row = $('<div class="nk-row"></div>');
|
|
1261
1250
|
row.append('<div class="nk-idx">' + (i + 1) + '</div>');
|
|
1262
1251
|
|
|
1263
|
-
|
|
1264
|
-
if (u.picture) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
info.append('<div class="nk-letter-avatar" style="background:' +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
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
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
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>
|