nodebb-plugin-niki-loyalty 1.0.28 → 1.0.29
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 +53 -33
- package/package.json +1 -1
package/library.js
CHANGED
|
@@ -13,14 +13,15 @@ const Plugin = {};
|
|
|
13
13
|
const SETTINGS = {
|
|
14
14
|
pointsPerHeartbeat: 5,
|
|
15
15
|
dailyCap: 250,
|
|
16
|
+
// coffeeCost: 250, // (eski kodda vardı ama artık tier sistemi var; istersen kullanırız)
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
// Rewards configuration (Ordered from highest cost to lowest)
|
|
19
20
|
const REWARDS = [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
{ cost: 250, name: 'Ücretsiz Kahve ☕' },
|
|
22
|
+
{ cost: 180, name: '%60 İndirimli Kahve' },
|
|
23
|
+
{ cost: 120, name: '%30 İndirimli Kahve' },
|
|
24
|
+
{ cost: 60, name: '1 Kurabiye 🍪' },
|
|
24
25
|
];
|
|
25
26
|
|
|
26
27
|
// TEST MODE (Set to true to bypass point checks)
|
|
@@ -33,13 +34,21 @@ function safeParseMaybeJson(x) {
|
|
|
33
34
|
if (x == null) return null;
|
|
34
35
|
if (typeof x === 'object') return x;
|
|
35
36
|
if (typeof x === 'string') {
|
|
36
|
-
try {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(x);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
37
42
|
}
|
|
38
43
|
return null;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
function safeStringify(obj) {
|
|
42
|
-
try {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.stringify(obj);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
// =========================
|
|
@@ -65,7 +74,7 @@ async function addKasaLog(staffUid, customerName, customerUid, rewardName, amoun
|
|
|
65
74
|
cust: customerName,
|
|
66
75
|
cuid: customerUid,
|
|
67
76
|
amt: amount,
|
|
68
|
-
reward: rewardName // Store the specific reward name
|
|
77
|
+
reward: rewardName, // Store the specific reward name
|
|
69
78
|
};
|
|
70
79
|
const payload = safeStringify(logEntry);
|
|
71
80
|
if (!payload) return;
|
|
@@ -80,7 +89,7 @@ Plugin.init = async function (params) {
|
|
|
80
89
|
const router = params.router;
|
|
81
90
|
const middleware = params.middleware;
|
|
82
91
|
|
|
83
|
-
// 1) HEARTBEAT (Earn Points)
|
|
92
|
+
// 1) HEARTBEAT (Earn Points) + ✅ EARN LOG (2. koddaki özellik)
|
|
84
93
|
router.post('/api/niki-loyalty/heartbeat', middleware.ensureLoggedIn, async (req, res) => {
|
|
85
94
|
try {
|
|
86
95
|
const uid = req.uid;
|
|
@@ -96,6 +105,9 @@ Plugin.init = async function (params) {
|
|
|
96
105
|
await user.incrementUserFieldBy(uid, 'niki_points', SETTINGS.pointsPerHeartbeat);
|
|
97
106
|
await db.incrObjectFieldBy(dailyKey, 'score', SETTINGS.pointsPerHeartbeat);
|
|
98
107
|
|
|
108
|
+
// ✅ Log: Cüzdan geçmişinde "kazanım" görünsün
|
|
109
|
+
await addUserLog(uid, 'earn', SETTINGS.pointsPerHeartbeat, 'Ders Çalışma 📚');
|
|
110
|
+
|
|
99
111
|
const newBalance = await user.getUserField(uid, 'niki_points');
|
|
100
112
|
return res.json({ earned: true, points: SETTINGS.pointsPerHeartbeat, total: newBalance });
|
|
101
113
|
} catch (err) {
|
|
@@ -121,10 +133,7 @@ Plugin.init = async function (params) {
|
|
|
121
133
|
let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
|
|
122
134
|
if (dailyPercent > 100) dailyPercent = 100;
|
|
123
135
|
|
|
124
|
-
const history = (historyRaw || [])
|
|
125
|
-
.map(safeParseMaybeJson)
|
|
126
|
-
.filter(Boolean)
|
|
127
|
-
.reverse();
|
|
136
|
+
const history = (historyRaw || []).map(safeParseMaybeJson).filter(Boolean).reverse();
|
|
128
137
|
|
|
129
138
|
return res.json({
|
|
130
139
|
points: currentPoints,
|
|
@@ -132,14 +141,14 @@ Plugin.init = async function (params) {
|
|
|
132
141
|
dailyCap: SETTINGS.dailyCap,
|
|
133
142
|
dailyPercent,
|
|
134
143
|
history,
|
|
135
|
-
rewards: REWARDS
|
|
144
|
+
rewards: REWARDS,
|
|
136
145
|
});
|
|
137
146
|
} catch (err) {
|
|
138
147
|
return res.status(500).json({ points: 0, history: [] });
|
|
139
148
|
}
|
|
140
149
|
});
|
|
141
150
|
|
|
142
|
-
// 3) KASA HISTORY
|
|
151
|
+
// 3) KASA HISTORY (+ ✅ iconBg + reward fallback (2. koddaki özellik))
|
|
143
152
|
router.get('/api/niki-loyalty/kasa-history', middleware.ensureLoggedIn, async (req, res) => {
|
|
144
153
|
try {
|
|
145
154
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
@@ -152,20 +161,27 @@ Plugin.init = async function (params) {
|
|
|
152
161
|
const uids = rows.map(r => parseInt(r.cuid, 10)).filter(n => Number.isFinite(n) && n > 0);
|
|
153
162
|
const users = await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'icon:bgColor']);
|
|
154
163
|
const userMap = {};
|
|
155
|
-
(users || []).forEach(u => {
|
|
164
|
+
(users || []).forEach(u => {
|
|
165
|
+
userMap[u.uid] = u;
|
|
166
|
+
});
|
|
156
167
|
|
|
157
168
|
const rp = nconf.get('relative_path') || '';
|
|
158
169
|
|
|
159
170
|
const enriched = rows.map(r => {
|
|
160
171
|
const uid = parseInt(r.cuid, 10);
|
|
161
172
|
const u = userMap[uid];
|
|
162
|
-
if (!u)
|
|
173
|
+
if (!u) {
|
|
174
|
+
// yine de reward fallback yapalım
|
|
175
|
+
return { ...r, reward: r.reward || 'İşlem' };
|
|
176
|
+
}
|
|
163
177
|
return {
|
|
164
178
|
...r,
|
|
165
179
|
cust: u.username || r.cust || 'Bilinmeyen',
|
|
166
180
|
userslug: u.userslug || r.userslug || '',
|
|
167
181
|
picture: u.picture || r.picture || '',
|
|
168
|
-
|
|
182
|
+
iconBg: u['icon:bgColor'] || r.iconBg || '#4b5563',
|
|
183
|
+
profileUrl: u.userslug ? `${rp}/user/${u.userslug}` : '',
|
|
184
|
+
reward: r.reward || 'İşlem',
|
|
169
185
|
};
|
|
170
186
|
});
|
|
171
187
|
|
|
@@ -180,8 +196,7 @@ Plugin.init = async function (params) {
|
|
|
180
196
|
try {
|
|
181
197
|
const uid = req.uid;
|
|
182
198
|
const points = parseInt((await user.getUserField(uid, 'niki_points')) || 0, 10);
|
|
183
|
-
|
|
184
|
-
// Get the cost of the cheapest reward
|
|
199
|
+
|
|
185
200
|
const minCost = REWARDS[REWARDS.length - 1].cost;
|
|
186
201
|
|
|
187
202
|
if (!TEST_MODE_UNLIMITED && points < minCost) {
|
|
@@ -201,7 +216,7 @@ Plugin.init = async function (params) {
|
|
|
201
216
|
// 5) SCAN QR (Determine Reward & Deduct Points)
|
|
202
217
|
router.post('/api/niki-loyalty/scan-qr', middleware.ensureLoggedIn, async (req, res) => {
|
|
203
218
|
try {
|
|
204
|
-
const token =
|
|
219
|
+
const token = req.body && req.body.token ? String(req.body.token) : '';
|
|
205
220
|
const isAdmin = await user.isAdministrator(req.uid);
|
|
206
221
|
const isMod = await user.isGlobalModerator(req.uid);
|
|
207
222
|
if (!isAdmin && !isMod) return res.status(403).json({ success: false, message: 'Yetkisiz' });
|
|
@@ -210,22 +225,21 @@ Plugin.init = async function (params) {
|
|
|
210
225
|
if (!customerUid) return res.json({ success: false, message: 'Geçersiz Kod' });
|
|
211
226
|
|
|
212
227
|
const pts = parseInt((await user.getUserField(customerUid, 'niki_points')) || 0, 10);
|
|
213
|
-
|
|
228
|
+
|
|
214
229
|
// Calculate best possible reward
|
|
215
230
|
let selectedReward = null;
|
|
216
231
|
if (!TEST_MODE_UNLIMITED) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (!selectedReward) {
|
|
224
|
-
return res.json({ success: false, message: 'Puan Yetersiz' });
|
|
232
|
+
for (const reward of REWARDS) {
|
|
233
|
+
if (pts >= reward.cost) {
|
|
234
|
+
selectedReward = reward;
|
|
235
|
+
break;
|
|
225
236
|
}
|
|
237
|
+
}
|
|
238
|
+
if (!selectedReward) {
|
|
239
|
+
return res.json({ success: false, message: 'Puan Yetersiz' });
|
|
240
|
+
}
|
|
226
241
|
} else {
|
|
227
|
-
|
|
228
|
-
selectedReward = REWARDS[0];
|
|
242
|
+
selectedReward = REWARDS[0];
|
|
229
243
|
}
|
|
230
244
|
|
|
231
245
|
// Deduct Points
|
|
@@ -268,8 +282,14 @@ Plugin.addScripts = async function (scripts) {
|
|
|
268
282
|
};
|
|
269
283
|
|
|
270
284
|
Plugin.addNavigation = async function (nav) {
|
|
271
|
-
nav.push({
|
|
285
|
+
nav.push({
|
|
286
|
+
route: '/niki-wallet',
|
|
287
|
+
title: 'Niki Cüzdan',
|
|
288
|
+
enabled: true,
|
|
289
|
+
iconClass: 'fa-coffee',
|
|
290
|
+
text: 'Niki Cüzdan',
|
|
291
|
+
});
|
|
272
292
|
return nav;
|
|
273
293
|
};
|
|
274
294
|
|
|
275
|
-
module.exports = Plugin;
|
|
295
|
+
module.exports = Plugin;
|