studyup 1.0.3 → 1.1.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.
Files changed (3) hide show
  1. package/index.html +5 -60
  2. package/package.json +1 -1
  3. package/server.js +0 -50
package/index.html CHANGED
@@ -7,7 +7,6 @@
7
7
  <!-- Material Design / Roboto Typography & Icons -->
8
8
  <link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap" rel="stylesheet">
9
9
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
10
-
11
10
  <style>
12
11
  :root {
13
12
  --primary-color: #3f51b5; /* Indigo */
@@ -23,7 +22,6 @@
23
22
  --shadow-elevation-2: 0 3px 1px -2px rgba(0,0,0,0.2), 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12);
24
23
  --shadow-elevation-4: 0 4px 5px -2px rgba(0,0,0,0.2), 0 7px 10px 1px rgba(0,0,0,0.14), 0 2px 16px 1px rgba(0,0,0,0.12);
25
24
  }
26
-
27
25
  body {
28
26
  font-family: 'Roboto', sans-serif;
29
27
  background-color: var(--background-color);
@@ -35,7 +33,6 @@
35
33
  min-height: 100vh;
36
34
  justify-content: space-between;
37
35
  }
38
-
39
36
  header {
40
37
  background-color: var(--primary-color);
41
38
  color: white;
@@ -45,7 +42,6 @@
45
42
  align-items: center;
46
43
  justify-content: space-between;
47
44
  }
48
-
49
45
  header h1 {
50
46
  margin: 0;
51
47
  font-size: 20px;
@@ -53,13 +49,11 @@
53
49
  letter-spacing: 0.5px;
54
50
  cursor: pointer;
55
51
  }
56
-
57
52
  .header-actions {
58
53
  display: flex;
59
54
  gap: 12px;
60
55
  align-items: center;
61
56
  }
62
-
63
57
  .view {
64
58
  display: none;
65
59
  flex: 1;
@@ -68,7 +62,6 @@
68
62
  .view.active {
69
63
  display: flex;
70
64
  }
71
-
72
65
  .container {
73
66
  max-width: 420px;
74
67
  width: 100%;
@@ -80,7 +73,6 @@
80
73
  align-items: center;
81
74
  justify-content: center;
82
75
  }
83
-
84
76
  .card {
85
77
  background-color: var(--card-background);
86
78
  border-radius: 2px;
@@ -93,7 +85,6 @@
93
85
  width: 100%;
94
86
  box-sizing: border-box;
95
87
  }
96
-
97
88
  .card-icon-wrapper {
98
89
  background-color: rgba(63, 81, 181, 0.1);
99
90
  color: var(--primary-color);
@@ -105,32 +96,27 @@
105
96
  justify-content: center;
106
97
  margin-bottom: 20px;
107
98
  }
108
-
109
99
  .card-icon-wrapper i {
110
100
  font-size: 32px;
111
101
  }
112
-
113
102
  .card-title {
114
103
  font-size: 24px;
115
104
  font-weight: 500;
116
105
  margin: 0 0 8px 0;
117
106
  color: var(--text-primary);
118
107
  }
119
-
120
108
  .card-subtitle {
121
109
  font-size: 14px;
122
110
  color: var(--text-secondary);
123
111
  margin: 0 0 24px 0;
124
112
  line-height: 1.4;
125
113
  }
126
-
127
114
  .input-container {
128
115
  position: relative;
129
116
  margin-bottom: 24px;
130
117
  width: 100%;
131
118
  text-align: left;
132
119
  }
133
-
134
120
  .input-container input {
135
121
  width: 100%;
136
122
  padding: 12px;
@@ -143,13 +129,11 @@
143
129
  background: transparent;
144
130
  transition: border-color 0.2s;
145
131
  }
146
-
147
132
  .input-container input:focus {
148
133
  border-color: var(--primary-color);
149
134
  border-width: 2px;
150
135
  padding: 11px;
151
136
  }
152
-
153
137
  .input-container label {
154
138
  position: absolute;
155
139
  left: 12px;
@@ -161,7 +145,6 @@
161
145
  background: white;
162
146
  padding: 0 4px;
163
147
  }
164
-
165
148
  .input-container input:focus ~ label,
166
149
  .input-container input:not(:placeholder-shown) ~ label {
167
150
  top: -10px;
@@ -169,7 +152,6 @@
169
152
  color: var(--primary-color);
170
153
  font-weight: 500;
171
154
  }
172
-
173
155
  #error-msg {
174
156
  color: var(--error-color);
175
157
  font-size: 13px;
@@ -177,7 +159,6 @@
177
159
  display: none;
178
160
  font-weight: 500;
179
161
  }
180
-
181
162
  .btn-raised {
182
163
  background-color: var(--primary-color);
183
164
  color: white;
@@ -194,26 +175,22 @@
194
175
  transition: background-color 0.2s, box-shadow 0.2s;
195
176
  letter-spacing: 0.5px;
196
177
  }
197
-
198
178
  .btn-raised:hover {
199
179
  background-color: var(--primary-dark);
200
180
  box-shadow: var(--shadow-elevation-4);
201
181
  }
202
-
203
182
  .btn-danger {
204
183
  background-color: var(--error-color);
205
184
  }
206
185
  .btn-danger:hover {
207
186
  background-color: #b71c1c;
208
187
  }
209
-
210
188
  .btn-warning {
211
189
  background-color: var(--warning-color);
212
190
  }
213
191
  .btn-warning:hover {
214
192
  background-color: #e65100;
215
193
  }
216
-
217
194
  .portal-container {
218
195
  max-width: 1000px;
219
196
  width: 100%;
@@ -225,7 +202,6 @@
225
202
  gap: 24px;
226
203
  flex: 1;
227
204
  }
228
-
229
205
  .portal-card {
230
206
  background-color: var(--card-background);
231
207
  border-radius: 2px;
@@ -235,11 +211,9 @@
235
211
  flex-direction: column;
236
212
  overflow: hidden;
237
213
  }
238
-
239
214
  .portal-card:hover {
240
215
  box-shadow: var(--shadow-elevation-4);
241
216
  }
242
-
243
217
  .card-media {
244
218
  height: 120px;
245
219
  display: flex;
@@ -247,33 +221,28 @@
247
221
  justify-content: center;
248
222
  color: white;
249
223
  }
250
-
251
224
  .card-content {
252
225
  padding: 24px;
253
226
  flex: 1;
254
227
  }
255
-
256
228
  .portal-card-title {
257
229
  font-size: 20px;
258
230
  font-weight: 500;
259
231
  margin: 0 0 8px 0;
260
232
  color: var(--text-primary);
261
233
  }
262
-
263
234
  .card-text {
264
235
  font-size: 14px;
265
236
  color: var(--text-secondary);
266
237
  line-height: 1.4;
267
238
  margin: 0;
268
239
  }
269
-
270
240
  .card-actions {
271
241
  padding: 8px 16px 16px 16px;
272
242
  display: flex;
273
243
  justify-content: flex-end;
274
244
  gap: 8px;
275
245
  }
276
-
277
246
  .btn-flat {
278
247
  background: transparent;
279
248
  border: none;
@@ -289,11 +258,9 @@
289
258
  transition: background-color 0.2s;
290
259
  letter-spacing: 0.5px;
291
260
  }
292
-
293
261
  .btn-flat:hover {
294
262
  background-color: rgba(63, 81, 181, 0.08);
295
263
  }
296
-
297
264
  .admin-section {
298
265
  background: white;
299
266
  padding: 24px;
@@ -301,7 +268,6 @@
301
268
  box-shadow: var(--shadow-elevation-2);
302
269
  margin-bottom: 24px;
303
270
  }
304
-
305
271
  .admin-section h2 {
306
272
  margin-top: 0;
307
273
  font-size: 18px;
@@ -309,7 +275,6 @@
309
275
  border-bottom: 2px solid var(--background-color);
310
276
  padding-bottom: 8px;
311
277
  }
312
-
313
278
  .code-list-item {
314
279
  display: flex;
315
280
  flex-direction: column;
@@ -320,7 +285,6 @@
320
285
  border: 1px solid #e0e0e0;
321
286
  gap: 12px;
322
287
  }
323
-
324
288
  @media(min-width: 600px) {
325
289
  .code-list-item {
326
290
  flex-direction: row;
@@ -328,7 +292,6 @@
328
292
  justify-content: space-between;
329
293
  }
330
294
  }
331
-
332
295
  .checkbox-group {
333
296
  display: flex;
334
297
  flex-wrap: wrap;
@@ -336,14 +299,12 @@
336
299
  font-size: 14px;
337
300
  align-items: center;
338
301
  }
339
-
340
302
  .checkbox-group label {
341
303
  display: flex;
342
304
  align-items: center;
343
305
  gap: 4px;
344
306
  cursor: pointer;
345
307
  }
346
-
347
308
  #game-modal {
348
309
  display: none;
349
310
  position: fixed;
@@ -357,7 +318,6 @@
357
318
  align-items: center;
358
319
  justify-content: center;
359
320
  }
360
-
361
321
  .modal-content {
362
322
  width: 90vw;
363
323
  height: 85vh;
@@ -368,7 +328,6 @@
368
328
  flex-direction: column;
369
329
  overflow: hidden;
370
330
  }
371
-
372
331
  .modal-header {
373
332
  background-color: #212121;
374
333
  color: white;
@@ -377,19 +336,16 @@
377
336
  align-items: center;
378
337
  justify-content: space-between;
379
338
  }
380
-
381
339
  .modal-title {
382
340
  margin: 0;
383
341
  font-size: 16px;
384
342
  font-weight: 500;
385
343
  }
386
-
387
344
  .modal-header-actions {
388
345
  display: flex;
389
346
  align-items: center;
390
347
  gap: 8px;
391
348
  }
392
-
393
349
  .modal-btn {
394
350
  background: transparent;
395
351
  border: none;
@@ -403,23 +359,19 @@
403
359
  border-radius: 50%;
404
360
  transition: background-color 0.2s;
405
361
  }
406
-
407
362
  .modal-btn:hover {
408
363
  background-color: rgba(255, 255, 255, 0.1);
409
364
  }
410
-
411
365
  .modal-body {
412
366
  flex: 1;
413
367
  width: 100%;
414
368
  background: #fff;
415
369
  }
416
-
417
370
  .modal-body iframe {
418
371
  width: 100%;
419
372
  height: 100%;
420
373
  border: none;
421
374
  }
422
-
423
375
  footer {
424
376
  text-align: center;
425
377
  padding: 16px;
@@ -429,7 +381,6 @@
429
381
  </style>
430
382
  </head>
431
383
  <body>
432
-
433
384
  <header>
434
385
  <h1 onclick="logout()">StudyUp</h1>
435
386
  <div class="header-actions">
@@ -452,9 +403,7 @@
452
403
  <input type="text" id="class-code-input" placeholder="e.g. 123456" maxlength="6" autofocus>
453
404
  <label for="class-code-input">Class Code</label>
454
405
  </div>
455
-
456
406
  <div id="error-msg">Invalid class code or system currently locked.</div>
457
-
458
407
  <button class="btn-raised" onclick="verifyCode()">Enter Code</button>
459
408
  </div>
460
409
  </main>
@@ -472,7 +421,6 @@
472
421
  <h2 style="margin: 0; color: var(--primary-color);">Admin Dashboard</h2>
473
422
  <button class="btn-raised" style="width: auto; padding: 8px 16px;" onclick="switchView('portal-view')">&larr; Back to Portal</button>
474
423
  </div>
475
-
476
424
  <!-- Global Control Section -->
477
425
  <div class="admin-section">
478
426
  <h2>Global System Controls</h2>
@@ -485,7 +433,6 @@
485
433
  <button id="global-toggle-btn" class="btn-raised btn-warning" onclick="toggleGlobalBlock()">Temporarily Block All Codes</button>
486
434
  </div>
487
435
  </div>
488
-
489
436
  <!-- Create Code Section -->
490
437
  <div class="admin-section">
491
438
  <h2>Create New Class Code</h2>
@@ -498,7 +445,6 @@
498
445
  </div>
499
446
  <div id="admin-error-msg" style="color: var(--error-color); font-size: 13px; margin-top: 8px; display: none;"></div>
500
447
  </div>
501
-
502
448
  <!-- Manage Codes Section -->
503
449
  <div class="admin-section">
504
450
  <h2>Manage Existing Codes & Game Permissions</h2>
@@ -522,7 +468,7 @@
522
468
  </div>
523
469
  </div>
524
470
  <div class="modal-body">
525
- <iframe id="game-iframe" src="" title="Game View" sandbox="allow-scripts allow-same-origin allow-modals allow-pointer-lock allow-downloads"></iframe>
471
+ <iframe id="game-iframe" src="" title="Game View" sandbox="allow-scripts allow-same-origin allow-modals allow-pointer-lock allow-downloads allow-forms allow-popups"></iframe>
526
472
  </div>
527
473
  </div>
528
474
  </div>
@@ -604,7 +550,7 @@
604
550
  document.getElementById('class-code-input').value = '';
605
551
  switchView('portal-view');
606
552
  document.getElementById('current-user-badge').innerText = 'Master Admin Mode';
607
-
553
+
608
554
  const allGameIds = AVAILABLE_GAMES.map(g => g.id);
609
555
  loadStudentDashboard(allGameIds, true);
610
556
  } else if (config.codes && config.codes[inputVal]) {
@@ -653,7 +599,7 @@
653
599
  displayCards.forEach(item => {
654
600
  const card = document.createElement('div');
655
601
  card.className = 'portal-card';
656
-
602
+
657
603
  if (item.isCustomAction) {
658
604
  card.innerHTML = `
659
605
  <div class="card-media" style="background-color: ${item.color};">
@@ -689,7 +635,7 @@
689
635
  async function loadAdminDashboard() {
690
636
  const config = await getConfig();
691
637
  const isBlocked = config.globalBlock;
692
-
638
+
693
639
  const statusText = document.getElementById('global-status-text');
694
640
  const toggleBtn = document.getElementById('global-toggle-btn');
695
641
  if (isBlocked) {
@@ -708,7 +654,6 @@
708
654
  listContainer.innerHTML = '';
709
655
 
710
656
  const studentCodes = Object.keys(config.codes || {});
711
-
712
657
  if (studentCodes.length === 0) {
713
658
  listContainer.innerHTML = `<p style="color: var(--text-secondary); font-style: italic;">No active student codes found. Create one above.</p>`;
714
659
  return;
@@ -720,7 +665,7 @@
720
665
 
721
666
  const item = document.createElement('div');
722
667
  item.className = 'code-list-item';
723
-
668
+
724
669
  let checkboxesHtml = AVAILABLE_GAMES.map(g => {
725
670
  const checked = assignedGames.includes(g.id) ? 'checked' : '';
726
671
  return `<label><input type="checkbox" value="${g.id}" ${checked} onchange="updateCodePermission('${code}', '${g.id}', this.checked)"> ${g.name}</label>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "studyup",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "StudyUp Game Portal",
5
5
  "main": "index.html",
6
6
  "scripts": {
package/server.js DELETED
@@ -1,50 +0,0 @@
1
- const express = require('express');
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- const app = express();
6
- const PORT = process.env.PORT || 3000;
7
- const DATA_FILE = path.join(__dirname, 'data.json');
8
-
9
- app.use(express.json());
10
- // Serve static files from the same root folder where server.js lives
11
- app.use(express.static(__dirname));
12
-
13
- // Helper to read data
14
- function getData() {
15
- if (!fs.existsSync(DATA_FILE)) {
16
- const defaultData = {
17
- globalBlock: false,
18
- codes: {
19
- "123456": { games: ["Sandboxels", "HexGL", "ClassiCube", "2048"] }
20
- }
21
- };
22
- fs.writeFileSync(DATA_FILE, JSON.stringify(defaultData, null, 2));
23
- return defaultData;
24
- }
25
- return JSON.parse(fs.readFileSync(DATA_FILE, 'utf8'));
26
- }
27
-
28
- // Helper to write data
29
- function saveData(data) {
30
- fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
31
- }
32
-
33
- // API: Get current config
34
- app.get('/api/config', (req, res) => {
35
- res.json(getData());
36
- });
37
-
38
- // API: Save/Update config (Admin actions)
39
- app.post('/api/config', (req, res) => {
40
- try {
41
- saveData(req.body);
42
- res.json({ success: true });
43
- } catch (err) {
44
- res.status(500).json({ success: false, error: err.message });
45
- }
46
- });
47
-
48
- app.listen(PORT, () => {
49
- console.log(`StudyUp server running on port ${PORT}`);
50
- });