nodebb-plugin-niki-loyalty 1.3.2 → 1.3.4
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 +90 -15
- package/package.json +1 -1
- package/static/lib/client.js +1 -1
package/library.js
CHANGED
|
@@ -319,37 +319,112 @@ Plugin.init = async function (params) {
|
|
|
319
319
|
}
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
-
// 3) KASA HISTORY -
|
|
322
|
+
// 3) KASA HISTORY - GELİŞMİŞ VERSİYON (Filtre + İstatistik)
|
|
323
323
|
router.get('/api/niki-loyalty/kasa-history', middleware.ensureLoggedIn, async (req, res) => {
|
|
324
324
|
try {
|
|
325
325
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
326
326
|
const isMod = await user.isGlobalModerator(req.uid);
|
|
327
|
-
if (!isAdmin && !isMod) return res.status(403).json(
|
|
327
|
+
if (!isAdmin && !isMod) return res.status(403).json({ error: 'Yetkisiz' });
|
|
328
|
+
|
|
329
|
+
// Query parametreleri
|
|
330
|
+
const { startDate, endDate, search, rewardType, exportAll } = req.query;
|
|
328
331
|
|
|
329
332
|
const raw = await db.getListRange('niki:kasa:history', 0, -1);
|
|
330
|
-
|
|
333
|
+
let rows = (raw || []).map(safeParseMaybeJson).filter(Boolean).reverse();
|
|
334
|
+
|
|
335
|
+
// Personel bilgilerini de al
|
|
336
|
+
const staffUids = [...new Set(rows.map(r => parseInt(r.staff, 10)).filter(n => Number.isFinite(n) && n > 0))];
|
|
337
|
+
const custUids = rows.map(r => parseInt(r.cuid, 10)).filter(n => Number.isFinite(n) && n > 0);
|
|
338
|
+
const allUids = [...new Set([...staffUids, ...custUids])];
|
|
331
339
|
|
|
332
|
-
const
|
|
333
|
-
const users = await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'icon:bgColor']);
|
|
340
|
+
const usersData = await user.getUsersFields(allUids, ['uid', 'username', 'userslug', 'picture', 'icon:bgColor']);
|
|
334
341
|
const userMap = {};
|
|
335
|
-
(
|
|
342
|
+
(usersData || []).forEach(u => userMap[u.uid] = u);
|
|
336
343
|
|
|
337
344
|
const rp = nconf.get('relative_path') || '';
|
|
338
|
-
|
|
339
|
-
|
|
345
|
+
|
|
346
|
+
// Zenginleştir
|
|
347
|
+
let enriched = rows.map(r => {
|
|
348
|
+
const custUser = userMap[r.cuid] || {};
|
|
349
|
+
const staffUser = userMap[r.staff] || {};
|
|
340
350
|
return {
|
|
341
351
|
...r,
|
|
342
|
-
cust:
|
|
343
|
-
picture:
|
|
344
|
-
iconBg:
|
|
345
|
-
profileUrl:
|
|
346
|
-
reward: r.reward || 'İşlem'
|
|
352
|
+
cust: custUser.username || r.cust || 'Bilinmeyen',
|
|
353
|
+
picture: custUser.picture || '',
|
|
354
|
+
iconBg: custUser['icon:bgColor'] || '#4b5563',
|
|
355
|
+
profileUrl: custUser.userslug ? `${rp}/user/${custUser.userslug}` : '',
|
|
356
|
+
reward: r.reward || 'İşlem',
|
|
357
|
+
staffName: staffUser.username || 'Personel',
|
|
358
|
+
staffPicture: staffUser.picture || '',
|
|
359
|
+
date: new Date(r.ts).toISOString().slice(0, 10) // YYYY-MM-DD
|
|
347
360
|
};
|
|
348
361
|
});
|
|
349
|
-
|
|
362
|
+
|
|
363
|
+
// FİLTRELEME
|
|
364
|
+
// 1. Tarih aralığı
|
|
365
|
+
if (startDate) {
|
|
366
|
+
const start = new Date(startDate).getTime();
|
|
367
|
+
enriched = enriched.filter(r => r.ts >= start);
|
|
368
|
+
}
|
|
369
|
+
if (endDate) {
|
|
370
|
+
const end = new Date(endDate).getTime() + 86400000; // gün sonu
|
|
371
|
+
enriched = enriched.filter(r => r.ts < end);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 2. Arama (kullanıcı adı)
|
|
375
|
+
if (search && search.trim()) {
|
|
376
|
+
const q = search.toLowerCase().trim();
|
|
377
|
+
enriched = enriched.filter(r =>
|
|
378
|
+
(r.cust && r.cust.toLowerCase().includes(q)) ||
|
|
379
|
+
(r.staffName && r.staffName.toLowerCase().includes(q))
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 3. Ödül tipi
|
|
384
|
+
if (rewardType && rewardType !== 'all') {
|
|
385
|
+
enriched = enriched.filter(r => r.reward === rewardType);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// İSTATİSTİKLER
|
|
389
|
+
const stats = {
|
|
390
|
+
totalTransactions: enriched.length,
|
|
391
|
+
totalPoints: enriched.reduce((sum, r) => sum + (parseFloat(r.amt) || 0), 0),
|
|
392
|
+
byReward: {},
|
|
393
|
+
byStaff: {},
|
|
394
|
+
byDate: {}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
enriched.forEach(r => {
|
|
398
|
+
// Ödül bazında
|
|
399
|
+
stats.byReward[r.reward] = (stats.byReward[r.reward] || 0) + 1;
|
|
400
|
+
// Personel bazında
|
|
401
|
+
stats.byStaff[r.staffName] = (stats.byStaff[r.staffName] || 0) + 1;
|
|
402
|
+
// Gün bazında (son 7 gün için chart)
|
|
403
|
+
stats.byDate[r.date] = (stats.byDate[r.date] || 0) + 1;
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Benzersiz ödül tipleri (filter dropdown için)
|
|
407
|
+
const rewardTypes = [...new Set(rows.map(r => r.reward || 'İşlem'))];
|
|
408
|
+
|
|
409
|
+
// Export all için sayfalama yok
|
|
410
|
+
if (exportAll === 'true') {
|
|
411
|
+
return res.json({
|
|
412
|
+
data: enriched,
|
|
413
|
+
stats,
|
|
414
|
+
rewardTypes
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Normal görünüm (son 100 işlem)
|
|
419
|
+
return res.json({
|
|
420
|
+
data: enriched.slice(0, 100),
|
|
421
|
+
stats,
|
|
422
|
+
rewardTypes,
|
|
423
|
+
hasMore: enriched.length > 100
|
|
424
|
+
});
|
|
350
425
|
} catch (e) {
|
|
351
426
|
console.error('[Niki-Loyalty] Kasa history error:', e);
|
|
352
|
-
return res.status(500).json(
|
|
427
|
+
return res.status(500).json({ error: 'Sunucu hatası' });
|
|
353
428
|
}
|
|
354
429
|
});
|
|
355
430
|
|
package/package.json
CHANGED