nodebb-plugin-niki-loyalty 1.3.2 → 1.3.3
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/static/widgets/niki-admin.tpl +686 -0
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
package/static/lib/client.js
CHANGED
|
@@ -0,0 +1,686 @@
|
|
|
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
|
+
<div class="niki-dashboard">
|
|
5
|
+
<!-- Yükleniyor -->
|
|
6
|
+
<div id="niki-loader" class="niki-loader">
|
|
7
|
+
<div class="spinner"></div>
|
|
8
|
+
<div class="loading-text">Veriler Yükleniyor...</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<!-- Ana İçerik -->
|
|
12
|
+
<div id="niki-content" style="display:none;">
|
|
13
|
+
|
|
14
|
+
<!-- Header -->
|
|
15
|
+
<div class="nk-header">
|
|
16
|
+
<div class="nk-header-icon"><i class="fa fa-shield"></i></div>
|
|
17
|
+
<div class="nk-header-text">
|
|
18
|
+
<h1>Yönetici Paneli</h1>
|
|
19
|
+
<p>Niki Loyalty Puan & Kullanıcı Yönetimi</p>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="nk-header-refresh">
|
|
22
|
+
<button id="btn-refresh" onclick="location.reload()" title="Yenile"><i class="fa fa-refresh"></i></button>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- İstatistik Kartları -->
|
|
27
|
+
<div class="nk-stats">
|
|
28
|
+
<div class="nk-card">
|
|
29
|
+
<div class="nk-card-val" id="val-users">0</div>
|
|
30
|
+
<div class="nk-card-lbl">KULLANICI SAYISI</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="nk-card primary">
|
|
33
|
+
<div class="nk-card-val" id="val-points">0</div>
|
|
34
|
+
<div class="nk-card-lbl">DAĞITILAN PUAN</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="nk-card">
|
|
37
|
+
<div class="nk-card-val" id="val-avg">0</div>
|
|
38
|
+
<div class="nk-card-lbl">ORTALAMA PUAN</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="nk-card accent">
|
|
41
|
+
<div class="nk-card-val" id="val-redeemed">0</div>
|
|
42
|
+
<div class="nk-card-lbl">HARCANAN PUAN</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="nk-card">
|
|
45
|
+
<div class="nk-card-val" id="val-transactions">0</div>
|
|
46
|
+
<div class="nk-card-lbl">TOPLAM İŞLEM</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="nk-card highlight">
|
|
49
|
+
<div class="nk-card-val" id="val-today">0</div>
|
|
50
|
+
<div class="nk-card-lbl">BUGÜN İŞLEM</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<!-- Liste Bölümü -->
|
|
55
|
+
<div class="nk-list-container">
|
|
56
|
+
<div class="nk-toolbar">
|
|
57
|
+
<div class="nk-tb-title">Kullanıcı Sıralaması</div>
|
|
58
|
+
<div class="nk-search-wrapper">
|
|
59
|
+
<i class="fa fa-search"></i>
|
|
60
|
+
<input type="text" id="nk-search-input" placeholder="Kullanıcı ara...">
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="nk-table-header">
|
|
65
|
+
<div style="width: 50px; text-align: center;">#</div>
|
|
66
|
+
<div style="flex:1; padding-left: 15px;">KULLANICI</div>
|
|
67
|
+
<div style="width: 120px; text-align: right; padding-right:10px;">PUAN</div>
|
|
68
|
+
<div style="width: 100px; text-align: center;">İŞLEMLER</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div id="nk-list-body" class="nk-list-body"></div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<!-- PUAN YÖNETİM MODALI -->
|
|
76
|
+
<div id="modal-manage" class="nk-modal-overlay" style="display:none;">
|
|
77
|
+
<div class="nk-modal">
|
|
78
|
+
<div class="nk-modal-head">
|
|
79
|
+
<h3>Puan Yönetimi</h3>
|
|
80
|
+
<button class="nk-close" onclick="closeManageModal()"><i class="fa fa-times"></i></button>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="nk-modal-body">
|
|
83
|
+
<div class="nk-user-preview">
|
|
84
|
+
<span id="manage-uname" style="font-weight:700; color:#C5A065;">Kullanıcı</span> düzenleniyor
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<label class="nk-label">İşlem Türü</label>
|
|
88
|
+
<div class="nk-switch-group">
|
|
89
|
+
<label class="nk-radio-box">
|
|
90
|
+
<input type="radio" name="manage_action" value="add" checked>
|
|
91
|
+
<span class="nk-radio-tile"><i class="fa fa-plus-circle"></i> EKLE</span>
|
|
92
|
+
</label>
|
|
93
|
+
<label class="nk-radio-box">
|
|
94
|
+
<input type="radio" name="manage_action" value="remove">
|
|
95
|
+
<span class="nk-radio-tile remove"><i class="fa fa-minus-circle"></i> ÇIKAR</span>
|
|
96
|
+
</label>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<label class="nk-label">Miktar</label>
|
|
100
|
+
<input type="number" id="manage-amount" class="nk-input" placeholder="0">
|
|
101
|
+
|
|
102
|
+
<label class="nk-label">Sebep (Loglama İçin)</label>
|
|
103
|
+
<input type="text" id="manage-reason" class="nk-input" placeholder="Örn: Manuel Telafi, Yarışma Ödülü...">
|
|
104
|
+
|
|
105
|
+
<div id="manage-error" style="color:#e53935; font-size:13px; margin-top:10px; display:none;"></div>
|
|
106
|
+
|
|
107
|
+
<button class="nk-btn-save" onclick="submitManagePoints()">KAYDET VE UYGULA</button>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<!-- KULLANICI DETAY MODALI -->
|
|
113
|
+
<div id="modal-detail" class="nk-modal-overlay" style="display:none;">
|
|
114
|
+
<div class="nk-modal nk-modal-large">
|
|
115
|
+
<div class="nk-modal-head">
|
|
116
|
+
<h3><i class="fa fa-user-circle"></i> Kullanıcı Detayı</h3>
|
|
117
|
+
<button class="nk-close" onclick="closeDetailModal()"><i class="fa fa-times"></i></button>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="nk-modal-body" id="detail-body">
|
|
120
|
+
<div class="niki-loader" style="padding:40px;">
|
|
121
|
+
<div class="spinner"></div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<style>
|
|
129
|
+
/* --- DARK PREMIUM THEME (POS STİLİ) --- */
|
|
130
|
+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
|
|
131
|
+
|
|
132
|
+
/* Scope */
|
|
133
|
+
.niki-dashboard {
|
|
134
|
+
font-family: 'Poppins', sans-serif;
|
|
135
|
+
color: #fff;
|
|
136
|
+
background: #111;
|
|
137
|
+
min-height: 600px;
|
|
138
|
+
padding: 30px;
|
|
139
|
+
border-radius: 20px;
|
|
140
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
|
|
141
|
+
max-width: 1200px;
|
|
142
|
+
margin: 20px auto;
|
|
143
|
+
position: relative;
|
|
144
|
+
}
|
|
145
|
+
.niki-dashboard * { box-sizing: border-box; }
|
|
146
|
+
|
|
147
|
+
/* Loader */
|
|
148
|
+
.niki-loader { text-align: center; padding: 100px 0; }
|
|
149
|
+
.niki-loader .spinner {
|
|
150
|
+
width: 50px; height: 50px; border: 4px solid #333;
|
|
151
|
+
border-top: 4px solid #C5A065; border-radius: 50%;
|
|
152
|
+
margin: 0 auto 20px; animation: spin 1s linear infinite;
|
|
153
|
+
}
|
|
154
|
+
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
155
|
+
.loading-text { color: #888; font-size: 14px; letter-spacing: 1px; }
|
|
156
|
+
|
|
157
|
+
/* Header */
|
|
158
|
+
.nk-header {
|
|
159
|
+
display: flex; align-items: center; margin-bottom: 35px;
|
|
160
|
+
padding-bottom: 25px; border-bottom: 1px solid #222;
|
|
161
|
+
}
|
|
162
|
+
.nk-header-icon {
|
|
163
|
+
width: 60px; height: 60px;
|
|
164
|
+
background: linear-gradient(135deg, #C5A065, #8D6E63);
|
|
165
|
+
color: #000; border-radius: 16px;
|
|
166
|
+
display: flex; align-items: center; justify-content: center;
|
|
167
|
+
font-size: 28px; box-shadow: 0 5px 20px rgba(197, 160, 101, 0.3);
|
|
168
|
+
margin-right: 20px;
|
|
169
|
+
}
|
|
170
|
+
.nk-header-text h1 { margin: 0; font-size: 24px; font-weight: 700; color: #fff; }
|
|
171
|
+
.nk-header-text p { margin: 5px 0 0; font-size: 13px; color: #888; }
|
|
172
|
+
.nk-header-refresh { margin-left: auto; }
|
|
173
|
+
#btn-refresh {
|
|
174
|
+
background: #222; border: 1px solid #333; color: #fff;
|
|
175
|
+
width: 40px; height: 40px; border-radius: 10px; cursor: pointer;
|
|
176
|
+
transition: 0.2s;
|
|
177
|
+
}
|
|
178
|
+
#btn-refresh:hover { background: #333; color: #C5A065; }
|
|
179
|
+
|
|
180
|
+
/* İstatistikler */
|
|
181
|
+
.nk-stats {
|
|
182
|
+
display: grid; grid-template-columns: repeat(6, 1fr); gap: 15px;
|
|
183
|
+
margin-bottom: 35px;
|
|
184
|
+
}
|
|
185
|
+
.nk-card {
|
|
186
|
+
background: #1a1a1a; border: 1px solid #2a2a2a;
|
|
187
|
+
padding: 20px 15px; border-radius: 16px; text-align: center;
|
|
188
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
|
189
|
+
transition: transform 0.2s, border-color 0.2s;
|
|
190
|
+
}
|
|
191
|
+
.nk-card:hover { transform: translateY(-3px); border-color: #333; }
|
|
192
|
+
.nk-card.primary {
|
|
193
|
+
background: linear-gradient(135deg, #2a2a2a, #1f1f1f);
|
|
194
|
+
border-color: #C5A065;
|
|
195
|
+
}
|
|
196
|
+
.nk-card.accent {
|
|
197
|
+
background: linear-gradient(135deg, rgba(198,40,40,0.15), #1a1a1a);
|
|
198
|
+
border-color: rgba(198,40,40,0.4);
|
|
199
|
+
}
|
|
200
|
+
.nk-card.highlight {
|
|
201
|
+
background: linear-gradient(135deg, rgba(46,125,50,0.15), #1a1a1a);
|
|
202
|
+
border-color: rgba(46,125,50,0.4);
|
|
203
|
+
}
|
|
204
|
+
.nk-card-val { font-size: 26px; font-weight: 700; color: #fff; margin-bottom: 5px; }
|
|
205
|
+
.nk-card.primary .nk-card-val { color: #C5A065; }
|
|
206
|
+
.nk-card.accent .nk-card-val { color: #ef5350; }
|
|
207
|
+
.nk-card.highlight .nk-card-val { color: #66bb6a; }
|
|
208
|
+
.nk-card-lbl { font-size: 10px; font-weight: 600; color: #666; letter-spacing: 0.8px; text-transform: uppercase; }
|
|
209
|
+
|
|
210
|
+
/* Liste Alanı */
|
|
211
|
+
.nk-list-container {
|
|
212
|
+
background: #1a1a1a; border-radius: 20px; overflow: hidden;
|
|
213
|
+
border: 1px solid #2a2a2a;
|
|
214
|
+
}
|
|
215
|
+
.nk-toolbar {
|
|
216
|
+
padding: 20px; border-bottom: 1px solid #2a2a2a;
|
|
217
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
218
|
+
}
|
|
219
|
+
.nk-tb-title { font-size: 16px; font-weight: 600; color: #eee; }
|
|
220
|
+
|
|
221
|
+
.nk-search-wrapper { position: relative; }
|
|
222
|
+
.nk-search-wrapper i {
|
|
223
|
+
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
|
|
224
|
+
color: #666; font-size: 14px;
|
|
225
|
+
}
|
|
226
|
+
#nk-search-input {
|
|
227
|
+
background: #111; border: 1px solid #333; color: #fff;
|
|
228
|
+
padding: 10px 15px 10px 35px; border-radius: 10px; font-size: 13px;
|
|
229
|
+
width: 240px; outline: none; transition: 0.2s;
|
|
230
|
+
}
|
|
231
|
+
#nk-search-input:focus { border-color: #C5A065; }
|
|
232
|
+
|
|
233
|
+
/* Tablo Başlık */
|
|
234
|
+
.nk-table-header {
|
|
235
|
+
display: flex; padding: 15px 20px; background: #151515;
|
|
236
|
+
font-size: 12px; font-weight: 700; color: #555;
|
|
237
|
+
text-transform: uppercase; letter-spacing: 0.5px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Liste Gövde */
|
|
241
|
+
.nk-list-body { max-height: 500px; overflow-y: auto; padding-bottom: 10px; }
|
|
242
|
+
|
|
243
|
+
/* Liste Satırı */
|
|
244
|
+
.nk-row {
|
|
245
|
+
display: flex; align-items: center; padding: 15px 20px;
|
|
246
|
+
border-bottom: 1px solid #222; transition: 0.2s;
|
|
247
|
+
}
|
|
248
|
+
.nk-row:hover { background: #222; }
|
|
249
|
+
|
|
250
|
+
.nk-idx { width: 50px; text-align: center; color: #444; font-weight: 700; font-size: 14px; }
|
|
251
|
+
|
|
252
|
+
.nk-user-info { flex: 1; display: flex; align-items: center; padding-left: 15px; }
|
|
253
|
+
.nk-avatar {
|
|
254
|
+
width: 40px; height: 40px; border-radius: 50%;
|
|
255
|
+
object-fit: cover; margin-right: 15px; border: 2px solid #333;
|
|
256
|
+
}
|
|
257
|
+
.nk-letter-avatar {
|
|
258
|
+
width: 40px; height: 40px; border-radius: 50%; display: flex;
|
|
259
|
+
align-items: center; justify-content: center; font-weight: 700;
|
|
260
|
+
color: #fff; margin-right: 15px; font-size: 16px; border: 2px solid #333;
|
|
261
|
+
}
|
|
262
|
+
.nk-username { font-weight: 600; color: #ddd; font-size: 14px; text-decoration: none; }
|
|
263
|
+
.nk-username:hover { text-decoration: underline; color: #C5A065; }
|
|
264
|
+
|
|
265
|
+
.nk-points {
|
|
266
|
+
width: 120px; text-align: right; padding-right: 10px;
|
|
267
|
+
font-size: 16px; font-weight: 700; color: #C5A065;
|
|
268
|
+
}
|
|
269
|
+
.nk-actions { width: 100px; text-align: center; display: flex; justify-content: center; gap: 6px; }
|
|
270
|
+
.nk-btn-action {
|
|
271
|
+
background: #2a2a2a; border: 1px solid #333; color: #888;
|
|
272
|
+
width: 32px; height: 32px; border-radius: 8px; cursor: pointer; transition: 0.2s;
|
|
273
|
+
}
|
|
274
|
+
.nk-btn-action:hover { background: #C5A065; color: #000; border-color: #C5A065; }
|
|
275
|
+
.nk-btn-action.info:hover { background: #1976D2; border-color: #1976D2; color: #fff; }
|
|
276
|
+
|
|
277
|
+
/* Scrollbar */
|
|
278
|
+
.nk-list-body::-webkit-scrollbar { width: 6px; }
|
|
279
|
+
.nk-list-body::-webkit-scrollbar-track { background: #111; }
|
|
280
|
+
.nk-list-body::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
|
|
281
|
+
.nk-list-body::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
282
|
+
|
|
283
|
+
/* MODAL */
|
|
284
|
+
.nk-modal-overlay {
|
|
285
|
+
position: fixed; top:0; left:0; width:100%; height:100%;
|
|
286
|
+
background: rgba(0,0,0,0.85); backdrop-filter: blur(8px);
|
|
287
|
+
z-index: 9999; display: flex; align-items: center; justify-content: center;
|
|
288
|
+
padding: 20px;
|
|
289
|
+
}
|
|
290
|
+
.nk-modal {
|
|
291
|
+
background: #1a1a1a; width: 380px; max-width: 100%; border-radius: 20px;
|
|
292
|
+
border: 1px solid #333; box-shadow: 0 20px 50px rgba(0,0,0,0.8);
|
|
293
|
+
overflow: hidden; max-height: 90vh; display: flex; flex-direction: column;
|
|
294
|
+
}
|
|
295
|
+
.nk-modal-large { width: 700px; }
|
|
296
|
+
.nk-modal-head {
|
|
297
|
+
background: #222; padding: 16px 20px; border-bottom: 1px solid #333;
|
|
298
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
299
|
+
flex-shrink: 0;
|
|
300
|
+
}
|
|
301
|
+
.nk-modal-head h3 { margin: 0; font-size: 16px; color: #fff; display: flex; align-items: center; gap: 10px; }
|
|
302
|
+
.nk-close { background: none; border: none; color: #666; font-size: 18px; cursor: pointer; padding: 5px; }
|
|
303
|
+
.nk-close:hover { color: #fff; }
|
|
304
|
+
|
|
305
|
+
.nk-modal-body { padding: 25px; overflow-y: auto; flex: 1; }
|
|
306
|
+
.nk-user-preview { background: #222; padding: 12px 16px; border-radius: 10px; margin-bottom: 20px; font-size: 14px; color: #888; }
|
|
307
|
+
.nk-label { display: block; font-size: 11px; color: #888; text-transform: uppercase; margin-bottom: 8px; font-weight: 600; letter-spacing: 0.5px; }
|
|
308
|
+
.nk-input {
|
|
309
|
+
width: 100%; background: #111; border: 1px solid #333; padding: 12px;
|
|
310
|
+
border-radius: 10px; color: #fff; margin-bottom: 20px; font-size: 14px;
|
|
311
|
+
}
|
|
312
|
+
.nk-input:focus { outline: none; border-color: #C5A065; }
|
|
313
|
+
|
|
314
|
+
.nk-switch-group { display: flex; gap: 10px; margin-bottom: 20px; }
|
|
315
|
+
.nk-radio-box { flex: 1; cursor: pointer; }
|
|
316
|
+
.nk-radio-box input { display: none; }
|
|
317
|
+
.nk-radio-tile {
|
|
318
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
319
|
+
gap: 5px; height: 60px; background: #222; border: 2px solid #333;
|
|
320
|
+
border-radius: 10px; color: #666; font-size: 11px; font-weight: 700;
|
|
321
|
+
}
|
|
322
|
+
.nk-radio-box input:checked + .nk-radio-tile {
|
|
323
|
+
background: rgba(46, 125, 50, 0.2); border-color: #2E7D32; color: #2E7D32;
|
|
324
|
+
}
|
|
325
|
+
.nk-radio-box input:checked + .nk-radio-tile.remove {
|
|
326
|
+
background: rgba(198, 40, 40, 0.2); border-color: #C62828; color: #C62828;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.nk-btn-save {
|
|
330
|
+
width: 100%; background: #C5A065; color: #000; border: none;
|
|
331
|
+
padding: 15px; border-radius: 12px; font-weight: 700; font-size: 14px;
|
|
332
|
+
cursor: pointer; transition: 0.2s;
|
|
333
|
+
}
|
|
334
|
+
.nk-btn-save:hover { background: #d4ac6e; transform: translateY(-2px); }
|
|
335
|
+
|
|
336
|
+
/* DETAY MODAL İÇERİĞİ */
|
|
337
|
+
.detail-header { display: flex; align-items: center; gap: 20px; margin-bottom: 25px; padding-bottom: 20px; border-bottom: 1px solid #2a2a2a; }
|
|
338
|
+
.detail-avatar { width: 70px; height: 70px; border-radius: 50%; border: 3px solid #C5A065; object-fit: cover; }
|
|
339
|
+
.detail-letter-avatar { width: 70px; height: 70px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 28px; font-weight: 700; color: #fff; border: 3px solid #C5A065; }
|
|
340
|
+
.detail-info h2 { margin: 0 0 5px; font-size: 20px; color: #fff; }
|
|
341
|
+
.detail-info p { margin: 0; font-size: 13px; color: #888; }
|
|
342
|
+
.detail-points-badge { margin-left: auto; background: linear-gradient(135deg, #C5A065, #8D6E63); padding: 15px 25px; border-radius: 14px; text-align: center; }
|
|
343
|
+
.detail-points-val { font-size: 28px; font-weight: 700; color: #000; }
|
|
344
|
+
.detail-points-lbl { font-size: 10px; color: rgba(0,0,0,0.6); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
345
|
+
|
|
346
|
+
.detail-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 25px; }
|
|
347
|
+
.detail-stat { background: #222; border-radius: 12px; padding: 15px; text-align: center; border: 1px solid #2a2a2a; }
|
|
348
|
+
.detail-stat-val { font-size: 20px; font-weight: 700; color: #fff; }
|
|
349
|
+
.detail-stat-val.green { color: #66bb6a; }
|
|
350
|
+
.detail-stat-val.red { color: #ef5350; }
|
|
351
|
+
.detail-stat-lbl { font-size: 10px; color: #666; text-transform: uppercase; margin-top: 4px; }
|
|
352
|
+
|
|
353
|
+
.detail-section { margin-bottom: 25px; }
|
|
354
|
+
.detail-section-title { font-size: 13px; font-weight: 700; color: #888; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
|
|
355
|
+
.detail-section-title i { color: #C5A065; }
|
|
356
|
+
|
|
357
|
+
.detail-tabs { display: flex; gap: 8px; margin-bottom: 15px; }
|
|
358
|
+
.detail-tab { background: #222; border: 1px solid #333; padding: 10px 16px; border-radius: 10px; font-size: 12px; font-weight: 600; color: #888; cursor: pointer; transition: 0.2s; }
|
|
359
|
+
.detail-tab:hover { border-color: #444; color: #fff; }
|
|
360
|
+
.detail-tab.active { background: #C5A065; border-color: #C5A065; color: #000; }
|
|
361
|
+
|
|
362
|
+
.detail-history { max-height: 250px; overflow-y: auto; background: #151515; border-radius: 12px; border: 1px solid #2a2a2a; }
|
|
363
|
+
.detail-history-item { display: flex; align-items: center; padding: 12px 15px; border-bottom: 1px solid #222; }
|
|
364
|
+
.detail-history-item:last-child { border-bottom: none; }
|
|
365
|
+
.detail-history-icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px; font-size: 14px; }
|
|
366
|
+
.detail-history-icon.earn { background: rgba(46,125,50,0.2); color: #66bb6a; }
|
|
367
|
+
.detail-history-icon.spend { background: rgba(198,40,40,0.2); color: #ef5350; }
|
|
368
|
+
.detail-history-text { flex: 1; }
|
|
369
|
+
.detail-history-desc { font-size: 13px; color: #ddd; }
|
|
370
|
+
.detail-history-time { font-size: 11px; color: #666; margin-top: 2px; }
|
|
371
|
+
.detail-history-val { font-weight: 700; font-size: 14px; }
|
|
372
|
+
.detail-history-val.earn { color: #66bb6a; }
|
|
373
|
+
.detail-history-val.spend { color: #ef5350; }
|
|
374
|
+
|
|
375
|
+
.detail-empty { text-align: center; padding: 30px; color: #555; font-size: 13px; }
|
|
376
|
+
.detail-empty i { font-size: 32px; display: block; margin-bottom: 10px; opacity: 0.5; }
|
|
377
|
+
|
|
378
|
+
/* Günlük Limit Çubuğu */
|
|
379
|
+
.daily-progress { margin-top: 10px; }
|
|
380
|
+
.daily-progress-bar { height: 8px; background: #333; border-radius: 4px; overflow: hidden; }
|
|
381
|
+
.daily-progress-fill { height: 100%; background: linear-gradient(90deg, #C5A065, #8D6E63); border-radius: 4px; transition: width 0.3s; }
|
|
382
|
+
.daily-progress-label { display: flex; justify-content: space-between; font-size: 11px; color: #666; margin-top: 5px; }
|
|
383
|
+
|
|
384
|
+
@media(max-width: 900px) {
|
|
385
|
+
.nk-stats { grid-template-columns: repeat(3, 1fr); }
|
|
386
|
+
.detail-stats { grid-template-columns: repeat(2, 1fr); }
|
|
387
|
+
}
|
|
388
|
+
@media(max-width: 600px) {
|
|
389
|
+
.niki-dashboard { padding: 15px; }
|
|
390
|
+
.nk-stats { grid-template-columns: repeat(2, 1fr); }
|
|
391
|
+
.nk-toolbar { flex-direction: column; gap: 15px; align-items: stretch; }
|
|
392
|
+
#nk-search-input { width: 100%; }
|
|
393
|
+
.nk-modal-large { width: 100%; }
|
|
394
|
+
.detail-header { flex-direction: column; text-align: center; }
|
|
395
|
+
.detail-points-badge { margin-left: 0; margin-top: 15px; }
|
|
396
|
+
.detail-stats { grid-template-columns: repeat(2, 1fr); }
|
|
397
|
+
}
|
|
398
|
+
</style>
|
|
399
|
+
|
|
400
|
+
<script>
|
|
401
|
+
(function() {
|
|
402
|
+
let g_users = [];
|
|
403
|
+
let g_targetUid = 0;
|
|
404
|
+
moment.locale('tr');
|
|
405
|
+
|
|
406
|
+
// Global functions
|
|
407
|
+
window.closeManageModal = function() { $('#modal-manage').fadeOut(200); };
|
|
408
|
+
window.closeDetailModal = function() { $('#modal-detail').fadeOut(200); };
|
|
409
|
+
|
|
410
|
+
window.openManageModal = function(uid, uname) {
|
|
411
|
+
g_targetUid = uid;
|
|
412
|
+
$('#manage-uname').text(uname);
|
|
413
|
+
$('#manage-amount').val('');
|
|
414
|
+
$('#manage-reason').val('');
|
|
415
|
+
$('#manage-error').hide();
|
|
416
|
+
$('#modal-manage').fadeIn(200).css('display', 'flex');
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
window.openDetailModal = function(uid) {
|
|
420
|
+
$('#modal-detail').fadeIn(200).css('display', 'flex');
|
|
421
|
+
$('#detail-body').html('<div class="niki-loader" style="padding:40px;"><div class="spinner"></div></div>');
|
|
422
|
+
|
|
423
|
+
socket.emit('plugins.niki.getUserDetail', { uid: uid }, function(err, data) {
|
|
424
|
+
if (err) {
|
|
425
|
+
$('#detail-body').html('<div class="detail-empty"><i class="fa fa-exclamation-circle"></i>Hata: ' + (err.message || 'Bilinmeyen hata') + '</div>');
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
renderDetailModal(data);
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
function renderDetailModal(data) {
|
|
433
|
+
const u = data.user;
|
|
434
|
+
const stats = data.stats;
|
|
435
|
+
const rp = config.relative_path || '';
|
|
436
|
+
|
|
437
|
+
let avatarHtml = '';
|
|
438
|
+
if (u.picture) {
|
|
439
|
+
avatarHtml = '<img src="' + u.picture + '" class="detail-avatar">';
|
|
440
|
+
} else {
|
|
441
|
+
const letter = u.username ? u.username[0].toUpperCase() : '?';
|
|
442
|
+
avatarHtml = '<div class="detail-letter-avatar" style="background:' + u.iconBg + '">' + letter + '</div>';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const dailyPercent = Math.min(100, (stats.todayScore / 35) * 100);
|
|
446
|
+
|
|
447
|
+
let html = `
|
|
448
|
+
<div class="detail-header">
|
|
449
|
+
${avatarHtml}
|
|
450
|
+
<div class="detail-info">
|
|
451
|
+
<h2>${escapeHtml(u.username)}</h2>
|
|
452
|
+
<p><i class="fa fa-envelope"></i> ${escapeHtml(u.email) || 'E-posta yok'}</p>
|
|
453
|
+
<p><i class="fa fa-clock-o"></i> Son görülme: ${u.lastonline ? moment(u.lastonline).fromNow() : 'Bilinmiyor'}</p>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="detail-points-badge">
|
|
456
|
+
<div class="detail-points-val">${Math.floor(u.points).toLocaleString('tr-TR')}</div>
|
|
457
|
+
<div class="detail-points-lbl">Mevcut Puan</div>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<div class="detail-stats">
|
|
462
|
+
<div class="detail-stat">
|
|
463
|
+
<div class="detail-stat-val green">+${Math.floor(stats.totalEarned).toLocaleString('tr-TR')}</div>
|
|
464
|
+
<div class="detail-stat-lbl">Kazanılan</div>
|
|
465
|
+
</div>
|
|
466
|
+
<div class="detail-stat">
|
|
467
|
+
<div class="detail-stat-val red">-${Math.floor(stats.totalSpent).toLocaleString('tr-TR')}</div>
|
|
468
|
+
<div class="detail-stat-lbl">Harcanan</div>
|
|
469
|
+
</div>
|
|
470
|
+
<div class="detail-stat">
|
|
471
|
+
<div class="detail-stat-val">${stats.todayScore}</div>
|
|
472
|
+
<div class="detail-stat-lbl">Bugün</div>
|
|
473
|
+
</div>
|
|
474
|
+
<div class="detail-stat">
|
|
475
|
+
<div class="detail-stat-val">${data.kasaHistory.length}</div>
|
|
476
|
+
<div class="detail-stat-lbl">Ödül Kullanımı</div>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
|
|
480
|
+
<div class="detail-section">
|
|
481
|
+
<div class="detail-section-title"><i class="fa fa-line-chart"></i> Günlük İlerleme</div>
|
|
482
|
+
<div class="daily-progress">
|
|
483
|
+
<div class="daily-progress-bar">
|
|
484
|
+
<div class="daily-progress-fill" style="width: ${dailyPercent}%"></div>
|
|
485
|
+
</div>
|
|
486
|
+
<div class="daily-progress-label">
|
|
487
|
+
<span>${stats.todayScore} / 35 puan</span>
|
|
488
|
+
<span>${dailyPercent.toFixed(0)}%</span>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
|
|
493
|
+
<div class="detail-section">
|
|
494
|
+
<div class="detail-section-title"><i class="fa fa-history"></i> Aktivite Geçmişi</div>
|
|
495
|
+
<div class="detail-tabs">
|
|
496
|
+
<div class="detail-tab active" onclick="switchHistoryTab('earn', this)">Kazanılan</div>
|
|
497
|
+
<div class="detail-tab" onclick="switchHistoryTab('spend', this)">Harcanan</div>
|
|
498
|
+
<div class="detail-tab" onclick="switchHistoryTab('kasa', this)">Ödül Kullanımı</div>
|
|
499
|
+
</div>
|
|
500
|
+
<div class="detail-history" id="history-container"></div>
|
|
501
|
+
</div>
|
|
502
|
+
`;
|
|
503
|
+
|
|
504
|
+
$('#detail-body').html(html);
|
|
505
|
+
|
|
506
|
+
// İlk sekmeyi göster
|
|
507
|
+
window.currentDetailData = data;
|
|
508
|
+
switchHistoryTab('earn');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
window.switchHistoryTab = function(type, el) {
|
|
512
|
+
$('.detail-tab').removeClass('active');
|
|
513
|
+
if (el) $(el).addClass('active');
|
|
514
|
+
else $('.detail-tab').first().addClass('active');
|
|
515
|
+
|
|
516
|
+
const data = window.currentDetailData;
|
|
517
|
+
if (!data) return;
|
|
518
|
+
|
|
519
|
+
const container = $('#history-container');
|
|
520
|
+
let items = [];
|
|
521
|
+
|
|
522
|
+
if (type === 'earn') {
|
|
523
|
+
items = data.earnHistory || [];
|
|
524
|
+
} else if (type === 'spend') {
|
|
525
|
+
items = data.spendHistory || [];
|
|
526
|
+
} else if (type === 'kasa') {
|
|
527
|
+
items = (data.kasaHistory || []).map(k => ({
|
|
528
|
+
type: 'spend',
|
|
529
|
+
ts: k.ts,
|
|
530
|
+
amt: k.amt,
|
|
531
|
+
txt: k.reward || 'Ödül Kullanımı'
|
|
532
|
+
}));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (items.length === 0) {
|
|
536
|
+
container.html('<div class="detail-empty"><i class="fa fa-inbox"></i>Kayıt bulunamadı</div>');
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
let html = '';
|
|
541
|
+
items.forEach(item => {
|
|
542
|
+
const isEarn = item.type === 'earn' || item.type === 'admin_adjust';
|
|
543
|
+
const iconClass = isEarn ? 'earn' : 'spend';
|
|
544
|
+
const icon = isEarn ? 'fa-arrow-up' : 'fa-arrow-down';
|
|
545
|
+
const valClass = isEarn ? 'earn' : 'spend';
|
|
546
|
+
const sign = isEarn ? '+' : '-';
|
|
547
|
+
|
|
548
|
+
html += `
|
|
549
|
+
<div class="detail-history-item">
|
|
550
|
+
<div class="detail-history-icon ${iconClass}"><i class="fa ${icon}"></i></div>
|
|
551
|
+
<div class="detail-history-text">
|
|
552
|
+
<div class="detail-history-desc">${escapeHtml(item.txt || 'İşlem')}</div>
|
|
553
|
+
<div class="detail-history-time">${moment(item.ts).format('DD MMM YYYY, HH:mm')}</div>
|
|
554
|
+
</div>
|
|
555
|
+
<div class="detail-history-val ${valClass}">${sign}${item.amt}</div>
|
|
556
|
+
</div>
|
|
557
|
+
`;
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
container.html(html);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
window.submitManagePoints = function() {
|
|
564
|
+
const amt = parseFloat($('#manage-amount').val());
|
|
565
|
+
const reason = $('#manage-reason').val().trim();
|
|
566
|
+
const action = $('input[name="manage_action"]:checked').val();
|
|
567
|
+
const $err = $('#manage-error');
|
|
568
|
+
|
|
569
|
+
if (!amt || amt <= 0) { $err.text('Geçerli bir miktar giriniz.').show(); return; }
|
|
570
|
+
if (!reason) { $err.text('Sebep girmek zorunludur.').show(); return; }
|
|
571
|
+
|
|
572
|
+
socket.emit('plugins.niki.managePoints', {
|
|
573
|
+
targetUid: g_targetUid,
|
|
574
|
+
amount: amt,
|
|
575
|
+
action: action,
|
|
576
|
+
reason: reason
|
|
577
|
+
}, function(err, result) {
|
|
578
|
+
if (err) {
|
|
579
|
+
$err.text(err.message || 'Hata oluştu.').show();
|
|
580
|
+
} else {
|
|
581
|
+
closeManageModal();
|
|
582
|
+
app.alertSuccess('İşlem Başarılı! Yeni Puan: ' + result.newPoints);
|
|
583
|
+
initNikiAdmin();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
function initNikiAdmin() {
|
|
589
|
+
if (typeof socket === 'undefined') return;
|
|
590
|
+
const $loader = $('#niki-loader');
|
|
591
|
+
const $content = $('#niki-content');
|
|
592
|
+
$loader.show(); $content.hide();
|
|
593
|
+
|
|
594
|
+
// Önce istatistikleri al
|
|
595
|
+
socket.emit('plugins.niki.getStats', {}, function(err, stats) {
|
|
596
|
+
if (!err && stats) {
|
|
597
|
+
$('#val-users').text(stats.usersWithPoints || 0);
|
|
598
|
+
$('#val-points').text(Number(stats.totalPoints || 0).toLocaleString('tr-TR'));
|
|
599
|
+
$('#val-avg').text(Number(stats.avgPoints || 0).toLocaleString('tr-TR'));
|
|
600
|
+
$('#val-redeemed').text(Number(stats.totalRedeemed || 0).toLocaleString('tr-TR'));
|
|
601
|
+
$('#val-transactions').text(stats.totalTransactions || 0);
|
|
602
|
+
$('#val-today').text(stats.todayTransactions || 0);
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
socket.emit('plugins.niki.getUsers', {}, function(err, users) {
|
|
607
|
+
$loader.hide();
|
|
608
|
+
if (err) {
|
|
609
|
+
console.error(err);
|
|
610
|
+
$('.niki-dashboard').html('<div style="color:#d32f2f; text-align:center; padding:40px;">HATA: ' + (err.message || 'Yetkisiz') + '</div>');
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (!users) users = [];
|
|
614
|
+
g_users = users;
|
|
615
|
+
$content.fadeIn(300);
|
|
616
|
+
renderList(users);
|
|
617
|
+
|
|
618
|
+
$('#nk-search-input').off('keyup').on('keyup', function() {
|
|
619
|
+
const val = $(this).val().toLowerCase();
|
|
620
|
+
const filtered = users.filter(function(u) {
|
|
621
|
+
return u.username.toLowerCase().indexOf(val) > -1;
|
|
622
|
+
});
|
|
623
|
+
renderList(filtered);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function renderList(list) {
|
|
629
|
+
const $container = $('#nk-list-body');
|
|
630
|
+
$container.empty();
|
|
631
|
+
if (list.length === 0) {
|
|
632
|
+
$container.html('<div style="text-align:center; padding:30px; color:#444;">Sonuç bulunamadı.</div>'); return;
|
|
633
|
+
}
|
|
634
|
+
const rp = config.relative_path || '';
|
|
635
|
+
|
|
636
|
+
$.each(list, function(i, u) {
|
|
637
|
+
const row = $('<div class="nk-row"></div>');
|
|
638
|
+
row.append('<div class="nk-idx">' + (i + 1) + '</div>');
|
|
639
|
+
|
|
640
|
+
const info = $('<div class="nk-user-info"></div>');
|
|
641
|
+
if (u.picture) { info.append('<img src="' + u.picture + '" class="nk-avatar">'); }
|
|
642
|
+
else {
|
|
643
|
+
const letter = (u.username && u.username[0]) ? u.username[0].toUpperCase() : '?';
|
|
644
|
+
const bg = u.iconBg || '#555';
|
|
645
|
+
info.append('<div class="nk-letter-avatar" style="background:' + bg + '">' + letter + '</div>');
|
|
646
|
+
}
|
|
647
|
+
const link = $('<a class="nk-username" target="_blank"></a>').attr('href', rp + '/user/' + u.userslug).text(u.username);
|
|
648
|
+
info.append(link);
|
|
649
|
+
row.append(info);
|
|
650
|
+
|
|
651
|
+
const pText = Math.floor(u.points || 0).toLocaleString('tr-TR');
|
|
652
|
+
row.append('<div class="nk-points">' + pText + '</div>');
|
|
653
|
+
|
|
654
|
+
// Butonlar
|
|
655
|
+
const btnCol = $('<div class="nk-actions"></div>');
|
|
656
|
+
|
|
657
|
+
// Detay Butonu
|
|
658
|
+
const btnDetail = $('<button class="nk-btn-action info" title="Detay"><i class="fa fa-eye"></i></button>');
|
|
659
|
+
btnDetail.click(function() { openDetailModal(u.uid); });
|
|
660
|
+
btnCol.append(btnDetail);
|
|
661
|
+
|
|
662
|
+
// Düzenle Butonu
|
|
663
|
+
const btnManage = $('<button class="nk-btn-action" title="Puan Düzenle"><i class="fa fa-cog"></i></button>');
|
|
664
|
+
btnManage.click(function() { openManageModal(u.uid, u.username); });
|
|
665
|
+
btnCol.append(btnManage);
|
|
666
|
+
|
|
667
|
+
row.append(btnCol);
|
|
668
|
+
$container.append(row);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function escapeHtml(text) {
|
|
673
|
+
if (!text) return '';
|
|
674
|
+
const div = document.createElement('div');
|
|
675
|
+
div.textContent = text;
|
|
676
|
+
return div.innerHTML;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
$(document).ready(function() {
|
|
680
|
+
if ($('.niki-dashboard').length) setTimeout(initNikiAdmin, 200);
|
|
681
|
+
});
|
|
682
|
+
$(window).on('action:ajaxify.end', function() {
|
|
683
|
+
if ($('.niki-dashboard').length) initNikiAdmin();
|
|
684
|
+
});
|
|
685
|
+
})();
|
|
686
|
+
</script>
|