nodebb-plugin-sso-biogrenci 1.0.3 → 1.0.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/image.png +0 -0
- package/library.js +8 -1
- package/package.json +1 -1
- package/static/lib/biogrenci-firsatlar.js +148 -112
- package/static/style.less +542 -371
- package/static/templates/biogrenci-firsatlar.tpl +42 -28
package/image.png
CHANGED
|
Binary file
|
package/library.js
CHANGED
|
@@ -16,7 +16,7 @@ const constants = {
|
|
|
16
16
|
name: 'biogrenci',
|
|
17
17
|
displayName: "bi'öğrenci",
|
|
18
18
|
icon: 'fa-graduation-cap',
|
|
19
|
-
scope: 'openid profile email student_status
|
|
19
|
+
scope: 'openid profile email student_status student opportunities.read',
|
|
20
20
|
authorizationURL: 'https://biogrenci.com/oauth/authorize',
|
|
21
21
|
tokenURL: 'https://biogrenci.com/api/oauth/token',
|
|
22
22
|
userInfoURL: 'https://biogrenci.com/api/oauth/userinfo',
|
|
@@ -214,6 +214,8 @@ plugin.getStrategy = async function (strategies) {
|
|
|
214
214
|
return strategies;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
try {
|
|
218
|
+
|
|
217
219
|
const callbackURL = nconf.get('url') + '/auth/biogrenci/callback';
|
|
218
220
|
winston.info(`${LOG_PREFIX} Registering OAuth2 strategy, callbackURL=%s`, callbackURL);
|
|
219
221
|
|
|
@@ -371,6 +373,11 @@ plugin.getStrategy = async function (strategies) {
|
|
|
371
373
|
});
|
|
372
374
|
|
|
373
375
|
winston.info(`${LOG_PREFIX} Strategy added to list. Total strategies: %d`, strategies.length);
|
|
376
|
+
|
|
377
|
+
} catch (err) {
|
|
378
|
+
winston.error(`${LOG_PREFIX} getStrategy FAILED: %s\n%s`, err.message, err.stack);
|
|
379
|
+
}
|
|
380
|
+
|
|
374
381
|
return strategies;
|
|
375
382
|
};
|
|
376
383
|
|
package/package.json
CHANGED
|
@@ -2,187 +2,227 @@
|
|
|
2
2
|
|
|
3
3
|
define('forum/biogrenci-firsatlar', ['alerts'], function (alerts) {
|
|
4
4
|
var Page = {};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
var state = {
|
|
6
|
+
page: 1,
|
|
7
|
+
type: '',
|
|
8
|
+
sort: 'featured',
|
|
9
|
+
search: '',
|
|
10
|
+
};
|
|
10
11
|
var searchTimeout = null;
|
|
12
|
+
var allBrands = new Set();
|
|
11
13
|
|
|
12
14
|
Page.init = function () {
|
|
13
15
|
console.log('[sso-biogrenci] Firsatlar page init');
|
|
14
16
|
loadOpportunities();
|
|
17
|
+
bindEvents();
|
|
18
|
+
};
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
function bindEvents() {
|
|
21
|
+
// Filters
|
|
22
|
+
$(document).on('click', '.bio-chip', function () {
|
|
23
|
+
$('.bio-chip').removeClass('active');
|
|
19
24
|
$(this).addClass('active');
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
console.log('[sso-biogrenci] Filter changed: type=%s', currentType);
|
|
25
|
+
state.type = $(this).attr('data-type');
|
|
26
|
+
state.page = 1;
|
|
23
27
|
loadOpportunities();
|
|
24
28
|
});
|
|
25
29
|
|
|
26
30
|
// Sort
|
|
27
31
|
$('#biogrenci-sort').on('change', function () {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console.log('[sso-biogrenci] Sort changed: %s', currentSort);
|
|
32
|
+
state.sort = $(this).val();
|
|
33
|
+
state.page = 1;
|
|
31
34
|
loadOpportunities();
|
|
32
35
|
});
|
|
33
36
|
|
|
34
|
-
// Search
|
|
37
|
+
// Search
|
|
35
38
|
$('#biogrenci-search').on('input', function () {
|
|
36
39
|
clearTimeout(searchTimeout);
|
|
37
40
|
var val = $(this).val();
|
|
38
41
|
searchTimeout = setTimeout(function () {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log('[sso-biogrenci] Search: %s', currentSearch);
|
|
42
|
+
state.search = val;
|
|
43
|
+
state.page = 1;
|
|
42
44
|
loadOpportunities();
|
|
43
|
-
},
|
|
45
|
+
}, 350);
|
|
44
46
|
});
|
|
45
47
|
|
|
46
|
-
//
|
|
47
|
-
$('#biogrenci-
|
|
48
|
-
if (
|
|
49
|
-
|
|
48
|
+
// ESC to clear search
|
|
49
|
+
$('#biogrenci-search').on('keydown', function (e) {
|
|
50
|
+
if (e.key === 'Escape') {
|
|
51
|
+
$(this).val('');
|
|
52
|
+
state.search = '';
|
|
53
|
+
state.page = 1;
|
|
50
54
|
loadOpportunities();
|
|
51
55
|
}
|
|
52
56
|
});
|
|
53
57
|
|
|
58
|
+
// Pagination
|
|
59
|
+
$('#biogrenci-prev').on('click', function () {
|
|
60
|
+
if (state.page > 1) { state.page--; loadOpportunities(); }
|
|
61
|
+
});
|
|
54
62
|
$('#biogrenci-next').on('click', function () {
|
|
55
|
-
|
|
63
|
+
state.page++;
|
|
56
64
|
loadOpportunities();
|
|
57
65
|
});
|
|
58
|
-
|
|
66
|
+
|
|
67
|
+
// Claim (delegated)
|
|
68
|
+
$(document).on('click', '.bio-card-cta', function () {
|
|
69
|
+
claimOpportunity($(this));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Copy (delegated)
|
|
73
|
+
$(document).on('click', '.bio-copy-btn', function () {
|
|
74
|
+
var code = $(this).attr('data-code');
|
|
75
|
+
navigator.clipboard.writeText(code);
|
|
76
|
+
alerts.success('Kopyalandı!');
|
|
77
|
+
$(this).html('<i class="fa fa-check"></i>');
|
|
78
|
+
var $btn = $(this);
|
|
79
|
+
setTimeout(function () { $btn.html('<i class="fa fa-copy"></i>'); }, 2000);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
59
82
|
|
|
60
83
|
function loadOpportunities() {
|
|
61
84
|
var $list = $('#biogrenci-list');
|
|
62
85
|
$list.html(
|
|
63
|
-
'<div class="
|
|
64
|
-
'<div class="
|
|
86
|
+
'<div class="bio-loading-state">' +
|
|
87
|
+
'<div class="bio-loader"></div>' +
|
|
65
88
|
'<p>Fırsatlar yükleniyor...</p>' +
|
|
66
89
|
'</div>'
|
|
67
90
|
);
|
|
68
91
|
|
|
69
92
|
var params = new URLSearchParams({
|
|
70
|
-
page:
|
|
93
|
+
page: state.page,
|
|
71
94
|
per_page: 20,
|
|
72
|
-
sort:
|
|
95
|
+
sort: state.sort,
|
|
73
96
|
});
|
|
74
|
-
if (
|
|
75
|
-
if (
|
|
97
|
+
if (state.type) params.set('type', state.type);
|
|
98
|
+
if (state.search) params.set('search', state.search);
|
|
76
99
|
|
|
77
100
|
var url = config.relative_path + '/api/biogrenci/firsatlar?' + params.toString();
|
|
78
|
-
console.log('[sso-biogrenci] Fetching
|
|
101
|
+
console.log('[sso-biogrenci] Fetching:', url);
|
|
79
102
|
|
|
80
103
|
fetch(url)
|
|
81
|
-
.then(function (r) {
|
|
82
|
-
console.log('[sso-biogrenci] API response status: %d', r.status);
|
|
83
|
-
return r.json();
|
|
84
|
-
})
|
|
104
|
+
.then(function (r) { return r.json(); })
|
|
85
105
|
.then(function (res) {
|
|
86
|
-
console.log('[sso-biogrenci] API response:', JSON.stringify(res).substring(0, 500));
|
|
87
|
-
console.log('[sso-biogrenci] Response keys:', Object.keys(res));
|
|
88
|
-
|
|
89
106
|
if (res.error) {
|
|
90
|
-
|
|
91
|
-
$list.html(
|
|
92
|
-
'<div class="biogrenci-empty biogrenci-error">' +
|
|
93
|
-
'<i class="fa fa-exclamation-triangle fa-3x"></i>' +
|
|
94
|
-
'<h4>Fırsatlar yüklenirken hata oluştu</h4>' +
|
|
95
|
-
'<p>' + res.error + '</p>' +
|
|
96
|
-
'</div>'
|
|
97
|
-
);
|
|
107
|
+
showError(res.error);
|
|
98
108
|
return;
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
// API returns { success, data: { opportunities: [...], meta: {...} } }
|
|
102
111
|
var payload = res.data || res;
|
|
103
112
|
var items = payload.opportunities || payload.data || payload.items || payload;
|
|
104
113
|
if (!Array.isArray(items)) items = [];
|
|
105
114
|
var meta = payload.meta || res.meta || {};
|
|
106
115
|
|
|
107
|
-
console.log('[sso-biogrenci]
|
|
116
|
+
console.log('[sso-biogrenci] Got %d items', items.length);
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
// Update stats
|
|
119
|
+
items.forEach(function (item) {
|
|
120
|
+
var brandName = item.brand ? item.brand.name : item.brand_name;
|
|
121
|
+
if (brandName) allBrands.add(brandName);
|
|
122
|
+
});
|
|
123
|
+
$('#bio-stat-total span').text(meta.total || items.length);
|
|
124
|
+
$('#bio-stat-brands span').text(allBrands.size || '--');
|
|
125
|
+
|
|
126
|
+
if (!items.length) {
|
|
127
|
+
showEmpty();
|
|
118
128
|
return;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
renderCards(items);
|
|
122
132
|
renderPagination(meta);
|
|
123
133
|
})
|
|
124
134
|
.catch(function (err) {
|
|
125
|
-
console.error('[sso-biogrenci]
|
|
126
|
-
|
|
127
|
-
'<div class="biogrenci-empty biogrenci-error">' +
|
|
128
|
-
'<i class="fa fa-exclamation-triangle fa-3x"></i>' +
|
|
129
|
-
'<h4>Fırsatlar yüklenirken hata oluştu</h4>' +
|
|
130
|
-
'<p>' + err.message + '</p>' +
|
|
131
|
-
'</div>'
|
|
132
|
-
);
|
|
135
|
+
console.error('[sso-biogrenci] Error:', err);
|
|
136
|
+
showError(err.message);
|
|
133
137
|
});
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
function
|
|
137
|
-
|
|
140
|
+
function showEmpty() {
|
|
141
|
+
$('#biogrenci-list').html(
|
|
142
|
+
'<div class="bio-empty-state">' +
|
|
143
|
+
'<div class="bio-empty-icon"><i class="fa fa-gift"></i></div>' +
|
|
144
|
+
'<h3>Fırsat bulunamadı</h3>' +
|
|
145
|
+
'<p>Filtre veya aramayı değiştirerek tekrar deneyin</p>' +
|
|
146
|
+
'</div>'
|
|
147
|
+
);
|
|
148
|
+
$('#biogrenci-pagination').hide();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function showError(msg) {
|
|
152
|
+
$('#biogrenci-list').html(
|
|
153
|
+
'<div class="bio-empty-state bio-error-state">' +
|
|
154
|
+
'<div class="bio-empty-icon"><i class="fa fa-exclamation-circle"></i></div>' +
|
|
155
|
+
'<h3>Bir hata oluştu</h3>' +
|
|
156
|
+
'<p>' + (msg || 'Lütfen tekrar deneyin') + '</p>' +
|
|
157
|
+
'</div>'
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getDaysLeft(endDate) {
|
|
162
|
+
if (!endDate) return null;
|
|
163
|
+
var now = new Date();
|
|
164
|
+
var end = new Date(endDate);
|
|
165
|
+
var diff = Math.ceil((end - now) / (1000 * 60 * 60 * 24));
|
|
166
|
+
return diff > 0 ? diff : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function renderCards(items) {
|
|
138
170
|
var html = items.map(function (item) {
|
|
171
|
+
var brandName = item.brand ? item.brand.name : (item.brand_name || '');
|
|
139
172
|
var typeLabel = item.type === 'kupon' ? 'Kupon' : item.type === 'qr' ? 'QR Kod' : 'Link';
|
|
140
|
-
var
|
|
173
|
+
var typeClass = 'bio-type-' + (item.type || 'kupon');
|
|
174
|
+
var typeIcon = item.type === 'kupon' ? 'fa-ticket' : item.type === 'qr' ? 'fa-qrcode' : 'fa-external-link';
|
|
175
|
+
var daysLeft = getDaysLeft(item.end_date);
|
|
176
|
+
var btnText = item.button_text || 'Fırsatı Al';
|
|
141
177
|
|
|
142
178
|
return (
|
|
143
|
-
'<div class="
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
'<div class="
|
|
149
|
-
'<span class="
|
|
150
|
-
(item.
|
|
179
|
+
'<div class="bio-card' + (item.is_featured ? ' bio-card-featured' : '') + '">' +
|
|
180
|
+
'<div class="bio-card-visual">' +
|
|
181
|
+
(item.image
|
|
182
|
+
? '<img src="' + item.image + '" alt="' + brandName + '" loading="lazy">'
|
|
183
|
+
: '<div class="bio-card-placeholder"><i class="fa fa-gift"></i></div>') +
|
|
184
|
+
'<div class="bio-card-overlay">' +
|
|
185
|
+
'<span class="bio-type-badge ' + typeClass + '"><i class="fa ' + typeIcon + '"></i> ' + typeLabel + '</span>' +
|
|
186
|
+
(item.is_featured ? '<span class="bio-featured-badge"><i class="fa fa-star"></i></span>' : '') +
|
|
151
187
|
'</div>' +
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
188
|
+
(daysLeft !== null
|
|
189
|
+
? '<div class="bio-card-countdown">' +
|
|
190
|
+
'<i class="fa fa-clock-o"></i> ' +
|
|
191
|
+
(daysLeft <= 3 ? '<strong>Son ' + daysLeft + ' gün!</strong>' : daysLeft + ' gün kaldı') +
|
|
192
|
+
'</div>'
|
|
193
|
+
: '') +
|
|
194
|
+
'</div>' +
|
|
195
|
+
'<div class="bio-card-content">' +
|
|
196
|
+
(brandName ? '<span class="bio-card-brand">' + brandName + '</span>' : '') +
|
|
197
|
+
'<h3 class="bio-card-title">' + item.title + '</h3>' +
|
|
198
|
+
(item.description ? '<p class="bio-card-desc">' + item.description + '</p>' : '') +
|
|
199
|
+
'<button class="bio-card-cta" data-id="' + item.id + '">' + btnText + '</button>' +
|
|
157
200
|
'</div>' +
|
|
158
201
|
'</div>'
|
|
159
202
|
);
|
|
160
203
|
}).join('');
|
|
161
204
|
|
|
162
|
-
$list.html(html);
|
|
163
|
-
|
|
164
|
-
$list.find('.biogrenci-claim-btn').on('click', function () {
|
|
165
|
-
claimOpportunity($(this));
|
|
166
|
-
});
|
|
205
|
+
$('#biogrenci-list').html(html);
|
|
167
206
|
}
|
|
168
207
|
|
|
169
208
|
function renderPagination(meta) {
|
|
170
209
|
var $el = $('#biogrenci-pagination');
|
|
171
|
-
|
|
210
|
+
var total = meta.total || 0;
|
|
211
|
+
if (total <= 20) {
|
|
172
212
|
$el.hide();
|
|
173
213
|
return;
|
|
174
214
|
}
|
|
175
215
|
$el.show();
|
|
176
|
-
var totalPages = Math.ceil(
|
|
177
|
-
$('#biogrenci-page-info').text(
|
|
178
|
-
$('#biogrenci-prev').prop('disabled',
|
|
179
|
-
$('#biogrenci-next').prop('disabled',
|
|
216
|
+
var totalPages = Math.ceil(total / 20);
|
|
217
|
+
$('#biogrenci-page-info').text(state.page + ' / ' + totalPages);
|
|
218
|
+
$('#biogrenci-prev').prop('disabled', state.page <= 1);
|
|
219
|
+
$('#biogrenci-next').prop('disabled', state.page >= totalPages);
|
|
180
220
|
}
|
|
181
221
|
|
|
182
222
|
function claimOpportunity($btn) {
|
|
183
223
|
var id = $btn.attr('data-id');
|
|
184
|
-
$btn.
|
|
185
|
-
|
|
224
|
+
var originalText = $btn.text();
|
|
225
|
+
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>');
|
|
186
226
|
|
|
187
227
|
fetch(config.relative_path + '/api/biogrenci/firsatlar/' + id + '/claim', {
|
|
188
228
|
method: 'POST',
|
|
@@ -193,41 +233,37 @@ define('forum/biogrenci-firsatlar', ['alerts'], function (alerts) {
|
|
|
193
233
|
})
|
|
194
234
|
.then(function (r) { return r.json(); })
|
|
195
235
|
.then(function (res) {
|
|
196
|
-
console.log('[sso-biogrenci] Claim response:', res);
|
|
197
236
|
if (res.error) {
|
|
198
237
|
alerts.error(res.error);
|
|
199
|
-
$btn.prop('disabled', false).
|
|
238
|
+
$btn.prop('disabled', false).text(originalText);
|
|
200
239
|
return;
|
|
201
240
|
}
|
|
202
241
|
|
|
203
242
|
var data = res.data || res;
|
|
204
243
|
if (data.coupon_code) {
|
|
205
244
|
$btn.replaceWith(
|
|
206
|
-
'<div class="
|
|
207
|
-
'<
|
|
208
|
-
|
|
209
|
-
'<i class="fa fa-copy"></i>' +
|
|
210
|
-
'</
|
|
245
|
+
'<div class="bio-claim-result">' +
|
|
246
|
+
'<div class="bio-coupon-box">' +
|
|
247
|
+
'<code>' + data.coupon_code + '</code>' +
|
|
248
|
+
'<button class="bio-copy-btn" data-code="' + data.coupon_code + '" title="Kopyala"><i class="fa fa-copy"></i></button>' +
|
|
249
|
+
'</div>' +
|
|
211
250
|
(data.redirect_url
|
|
212
|
-
? '<a href="' + data.redirect_url + '" target="_blank" rel="noopener" class="
|
|
251
|
+
? '<a href="' + data.redirect_url + '" target="_blank" rel="noopener" class="bio-go-link">Siteye Git <i class="fa fa-arrow-right"></i></a>'
|
|
213
252
|
: '') +
|
|
214
253
|
'</div>'
|
|
215
254
|
);
|
|
216
|
-
$('.biogrenci-copy-btn').last().on('click', function () {
|
|
217
|
-
navigator.clipboard.writeText($(this).attr('data-code'));
|
|
218
|
-
alerts.success('Kopyalandı!');
|
|
219
|
-
});
|
|
220
255
|
} else if (data.qr_code) {
|
|
221
|
-
$btn.replaceWith(
|
|
256
|
+
$btn.replaceWith(
|
|
257
|
+
'<div class="bio-claim-result bio-qr-box">' + data.qr_code + '</div>'
|
|
258
|
+
);
|
|
222
259
|
} else if (data.redirect_url) {
|
|
223
260
|
window.open(data.redirect_url, '_blank');
|
|
224
|
-
$btn.html('<i class="fa fa-check"></i> Açıldı')
|
|
261
|
+
$btn.addClass('bio-cta-done').html('<i class="fa fa-check"></i> Açıldı');
|
|
225
262
|
}
|
|
226
263
|
})
|
|
227
|
-
.catch(function (
|
|
228
|
-
console.error('[sso-biogrenci] Claim error:', err);
|
|
264
|
+
.catch(function () {
|
|
229
265
|
alerts.error('Bir hata oluştu');
|
|
230
|
-
$btn.prop('disabled', false).
|
|
266
|
+
$btn.prop('disabled', false).text(originalText);
|
|
231
267
|
});
|
|
232
268
|
}
|
|
233
269
|
|