nodebb-plugin-sso-biogrenci 1.0.0
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 +326 -0
- package/package.json +20 -0
- package/plugin.json +18 -0
- package/static/style.less +331 -0
- package/static/templates/account/biogrenci.tpl +83 -0
- package/static/templates/admin/plugins/sso-biogrenci.tpl +39 -0
- package/static/templates/biogrenci-firsatlar.tpl +198 -0
package/library.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const passport = require('passport');
|
|
4
|
+
const OAuth2Strategy = require('passport-oauth2');
|
|
5
|
+
const nconf = require.main.require('nconf');
|
|
6
|
+
const user = require.main.require('./src/user');
|
|
7
|
+
const db = require.main.require('./src/database');
|
|
8
|
+
const meta = require.main.require('./src/meta');
|
|
9
|
+
const groups = require.main.require('./src/groups');
|
|
10
|
+
const authenticationController = require.main.require('./src/controllers/authentication');
|
|
11
|
+
|
|
12
|
+
const constants = {
|
|
13
|
+
name: 'biogrenci',
|
|
14
|
+
displayName: "bi'öğrenci",
|
|
15
|
+
icon: 'fa-graduation-cap',
|
|
16
|
+
scope: 'openid profile email student_status education opportunities.read',
|
|
17
|
+
authorizationURL: 'https://biogrenci.com/oauth/authorize',
|
|
18
|
+
tokenURL: 'https://biogrenci.com/api/oauth/token',
|
|
19
|
+
userInfoURL: 'https://biogrenci.com/api/oauth/userinfo',
|
|
20
|
+
partnerAPIBase: 'https://biogrenci.com/api/partner',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const plugin = {
|
|
24
|
+
settings: {
|
|
25
|
+
clientId: '019ccf3d-0032-70e0-bf69-474e3f4dd2fa',
|
|
26
|
+
clientSecret: '0bpVolqxVtNGcrZuVWtWjNfKr36EABj8zzj61e6i',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
plugin.init = async function (params) {
|
|
31
|
+
const { router, middleware } = params;
|
|
32
|
+
|
|
33
|
+
// Admin settings page
|
|
34
|
+
router.get('/admin/plugins/sso-biogrenci', middleware.admin.buildHeader, renderAdmin);
|
|
35
|
+
router.get('/api/admin/plugins/sso-biogrenci', renderAdmin);
|
|
36
|
+
|
|
37
|
+
// Firsatlar (discounts) page
|
|
38
|
+
router.get('/firsatlar', middleware.buildHeader, renderFirsatlar);
|
|
39
|
+
router.get('/api/firsatlar', renderFirsatlar);
|
|
40
|
+
|
|
41
|
+
// API: fetch opportunities from bi'ogrenci
|
|
42
|
+
router.get('/api/biogrenci/firsatlar', async (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
const data = await fetchOpportunities(req.query);
|
|
45
|
+
res.json(data);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
res.status(500).json({ error: err.message });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// API: claim an opportunity
|
|
52
|
+
router.post('/api/biogrenci/firsatlar/:id/claim', async (req, res) => {
|
|
53
|
+
if (!req.uid) {
|
|
54
|
+
return res.status(401).json({ error: 'Giriş yapmanız gerekiyor' });
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const accessToken = await db.getObjectField(`biogrenci:uid:${req.uid}`, 'accessToken');
|
|
58
|
+
if (!accessToken) {
|
|
59
|
+
return res.status(403).json({ error: "bi'öğrenci hesabınızla giriş yapmalısınız" });
|
|
60
|
+
}
|
|
61
|
+
const response = await fetch(`${constants.partnerAPIBase}/opportunities/${req.params.id}/claim`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
64
|
+
});
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
res.json(data);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
res.status(500).json({ error: err.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Profile: link/unlink bi'öğrenci account
|
|
73
|
+
router.get('/api/biogrenci/status', async (req, res) => {
|
|
74
|
+
if (!req.uid) {
|
|
75
|
+
return res.json({ linked: false });
|
|
76
|
+
}
|
|
77
|
+
const data = await db.getObject(`biogrenci:uid:${req.uid}`);
|
|
78
|
+
if (data && data.biogrenciId) {
|
|
79
|
+
const education = await user.getUserField(req.uid, 'biogrenci_education');
|
|
80
|
+
const verified = await user.getUserField(req.uid, 'biogrenci_verified');
|
|
81
|
+
return res.json({
|
|
82
|
+
linked: true,
|
|
83
|
+
biogrenciId: data.biogrenciId,
|
|
84
|
+
education: education || null,
|
|
85
|
+
verified: !!verified,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
res.json({ linked: false });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
router.post('/api/biogrenci/unlink', async (req, res) => {
|
|
92
|
+
if (!req.uid) {
|
|
93
|
+
return res.status(401).json({ error: 'Giriş yapmanız gerekiyor' });
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const data = await db.getObject(`biogrenci:uid:${req.uid}`);
|
|
97
|
+
if (data && data.biogrenciId) {
|
|
98
|
+
await db.deleteObjectField('biogrenci:id', data.biogrenciId);
|
|
99
|
+
}
|
|
100
|
+
await db.delete(`biogrenci:uid:${req.uid}`);
|
|
101
|
+
await db.delete(`user:${req.uid}:biogrenci`);
|
|
102
|
+
await user.setUserField(req.uid, 'biogrenci_education', '');
|
|
103
|
+
await user.setUserField(req.uid, 'biogrenci_verified', false);
|
|
104
|
+
await groups.leave('Doğrulanmış Öğrenciler', req.uid);
|
|
105
|
+
res.json({ success: true });
|
|
106
|
+
} catch (err) {
|
|
107
|
+
res.status(500).json({ error: err.message });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Load settings from DB
|
|
112
|
+
const settings = await meta.settings.get('sso-biogrenci');
|
|
113
|
+
if (settings.clientId) plugin.settings.clientId = settings.clientId;
|
|
114
|
+
if (settings.clientSecret) plugin.settings.clientSecret = settings.clientSecret;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
async function renderAdmin(req, res) {
|
|
118
|
+
res.render('admin/plugins/sso-biogrenci', {
|
|
119
|
+
title: "bi'öğrenci SSO",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function renderFirsatlar(req, res) {
|
|
124
|
+
res.render('biogrenci-firsatlar', {
|
|
125
|
+
title: 'Öğrenci Fırsatları',
|
|
126
|
+
uid: req.uid || 0,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function fetchOpportunities(query) {
|
|
131
|
+
const params = new URLSearchParams();
|
|
132
|
+
if (query.category_id) params.set('category_id', query.category_id);
|
|
133
|
+
if (query.brand_id) params.set('brand_id', query.brand_id);
|
|
134
|
+
if (query.type) params.set('type', query.type);
|
|
135
|
+
if (query.search) params.set('search', query.search);
|
|
136
|
+
if (query.sort) params.set('sort', query.sort || 'featured');
|
|
137
|
+
params.set('page', query.page || 1);
|
|
138
|
+
params.set('per_page', query.per_page || 20);
|
|
139
|
+
|
|
140
|
+
const response = await fetch(`${constants.partnerAPIBase}/opportunities?${params.toString()}`);
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`Partner API error: ${response.status}`);
|
|
143
|
+
}
|
|
144
|
+
return response.json();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
plugin.getStrategy = async function (strategies) {
|
|
148
|
+
if (!plugin.settings.clientId || !plugin.settings.clientSecret) {
|
|
149
|
+
return strategies;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const callbackURL = nconf.get('url') + '/auth/biogrenci/callback';
|
|
153
|
+
|
|
154
|
+
const strategy = new OAuth2Strategy(
|
|
155
|
+
{
|
|
156
|
+
authorizationURL: constants.authorizationURL,
|
|
157
|
+
tokenURL: constants.tokenURL,
|
|
158
|
+
clientID: plugin.settings.clientId,
|
|
159
|
+
clientSecret: plugin.settings.clientSecret,
|
|
160
|
+
callbackURL: callbackURL,
|
|
161
|
+
scope: constants.scope,
|
|
162
|
+
passReqToCallback: true,
|
|
163
|
+
},
|
|
164
|
+
async (req, accessToken, refreshToken, params, profile, done) => {
|
|
165
|
+
try {
|
|
166
|
+
// Fetch user info from bi'ogrenci
|
|
167
|
+
const response = await fetch(constants.userInfoURL, {
|
|
168
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
return done(new Error('Kullanıcı bilgisi alınamadı'));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const biogrenciUser = await response.json();
|
|
176
|
+
|
|
177
|
+
const handleUser = async function (uid) {
|
|
178
|
+
// Store access/refresh tokens
|
|
179
|
+
await db.setObject(`biogrenci:uid:${uid}`, {
|
|
180
|
+
accessToken,
|
|
181
|
+
refreshToken: refreshToken || '',
|
|
182
|
+
biogrenciId: biogrenciUser.sub,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Store education info in user data
|
|
186
|
+
if (biogrenciUser.education) {
|
|
187
|
+
const edu = biogrenciUser.education;
|
|
188
|
+
const eduString = [edu.university, edu.faculty, edu.department, edu.class_level]
|
|
189
|
+
.filter(Boolean)
|
|
190
|
+
.join(' - ');
|
|
191
|
+
await user.setUserField(uid, 'biogrenci_education', eduString);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (biogrenciUser.student_verified) {
|
|
195
|
+
await user.setUserField(uid, 'biogrenci_verified', true);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Add verified students to a group
|
|
199
|
+
if (biogrenciUser.student_verified) {
|
|
200
|
+
const groupExists = await groups.exists('Doğrulanmış Öğrenciler');
|
|
201
|
+
if (!groupExists) {
|
|
202
|
+
await groups.create({
|
|
203
|
+
name: 'Doğrulanmış Öğrenciler',
|
|
204
|
+
description: "bi'öğrenci ile doğrulanmış öğrenciler",
|
|
205
|
+
hidden: 0,
|
|
206
|
+
private: 1,
|
|
207
|
+
disableJoinRequests: 1,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
await groups.join('Doğrulanmış Öğrenciler', uid);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
done(null, uid);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Check if user already linked
|
|
217
|
+
const existingUid = await plugin.getUidByBiogrenciId(biogrenciUser.sub);
|
|
218
|
+
if (existingUid) {
|
|
219
|
+
return handleUser(existingUid);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if logged-in user wants to link account
|
|
223
|
+
if (req.uid) {
|
|
224
|
+
await plugin.linkAccount(req.uid, biogrenciUser.sub);
|
|
225
|
+
return handleUser(req.uid);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check if email matches existing user
|
|
229
|
+
if (biogrenciUser.email) {
|
|
230
|
+
const existingEmailUid = await user.getUidByEmail(biogrenciUser.email);
|
|
231
|
+
if (existingEmailUid) {
|
|
232
|
+
await plugin.linkAccount(existingEmailUid, biogrenciUser.sub);
|
|
233
|
+
return handleUser(existingEmailUid);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Create new user
|
|
238
|
+
const newUid = await user.create({
|
|
239
|
+
username: biogrenciUser.first_name
|
|
240
|
+
? `${biogrenciUser.first_name}${biogrenciUser.last_name ? '_' + biogrenciUser.last_name : ''}`
|
|
241
|
+
: `biogrenci_${biogrenciUser.sub}`,
|
|
242
|
+
email: biogrenciUser.email || undefined,
|
|
243
|
+
fullname: biogrenciUser.name || undefined,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
await plugin.linkAccount(newUid, biogrenciUser.sub);
|
|
247
|
+
|
|
248
|
+
// Set avatar if available
|
|
249
|
+
if (biogrenciUser.avatar) {
|
|
250
|
+
await user.setUserField(newUid, 'picture', biogrenciUser.avatar);
|
|
251
|
+
await user.setUserField(newUid, 'uploadedpicture', biogrenciUser.avatar);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
handleUser(newUid);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
done(err);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
strategy.userProfile = function (accessToken, done) {
|
|
262
|
+
done(null, {});
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
passport.use(constants.name, strategy);
|
|
266
|
+
|
|
267
|
+
strategies.push({
|
|
268
|
+
name: constants.name,
|
|
269
|
+
url: '/auth/biogrenci',
|
|
270
|
+
callbackURL: '/auth/biogrenci/callback',
|
|
271
|
+
icon: constants.icon,
|
|
272
|
+
color: '#1a73e8',
|
|
273
|
+
displayName: constants.displayName,
|
|
274
|
+
scope: constants.scope,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
return strategies;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
plugin.listStrategy = async function (strategies) {
|
|
281
|
+
strategies.push({
|
|
282
|
+
name: constants.name,
|
|
283
|
+
displayName: constants.displayName,
|
|
284
|
+
icon: constants.icon,
|
|
285
|
+
});
|
|
286
|
+
return strategies;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
plugin.addAdminNavigation = async function (header) {
|
|
290
|
+
header.plugins.push({
|
|
291
|
+
route: '/plugins/sso-biogrenci',
|
|
292
|
+
icon: constants.icon,
|
|
293
|
+
name: "bi'öğrenci SSO",
|
|
294
|
+
});
|
|
295
|
+
return header;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
plugin.addProfileMenuItem = async function (data) {
|
|
299
|
+
data.links.push({
|
|
300
|
+
id: 'biogrenci',
|
|
301
|
+
route: 'biogrenci',
|
|
302
|
+
icon: constants.icon,
|
|
303
|
+
name: "bi'öğrenci",
|
|
304
|
+
visibility: { self: true },
|
|
305
|
+
});
|
|
306
|
+
return data;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
plugin.addCustomFields = async function (data) {
|
|
310
|
+
if (data.userData) {
|
|
311
|
+
const biogrenciData = await db.getObject(`biogrenci:uid:${data.userData.uid}`);
|
|
312
|
+
data.userData.biogrenciLinked = !!(biogrenciData && biogrenciData.biogrenciId);
|
|
313
|
+
}
|
|
314
|
+
return data;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
plugin.linkAccount = async function (uid, biogrenciId) {
|
|
318
|
+
await db.setObjectField('biogrenci:id', biogrenciId, uid);
|
|
319
|
+
await db.setObjectField(`user:${uid}:biogrenci`, 'biogrenciId', biogrenciId);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
plugin.getUidByBiogrenciId = async function (biogrenciId) {
|
|
323
|
+
return db.getObjectField('biogrenci:id', biogrenciId);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
module.exports = plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nodebb-plugin-sso-biogrenci",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "bi'öğrenci OAuth2 SSO login & öğrenci indirimleri for NodeBB",
|
|
5
|
+
"main": "library.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"nodebb",
|
|
8
|
+
"plugin",
|
|
9
|
+
"sso",
|
|
10
|
+
"biogrenci",
|
|
11
|
+
"oauth2"
|
|
12
|
+
],
|
|
13
|
+
"nbbpm": {
|
|
14
|
+
"compatibility": "^3.2.0"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"passport-oauth2": "^1.8.0"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT"
|
|
20
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nodebb-plugin-sso-biogrenci",
|
|
3
|
+
"url": "",
|
|
4
|
+
"library": "./library.js",
|
|
5
|
+
"staticDirs": {
|
|
6
|
+
"static": "./static"
|
|
7
|
+
},
|
|
8
|
+
"less": ["static/style.less"],
|
|
9
|
+
"hooks": [
|
|
10
|
+
{ "hook": "filter:auth.init", "method": "getStrategy" },
|
|
11
|
+
{ "hook": "filter:auth.list", "method": "listStrategy" },
|
|
12
|
+
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
|
|
13
|
+
{ "hook": "static:app.load", "method": "init" },
|
|
14
|
+
{ "hook": "filter:user.profileMenu", "method": "addProfileMenuItem" },
|
|
15
|
+
{ "hook": "filter:user.customFields", "method": "addCustomFields" }
|
|
16
|
+
],
|
|
17
|
+
"templates": "static/templates"
|
|
18
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// bi'öğrenci Fırsatlar Page
|
|
2
|
+
.biogrenci-page {
|
|
3
|
+
max-width: 1200px;
|
|
4
|
+
margin: 0 auto;
|
|
5
|
+
padding: 20px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.biogrenci-header {
|
|
9
|
+
text-align: center;
|
|
10
|
+
margin-bottom: 30px;
|
|
11
|
+
|
|
12
|
+
.biogrenci-logo {
|
|
13
|
+
width: 48px;
|
|
14
|
+
height: 48px;
|
|
15
|
+
margin-bottom: 10px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
h2 {
|
|
19
|
+
margin: 0 0 5px;
|
|
20
|
+
font-size: 28px;
|
|
21
|
+
font-weight: 700;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.biogrenci-subtitle {
|
|
25
|
+
color: #666;
|
|
26
|
+
margin: 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.biogrenci-filters {
|
|
31
|
+
display: flex;
|
|
32
|
+
gap: 12px;
|
|
33
|
+
align-items: center;
|
|
34
|
+
margin-bottom: 24px;
|
|
35
|
+
flex-wrap: wrap;
|
|
36
|
+
|
|
37
|
+
.biogrenci-search {
|
|
38
|
+
flex: 1;
|
|
39
|
+
min-width: 200px;
|
|
40
|
+
|
|
41
|
+
input {
|
|
42
|
+
border-radius: 8px;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.biogrenci-filter-buttons {
|
|
47
|
+
display: flex;
|
|
48
|
+
gap: 6px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#biogrenci-sort {
|
|
52
|
+
width: auto;
|
|
53
|
+
border-radius: 8px;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.biogrenci-filter {
|
|
58
|
+
border-radius: 20px !important;
|
|
59
|
+
padding: 6px 16px;
|
|
60
|
+
font-size: 13px;
|
|
61
|
+
border: 1px solid #ddd;
|
|
62
|
+
background: #fff;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
transition: all 0.2s;
|
|
65
|
+
|
|
66
|
+
&.active,
|
|
67
|
+
&:hover {
|
|
68
|
+
background: #1a73e8;
|
|
69
|
+
color: #fff;
|
|
70
|
+
border-color: #1a73e8;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.biogrenci-grid {
|
|
75
|
+
display: grid;
|
|
76
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
77
|
+
gap: 20px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.biogrenci-card {
|
|
81
|
+
border: 1px solid #e0e0e0;
|
|
82
|
+
border-radius: 12px;
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
background: #fff;
|
|
85
|
+
transition: box-shadow 0.2s, transform 0.2s;
|
|
86
|
+
|
|
87
|
+
&:hover {
|
|
88
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
|
89
|
+
transform: translateY(-2px);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.biogrenci-card-img {
|
|
93
|
+
height: 160px;
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
background: #f5f5f5;
|
|
96
|
+
|
|
97
|
+
img {
|
|
98
|
+
width: 100%;
|
|
99
|
+
height: 100%;
|
|
100
|
+
object-fit: cover;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.biogrenci-card-body {
|
|
105
|
+
padding: 16px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.biogrenci-card-top {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 8px;
|
|
112
|
+
margin-bottom: 8px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.biogrenci-card-title {
|
|
116
|
+
font-size: 16px;
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
margin: 0 0 8px;
|
|
119
|
+
line-height: 1.3;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.biogrenci-card-desc {
|
|
123
|
+
font-size: 13px;
|
|
124
|
+
color: #666;
|
|
125
|
+
margin: 0 0 12px;
|
|
126
|
+
display: -webkit-box;
|
|
127
|
+
-webkit-line-clamp: 2;
|
|
128
|
+
-webkit-box-orient: vertical;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.biogrenci-claim-btn {
|
|
133
|
+
width: 100%;
|
|
134
|
+
border-radius: 8px;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
background: #1a73e8;
|
|
137
|
+
border: none;
|
|
138
|
+
padding: 10px;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.biogrenci-badge {
|
|
143
|
+
font-size: 11px;
|
|
144
|
+
padding: 3px 10px;
|
|
145
|
+
border-radius: 12px;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
|
|
148
|
+
&.badge-kupon {
|
|
149
|
+
background: #e8f5e9;
|
|
150
|
+
color: #2e7d32;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&.badge-qr {
|
|
154
|
+
background: #e3f2fd;
|
|
155
|
+
color: #1565c0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&.badge-affiliate {
|
|
159
|
+
background: #fff3e0;
|
|
160
|
+
color: #e65100;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.biogrenci-brand {
|
|
165
|
+
font-size: 12px;
|
|
166
|
+
color: #999;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.biogrenci-coupon-result {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
gap: 8px;
|
|
173
|
+
flex-wrap: wrap;
|
|
174
|
+
|
|
175
|
+
.biogrenci-coupon-code {
|
|
176
|
+
background: #f0f7ff;
|
|
177
|
+
border: 2px dashed #1a73e8;
|
|
178
|
+
padding: 8px 16px;
|
|
179
|
+
border-radius: 8px;
|
|
180
|
+
font-weight: 700;
|
|
181
|
+
font-size: 16px;
|
|
182
|
+
letter-spacing: 1px;
|
|
183
|
+
font-family: monospace;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.biogrenci-qr-result {
|
|
188
|
+
text-align: center;
|
|
189
|
+
padding: 10px;
|
|
190
|
+
|
|
191
|
+
svg {
|
|
192
|
+
max-width: 200px;
|
|
193
|
+
height: auto;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.biogrenci-loading,
|
|
198
|
+
.biogrenci-empty {
|
|
199
|
+
grid-column: 1 / -1;
|
|
200
|
+
text-align: center;
|
|
201
|
+
padding: 60px 20px;
|
|
202
|
+
color: #999;
|
|
203
|
+
font-size: 16px;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.biogrenci-pagination {
|
|
207
|
+
display: flex;
|
|
208
|
+
justify-content: center;
|
|
209
|
+
align-items: center;
|
|
210
|
+
gap: 16px;
|
|
211
|
+
margin-top: 30px;
|
|
212
|
+
padding: 20px 0;
|
|
213
|
+
|
|
214
|
+
#biogrenci-page-info {
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Profile Section
|
|
220
|
+
.biogrenci-profile-section {
|
|
221
|
+
max-width: 600px;
|
|
222
|
+
margin: 0 auto;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.biogrenci-profile-card {
|
|
226
|
+
background: #fff;
|
|
227
|
+
border: 1px solid #e0e0e0;
|
|
228
|
+
border-radius: 12px;
|
|
229
|
+
padding: 30px;
|
|
230
|
+
text-align: center;
|
|
231
|
+
|
|
232
|
+
.biogrenci-profile-icon {
|
|
233
|
+
width: 64px;
|
|
234
|
+
height: 64px;
|
|
235
|
+
background: #e8f0fe;
|
|
236
|
+
border-radius: 50%;
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
justify-content: center;
|
|
240
|
+
margin: 0 auto 16px;
|
|
241
|
+
|
|
242
|
+
i {
|
|
243
|
+
font-size: 28px;
|
|
244
|
+
color: #1a73e8;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
h3 {
|
|
249
|
+
margin: 0 0 20px;
|
|
250
|
+
font-weight: 600;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.biogrenci-linked {
|
|
255
|
+
.biogrenci-linked-badge {
|
|
256
|
+
display: inline-block;
|
|
257
|
+
background: #e8f5e9;
|
|
258
|
+
color: #2e7d32;
|
|
259
|
+
padding: 8px 20px;
|
|
260
|
+
border-radius: 20px;
|
|
261
|
+
font-weight: 600;
|
|
262
|
+
font-size: 15px;
|
|
263
|
+
margin-bottom: 12px;
|
|
264
|
+
|
|
265
|
+
i { margin-right: 6px; }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.biogrenci-verified-badge {
|
|
269
|
+
display: inline-block;
|
|
270
|
+
background: #fff3e0;
|
|
271
|
+
color: #e65100;
|
|
272
|
+
padding: 6px 16px;
|
|
273
|
+
border-radius: 20px;
|
|
274
|
+
font-size: 13px;
|
|
275
|
+
font-weight: 600;
|
|
276
|
+
margin-bottom: 12px;
|
|
277
|
+
|
|
278
|
+
i { margin-right: 4px; }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.biogrenci-education {
|
|
282
|
+
color: #555;
|
|
283
|
+
font-size: 14px;
|
|
284
|
+
margin-bottom: 8px;
|
|
285
|
+
|
|
286
|
+
i {
|
|
287
|
+
margin-right: 6px;
|
|
288
|
+
color: #999;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
hr {
|
|
293
|
+
margin: 20px 0;
|
|
294
|
+
border-color: #eee;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.biogrenci-not-linked {
|
|
299
|
+
p {
|
|
300
|
+
margin: 0 0 12px;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.btn-primary {
|
|
304
|
+
background: #1a73e8;
|
|
305
|
+
border: none;
|
|
306
|
+
border-radius: 8px;
|
|
307
|
+
padding: 10px 24px;
|
|
308
|
+
font-weight: 600;
|
|
309
|
+
|
|
310
|
+
i { margin-right: 6px; }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Responsive
|
|
315
|
+
@media (max-width: 600px) {
|
|
316
|
+
.biogrenci-filters {
|
|
317
|
+
flex-direction: column;
|
|
318
|
+
|
|
319
|
+
.biogrenci-search {
|
|
320
|
+
width: 100%;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.biogrenci-filter-buttons {
|
|
324
|
+
flex-wrap: wrap;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.biogrenci-grid {
|
|
329
|
+
grid-template-columns: 1fr;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!-- IMPORT partials/account/header.tpl -->
|
|
2
|
+
|
|
3
|
+
<div class="biogrenci-profile-section">
|
|
4
|
+
<div class="biogrenci-profile-card">
|
|
5
|
+
<div class="biogrenci-profile-icon">
|
|
6
|
+
<i class="fa fa-graduation-cap"></i>
|
|
7
|
+
</div>
|
|
8
|
+
<h3>bi'öğrenci Hesap Bağlantısı</h3>
|
|
9
|
+
|
|
10
|
+
<div id="biogrenci-status" class="biogrenci-status-loading">
|
|
11
|
+
<i class="fa fa-spinner fa-spin"></i> Durum kontrol ediliyor...
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
(function () {
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
function loadStatus() {
|
|
21
|
+
fetch(config.relative_path + '/api/biogrenci/status')
|
|
22
|
+
.then(function (r) { return r.json(); })
|
|
23
|
+
.then(function (data) {
|
|
24
|
+
var el = document.getElementById('biogrenci-status');
|
|
25
|
+
if (data.linked) {
|
|
26
|
+
el.innerHTML =
|
|
27
|
+
'<div class="biogrenci-linked">' +
|
|
28
|
+
'<div class="biogrenci-linked-badge">' +
|
|
29
|
+
'<i class="fa fa-check-circle"></i> Hesap Bağlı' +
|
|
30
|
+
'</div>' +
|
|
31
|
+
(data.verified
|
|
32
|
+
? '<div class="biogrenci-verified-badge"><i class="fa fa-shield"></i> Doğrulanmış Öğrenci</div>'
|
|
33
|
+
: '') +
|
|
34
|
+
(data.education
|
|
35
|
+
? '<div class="biogrenci-education"><i class="fa fa-university"></i> ' + data.education + '</div>'
|
|
36
|
+
: '') +
|
|
37
|
+
'<hr/>' +
|
|
38
|
+
'<button id="biogrenci-unlink" class="btn btn-danger btn-sm">' +
|
|
39
|
+
'<i class="fa fa-unlink"></i> Bağlantıyı Kaldır' +
|
|
40
|
+
'</button>' +
|
|
41
|
+
'</div>';
|
|
42
|
+
|
|
43
|
+
document.getElementById('biogrenci-unlink').addEventListener('click', unlinkAccount);
|
|
44
|
+
} else {
|
|
45
|
+
el.innerHTML =
|
|
46
|
+
'<div class="biogrenci-not-linked">' +
|
|
47
|
+
'<p>bi\'öğrenci hesabınız henüz bağlı değil.</p>' +
|
|
48
|
+
'<p class="text-muted">Hesabınızı bağlayarak öğrenci doğrulaması yapabilir ve öğrenci fırsatlarından yararlanabilirsiniz.</p>' +
|
|
49
|
+
'<a href="' + config.relative_path + '/auth/biogrenci" class="btn btn-primary">' +
|
|
50
|
+
'<i class="fa fa-graduation-cap"></i> bi\'öğrenci Hesabını Bağla' +
|
|
51
|
+
'</a>' +
|
|
52
|
+
'</div>';
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function unlinkAccount() {
|
|
58
|
+
if (!confirm("bi'öğrenci hesap bağlantısını kaldırmak istediğinize emin misiniz?")) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
fetch(config.relative_path + '/api/biogrenci/unlink', {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'x-csrf-token': config.csrf_token,
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
.then(function (r) { return r.json(); })
|
|
69
|
+
.then(function (res) {
|
|
70
|
+
if (res.success) {
|
|
71
|
+
app.alertSuccess('bi\'öğrenci bağlantısı kaldırıldı');
|
|
72
|
+
loadStatus();
|
|
73
|
+
} else {
|
|
74
|
+
app.alertError(res.error || 'Bir hata oluştu');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
loadStatus();
|
|
80
|
+
})();
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<!-- IMPORT partials/account/footer.tpl -->
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<div class="col-lg-9">
|
|
3
|
+
<div class="panel panel-default">
|
|
4
|
+
<div class="panel-heading">bi'öğrenci SSO Ayarları</div>
|
|
5
|
+
<div class="panel-body">
|
|
6
|
+
<form id="sso-biogrenci-settings" role="form">
|
|
7
|
+
<div class="form-group">
|
|
8
|
+
<label for="clientId">Client ID</label>
|
|
9
|
+
<input type="text" id="clientId" name="clientId" class="form-control" placeholder="bi'öğrenci Client ID">
|
|
10
|
+
</div>
|
|
11
|
+
<div class="form-group">
|
|
12
|
+
<label for="clientSecret">Client Secret</label>
|
|
13
|
+
<input type="password" id="clientSecret" name="clientSecret" class="form-control" placeholder="bi'öğrenci Client Secret">
|
|
14
|
+
</div>
|
|
15
|
+
<hr/>
|
|
16
|
+
<p class="help-block">
|
|
17
|
+
Redirect URI olarak şunu bi'öğrenci paneline ekleyin:<br>
|
|
18
|
+
<code>{callbackURL}</code>
|
|
19
|
+
</p>
|
|
20
|
+
</form>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<button id="save" class="floating-button mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect mdl-button--colored">
|
|
27
|
+
<i class="material-icons">save</i>
|
|
28
|
+
</button>
|
|
29
|
+
|
|
30
|
+
<script>
|
|
31
|
+
require(['settings'], function (Settings) {
|
|
32
|
+
Settings.load('sso-biogrenci', $('.acp-page-container form'));
|
|
33
|
+
$('#save').on('click', function () {
|
|
34
|
+
Settings.save('sso-biogrenci', $('.acp-page-container form'), function () {
|
|
35
|
+
app.alertSuccess('Ayarlar kaydedildi');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
</script>
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<!-- IMPORT partials/breadcrumbs.tpl -->
|
|
2
|
+
|
|
3
|
+
<div id="biogrenci-firsatlar" class="biogrenci-page">
|
|
4
|
+
<div class="biogrenci-header">
|
|
5
|
+
<img src="https://biogrenci.com/favicon.ico" alt="bi'öğrenci" class="biogrenci-logo">
|
|
6
|
+
<h2>Öğrenci Fırsatları</h2>
|
|
7
|
+
<p class="biogrenci-subtitle">bi'öğrenci ile sunulan öğrenci indirim ve fırsatları</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="biogrenci-filters">
|
|
11
|
+
<div class="biogrenci-search">
|
|
12
|
+
<input type="text" id="biogrenci-search" class="form-control" placeholder="Fırsat ara...">
|
|
13
|
+
</div>
|
|
14
|
+
<div class="biogrenci-filter-buttons">
|
|
15
|
+
<button class="btn btn-outline biogrenci-filter active" data-type="">Tümü</button>
|
|
16
|
+
<button class="btn btn-outline biogrenci-filter" data-type="kupon">Kuponlar</button>
|
|
17
|
+
<button class="btn btn-outline biogrenci-filter" data-type="qr">QR Kod</button>
|
|
18
|
+
<button class="btn btn-outline biogrenci-filter" data-type="affiliate">Linkler</button>
|
|
19
|
+
</div>
|
|
20
|
+
<select id="biogrenci-sort" class="form-control">
|
|
21
|
+
<option value="featured">Öne Çıkan</option>
|
|
22
|
+
<option value="popular">Popüler</option>
|
|
23
|
+
<option value="newest">En Yeni</option>
|
|
24
|
+
<option value="alphabetical">A-Z</option>
|
|
25
|
+
</select>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div id="biogrenci-list" class="biogrenci-grid">
|
|
29
|
+
<div class="biogrenci-loading">
|
|
30
|
+
<i class="fa fa-spinner fa-spin"></i> Fırsatlar yükleniyor...
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div id="biogrenci-pagination" class="biogrenci-pagination" style="display:none;">
|
|
35
|
+
<button id="biogrenci-prev" class="btn btn-default" disabled>← Önceki</button>
|
|
36
|
+
<span id="biogrenci-page-info"></span>
|
|
37
|
+
<button id="biogrenci-next" class="btn btn-default">Sonraki →</button>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<script>
|
|
42
|
+
(function () {
|
|
43
|
+
'use strict';
|
|
44
|
+
|
|
45
|
+
let currentPage = 1;
|
|
46
|
+
let currentType = '';
|
|
47
|
+
let currentSort = 'featured';
|
|
48
|
+
let currentSearch = '';
|
|
49
|
+
let searchTimeout = null;
|
|
50
|
+
|
|
51
|
+
function loadOpportunities() {
|
|
52
|
+
const list = document.getElementById('biogrenci-list');
|
|
53
|
+
list.innerHTML = '<div class="biogrenci-loading"><i class="fa fa-spinner fa-spin"></i> Fırsatlar yükleniyor...</div>';
|
|
54
|
+
|
|
55
|
+
const params = new URLSearchParams({
|
|
56
|
+
page: currentPage,
|
|
57
|
+
per_page: 20,
|
|
58
|
+
sort: currentSort,
|
|
59
|
+
});
|
|
60
|
+
if (currentType) params.set('type', currentType);
|
|
61
|
+
if (currentSearch) params.set('search', currentSearch);
|
|
62
|
+
|
|
63
|
+
fetch(config.relative_path + '/api/biogrenci/firsatlar?' + params.toString())
|
|
64
|
+
.then(function (r) { return r.json(); })
|
|
65
|
+
.then(function (res) {
|
|
66
|
+
if (!res.data || !res.data.length) {
|
|
67
|
+
list.innerHTML = '<div class="biogrenci-empty">Fırsat bulunamadı</div>';
|
|
68
|
+
document.getElementById('biogrenci-pagination').style.display = 'none';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
renderOpportunities(res.data);
|
|
72
|
+
renderPagination(res.meta || {});
|
|
73
|
+
})
|
|
74
|
+
.catch(function (err) {
|
|
75
|
+
list.innerHTML = '<div class="biogrenci-empty">Fırsatlar yüklenirken hata oluştu</div>';
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderOpportunities(items) {
|
|
80
|
+
const list = document.getElementById('biogrenci-list');
|
|
81
|
+
list.innerHTML = items.map(function (item) {
|
|
82
|
+
var typeLabel = item.type === 'kupon' ? 'Kupon' : item.type === 'qr' ? 'QR Kod' : 'Link';
|
|
83
|
+
var typeBadge = item.type === 'kupon' ? 'badge-kupon' : item.type === 'qr' ? 'badge-qr' : 'badge-affiliate';
|
|
84
|
+
return '<div class="biogrenci-card">' +
|
|
85
|
+
(item.image ? '<div class="biogrenci-card-img"><img src="' + item.image + '" alt="' + (item.brand_name || '') + '"></div>' : '') +
|
|
86
|
+
'<div class="biogrenci-card-body">' +
|
|
87
|
+
'<div class="biogrenci-card-top">' +
|
|
88
|
+
'<span class="biogrenci-badge ' + typeBadge + '">' + typeLabel + '</span>' +
|
|
89
|
+
(item.brand_name ? '<span class="biogrenci-brand">' + item.brand_name + '</span>' : '') +
|
|
90
|
+
'</div>' +
|
|
91
|
+
'<h3 class="biogrenci-card-title">' + item.title + '</h3>' +
|
|
92
|
+
(item.description ? '<p class="biogrenci-card-desc">' + item.description + '</p>' : '') +
|
|
93
|
+
'<button class="btn btn-primary biogrenci-claim-btn" data-id="' + item.id + '">Fırsatı Al</button>' +
|
|
94
|
+
'</div>' +
|
|
95
|
+
'</div>';
|
|
96
|
+
}).join('');
|
|
97
|
+
|
|
98
|
+
// Bind claim buttons
|
|
99
|
+
list.querySelectorAll('.biogrenci-claim-btn').forEach(function (btn) {
|
|
100
|
+
btn.addEventListener('click', function () { claimOpportunity(this); });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderPagination(meta) {
|
|
105
|
+
var el = document.getElementById('biogrenci-pagination');
|
|
106
|
+
if (!meta.total || meta.total <= 20) {
|
|
107
|
+
el.style.display = 'none';
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
el.style.display = 'flex';
|
|
111
|
+
var totalPages = Math.ceil(meta.total / 20);
|
|
112
|
+
document.getElementById('biogrenci-page-info').textContent = currentPage + ' / ' + totalPages;
|
|
113
|
+
document.getElementById('biogrenci-prev').disabled = currentPage <= 1;
|
|
114
|
+
document.getElementById('biogrenci-next').disabled = currentPage >= totalPages;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function claimOpportunity(btn) {
|
|
118
|
+
var id = btn.getAttribute('data-id');
|
|
119
|
+
btn.disabled = true;
|
|
120
|
+
btn.textContent = 'Alınıyor...';
|
|
121
|
+
|
|
122
|
+
fetch(config.relative_path + '/api/biogrenci/firsatlar/' + id + '/claim', {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: {
|
|
125
|
+
'x-csrf-token': config.csrf_token,
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
.then(function (r) { return r.json(); })
|
|
130
|
+
.then(function (res) {
|
|
131
|
+
if (res.error) {
|
|
132
|
+
app.alertError(res.error);
|
|
133
|
+
btn.disabled = false;
|
|
134
|
+
btn.textContent = 'Fırsatı Al';
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (res.data && res.data.coupon_code) {
|
|
138
|
+
btn.outerHTML = '<div class="biogrenci-coupon-result">' +
|
|
139
|
+
'<span class="biogrenci-coupon-code">' + res.data.coupon_code + '</span>' +
|
|
140
|
+
'<button class="btn btn-sm btn-default biogrenci-copy-btn" onclick="navigator.clipboard.writeText(\'' + res.data.coupon_code + '\');app.alertSuccess(\'Kopyalandı!\')">Kopyala</button>' +
|
|
141
|
+
(res.data.redirect_url ? '<a href="' + res.data.redirect_url + '" target="_blank" class="btn btn-sm btn-link">Siteye Git →</a>' : '') +
|
|
142
|
+
'</div>';
|
|
143
|
+
} else if (res.data && res.data.qr_code) {
|
|
144
|
+
btn.outerHTML = '<div class="biogrenci-qr-result">' + res.data.qr_code + '</div>';
|
|
145
|
+
} else if (res.data && res.data.redirect_url) {
|
|
146
|
+
window.open(res.data.redirect_url, '_blank');
|
|
147
|
+
btn.textContent = 'Açıldı ✓';
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
.catch(function () {
|
|
151
|
+
app.alertError('Bir hata oluştu');
|
|
152
|
+
btn.disabled = false;
|
|
153
|
+
btn.textContent = 'Fırsatı Al';
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Filter buttons
|
|
158
|
+
document.querySelectorAll('.biogrenci-filter').forEach(function (btn) {
|
|
159
|
+
btn.addEventListener('click', function () {
|
|
160
|
+
document.querySelectorAll('.biogrenci-filter').forEach(function (b) { b.classList.remove('active'); });
|
|
161
|
+
this.classList.add('active');
|
|
162
|
+
currentType = this.getAttribute('data-type');
|
|
163
|
+
currentPage = 1;
|
|
164
|
+
loadOpportunities();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Sort
|
|
169
|
+
document.getElementById('biogrenci-sort').addEventListener('change', function () {
|
|
170
|
+
currentSort = this.value;
|
|
171
|
+
currentPage = 1;
|
|
172
|
+
loadOpportunities();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Search
|
|
176
|
+
document.getElementById('biogrenci-search').addEventListener('input', function () {
|
|
177
|
+
clearTimeout(searchTimeout);
|
|
178
|
+
var val = this.value;
|
|
179
|
+
searchTimeout = setTimeout(function () {
|
|
180
|
+
currentSearch = val;
|
|
181
|
+
currentPage = 1;
|
|
182
|
+
loadOpportunities();
|
|
183
|
+
}, 400);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Pagination
|
|
187
|
+
document.getElementById('biogrenci-prev').addEventListener('click', function () {
|
|
188
|
+
if (currentPage > 1) { currentPage--; loadOpportunities(); }
|
|
189
|
+
});
|
|
190
|
+
document.getElementById('biogrenci-next').addEventListener('click', function () {
|
|
191
|
+
currentPage++;
|
|
192
|
+
loadOpportunities();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Initial load
|
|
196
|
+
loadOpportunities();
|
|
197
|
+
})();
|
|
198
|
+
</script>
|