Anupriya commited on
Commit
768d897
·
1 Parent(s): 4e121c9

generate Ques without vs

Browse files
.gitignore CHANGED
@@ -40,3 +40,26 @@ testem.log
40
  # System files
41
  .DS_Store
42
  Thumbs.db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  # System files
41
  .DS_Store
42
  Thumbs.db
43
+
44
+
45
+ # Visual Studio / .NET
46
+ .vs/
47
+ bin/
48
+ obj/
49
+
50
+ # Angular/Node
51
+ node_modules/
52
+ dist/
53
+ .angular/
54
+
55
+ # Python
56
+ __pycache__/
57
+ *.pyc
58
+ .venv/
59
+ venv/
60
+
61
+ # OS / secrets
62
+ .DS_Store
63
+ Thumbs.db
64
+ .env
65
+
src/app/generate-questions/generate-questions.component.css CHANGED
@@ -25,7 +25,7 @@ body {
25
  width: 100%;
26
  position: sticky;
27
  top: 0;
28
- z-index:1;
29
  }
30
 
31
  .logo img {
@@ -38,24 +38,24 @@ body {
38
 
39
  .header-title {
40
  text-align: center;
41
- flex: 1;
42
  }
43
 
44
  .header-title h1 {
45
- font-size: 3vw;
46
- color: #fff;
47
  margin: 0;
48
  }
49
 
50
  .home-btn img {
51
- width: 5vw;
52
  }
53
 
54
  .home-btn img:hover {
55
- transform: scale(1.1);
56
  }
57
 
58
- /* ==================================================*/
59
 
60
 
61
 
@@ -77,27 +77,24 @@ body {
77
  .btn-generate,
78
  .reset-btn {
79
  padding: 1vw 2vw;
80
- font-size: 1.6rem;
81
  border: none;
82
- border-radius: 0.5vw;
83
  cursor: pointer;
84
  background-color: #5c67f2;
85
  color: white;
86
  font-family: "Open Sans", sans-serif;
87
  font-weight: bold;
88
- transition: all 0.3s ease;
89
  }
90
 
91
- .btn-generate:hover,
92
- .reset-btn:hover {
93
- background-color: #0056b3;
94
- }
95
 
96
  .btn-generate:disabled,
97
  .reset-btn:disabled {
98
- background-color: #cccccc;
99
- color: #666666;
100
- cursor: not-allowed;
101
  }
102
 
103
 
@@ -111,7 +108,7 @@ body {
111
  left: 0;
112
  width: 100vw;
113
  height: 100vh;
114
- background-color: rgba(0, 0, 0, 0.5);
115
  display: flex;
116
  justify-content: center;
117
  align-items: center;
@@ -130,7 +127,7 @@ body {
130
 
131
  .error-popup-content p {
132
  font-size: 1.4vw;
133
- color: #dc3545;
134
  margin-bottom: 1vw;
135
  }
136
 
@@ -185,19 +182,19 @@ body {
185
 
186
 
187
 
188
- /* Use the `src` of the hidden image */
189
- .app-container::before {
190
- content: "";
191
- position: fixed;
192
- top: 0;
193
- left: 0;
194
- width: 100%;
195
- height: 100%;
196
- background-image: attr(data-bg); /* This doesn't work in CSS alone */
197
- background-size: cover;
198
- background-position: center;
199
- z-index: -1; /* Push behind other content */
200
- }
201
 
202
 
203
 
@@ -218,15 +215,16 @@ body {
218
  }
219
 
220
 
221
- .full-content {
222
- display: flex;
223
- padding: 10px;
224
- box-sizing: border-box;
225
- margin-top: 1vw;
226
- }
227
 
228
  /* Questions Section */
229
  .questions-container {
 
230
  max-height: 100%;
231
  overflow: hidden;
232
  margin: 1vw;
@@ -238,6 +236,8 @@ body {
238
  background: #e3f2fd;
239
  padding: 1vw 1vw 1vw 2vw;
240
  width: 100%;
 
 
241
  }
242
 
243
 
@@ -270,14 +270,15 @@ body {
270
  }
271
 
272
 
273
- .btn-submit:disabled {
274
- background-color: #ccc;
275
- cursor: not-allowed;
276
- }
 
277
  .section-title {
278
  font-size: 2.5rem;
279
  color: #4ca1af;
280
- font-family: 'Open Sans', sans-serif;
281
  }
282
 
283
 
@@ -401,7 +402,6 @@ body {
401
  text-align: center;
402
  margin: 50px auto;
403
  padding: 40px;
404
-
405
  background: white;
406
  border-radius: 15px;
407
  width: 66vw;
@@ -411,62 +411,64 @@ body {
411
  justify-content: center;
412
  align-items: center;
413
  font-family: 'Pacifico', cursive;
414
- position: relative;
415
- z-index: 1;
 
 
416
  }
417
 
418
 
419
- .congratulation-message h2 {
420
- color: #4ca1af;
421
- margin-bottom: 15px;
422
- font-size: 2.5vw;
423
- white-space: nowrap;
424
- overflow: hidden;
425
- text-overflow: ellipsis;
426
- text-align: center;
427
- }
428
 
429
  @media (max-width: 768px) {
430
  .congratulation-message h2 {
431
- font-size: 2vw;
432
  }
433
  }
434
 
435
  @media (max-width: 480px) {
436
  .congratulation-message h2 {
437
- font-size: 3vw;
438
  }
439
  }
440
 
441
 
442
- .congratulation-message p {
443
- color: #333;
444
- margin-bottom: 20px;
445
- font-size: 1.8vw;
446
- }
447
 
448
- .congratulation-message button {
449
- padding: 20px 40px;
450
- font-size: 1.7em;
451
- background: linear-gradient(to right, #4ca1af, #6ac5cb);
452
- color: white;
453
- border: none;
454
- border-radius: 10px;
455
- cursor: pointer;
456
- margin-top: 20px;
457
- transition: all 0.3s ease;
458
- font-family: 'Open Sans', sans-serif;
459
- font-weight: bold;
 
 
 
 
 
460
  }
461
 
462
- .congratulation-message button:hover {
463
- background-color: #081f95;
464
- transform: scale(1.03);
465
- }
466
 
467
 
468
 
469
-
470
 
471
  .hint-toggle-btn {
472
  background-color: #5c67f2;
@@ -495,7 +497,7 @@ body {
495
  }
496
 
497
  .hint-toggle-btn::before {
498
- content: "i";
499
  font-weight: bold;
500
  font-size: 2rem;
501
  color: white;
@@ -508,7 +510,7 @@ body {
508
  .side-menu {
509
  position: fixed;
510
  top: 0;
511
- right: -100%;
512
  width: 50%;
513
  height: 100%;
514
  background-color: #ffffff;
@@ -516,12 +518,12 @@ body {
516
  padding: 20px;
517
  box-sizing: border-box;
518
  overflow-y: auto;
519
- transition: right 0.3s ease;
520
  z-index: 999;
521
  }
522
 
523
  .side-menu.active {
524
- right: 0;
525
  }
526
 
527
 
@@ -532,21 +534,21 @@ body {
532
  margin-bottom: 20px;
533
  }
534
 
535
- .side-menu-header h3 {
536
- font-size: 1.5rem;
537
- color: #4ca1af;
538
- }
539
 
540
- .side-menu-header .close-btn {
541
- background-color: #ff5c5c;
542
- color: white;
543
- border: none;
544
- border-radius: 50%;
545
- font-size: 1.2rem;
546
- width: 30px;
547
- height: 30px;
548
- cursor: pointer;
549
- }
550
 
551
  .side-menu ul {
552
  list-style: none;
@@ -591,7 +593,6 @@ body {
591
 
592
  .logo-container {
593
  width: 10vw; /* Adjust logo width */
594
-
595
  }
596
 
597
  .back-button {
@@ -633,7 +634,7 @@ body {
633
 
634
 
635
  .side-menu {
636
- width: 40%;
637
  }
638
 
639
  .congratulation-message {
@@ -666,14 +667,14 @@ body {
666
  }
667
 
668
 
669
- .congratulation-message p {
670
- font-size: 1.4rem;
671
- }
672
 
673
- .congratulation-message button {
674
- padding: 15px 30px;
675
- font-size: 1.4rem;
676
- }
677
  }
678
 
679
 
@@ -739,7 +740,7 @@ h3 {
739
  left: 0;
740
  width: 100%;
741
  height: 100%;
742
- /* background: rgba(0, 0, 0, 0.6);*/
743
  display: flex;
744
  flex-direction: column;
745
  justify-content: center;
@@ -882,12 +883,12 @@ h3 {
882
 
883
  /* Button Colors */
884
  .btn-generate {
885
- background-color: #3f51b5;
886
  color: white;
887
  }
888
 
889
  .reset-btn {
890
- background-color: #3f51b5;
891
  color: white;
892
  }
893
 
@@ -958,7 +959,7 @@ h3 {
958
  transform: translateY(-50%);
959
  display: flex;
960
  justify-content: space-between;
961
- /* z-index: 2;*/
962
  padding: 0 8px;
963
  }
964
 
@@ -991,7 +992,7 @@ h3 {
991
  border-radius: 6px;
992
  white-space: nowrap;
993
  box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2);
994
- /* z-index: 5;*/
995
  opacity: 1;
996
  }
997
 
@@ -1023,9 +1024,9 @@ h3 {
1023
  /*hint button css start here */
1024
  /* Anchor the i button on the right side of the screen (adjust as you like) */
1025
  .hint-anchor {
1026
- position: fixed; /* or absolute relative to a container */
1027
- top: 30vh; /* vertical position */
1028
- right: 33px; /* distance from right edge */
1029
  z-index: 1101;
1030
  }
1031
 
@@ -1175,3 +1176,146 @@ h3 {
1175
  transform: rotate(45deg);
1176
  }
1177
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  width: 100%;
26
  position: sticky;
27
  top: 0;
28
+ z-index: 1;
29
  }
30
 
31
  .logo img {
 
38
 
39
  .header-title {
40
  text-align: center;
41
+ flex: 1;
42
  }
43
 
44
  .header-title h1 {
45
+ font-size: 3vw;
46
+ color: #fff;
47
  margin: 0;
48
  }
49
 
50
  .home-btn img {
51
+ width: 5vw;
52
  }
53
 
54
  .home-btn img:hover {
55
+ transform: scale(1.1);
56
  }
57
 
58
+ /* ==================================================*/
59
 
60
 
61
 
 
77
  .btn-generate,
78
  .reset-btn {
79
  padding: 1vw 2vw;
80
+ font-size: 1.6rem;
81
  border: none;
82
+ border-radius: 0.5vw;
83
  cursor: pointer;
84
  background-color: #5c67f2;
85
  color: white;
86
  font-family: "Open Sans", sans-serif;
87
  font-weight: bold;
88
+ transition: all 0.3s ease;
89
  }
90
 
91
+
 
 
 
92
 
93
  .btn-generate:disabled,
94
  .reset-btn:disabled {
95
+ background-color: #cccccc;
96
+ color: #666666;
97
+ cursor: not-allowed;
98
  }
99
 
100
 
 
108
  left: 0;
109
  width: 100vw;
110
  height: 100vh;
111
+ background-color: rgba(0, 0, 0, 0.5);
112
  display: flex;
113
  justify-content: center;
114
  align-items: center;
 
127
 
128
  .error-popup-content p {
129
  font-size: 1.4vw;
130
+ color: #dc3545;
131
  margin-bottom: 1vw;
132
  }
133
 
 
182
 
183
 
184
 
185
+ /* Use the `src` of the hidden image */
186
+ .app-container::before {
187
+ content: "";
188
+ position: fixed;
189
+ top: 0;
190
+ left: 0;
191
+ width: 100%;
192
+ height: 100%;
193
+ background-image: attr(data-bg); /* This doesn't work in CSS alone */
194
+ background-size: cover;
195
+ background-position: center;
196
+ z-index: -1; /* Push behind other content */
197
+ }
198
 
199
 
200
 
 
215
  }
216
 
217
 
218
+ .full-content {
219
+ display: flex;
220
+ padding: 10px;
221
+ box-sizing: border-box;
222
+ margin-top: 1vw;
223
+ }
224
 
225
  /* Questions Section */
226
  .questions-container {
227
+ position: relative; /* important for centering inside this box */
228
  max-height: 100%;
229
  overflow: hidden;
230
  margin: 1vw;
 
236
  background: #e3f2fd;
237
  padding: 1vw 1vw 1vw 2vw;
238
  width: 100%;
239
+ box-shadow: 0 .4vw 1vw rgba(0, 0, 0, .1);
240
+ border: 9px solid #009688;
241
  }
242
 
243
 
 
270
  }
271
 
272
 
273
+ .btn-submit:disabled {
274
+ background-color: #ccc;
275
+ cursor: not-allowed;
276
+ }
277
+
278
  .section-title {
279
  font-size: 2.5rem;
280
  color: #4ca1af;
281
+ font-family: 'Open Sans', sans-serif;
282
  }
283
 
284
 
 
402
  text-align: center;
403
  margin: 50px auto;
404
  padding: 40px;
 
405
  background: white;
406
  border-radius: 15px;
407
  width: 66vw;
 
411
  justify-content: center;
412
  align-items: center;
413
  font-family: 'Pacifico', cursive;
414
+ position: relative;
415
+ z-index: 1;
416
+ box-shadow: 0 .4vw 1vw rgba(0, 0, 0, .1);
417
+ border: 9px solid #009688;
418
  }
419
 
420
 
421
+ .congratulation-message h2 {
422
+ color: #4ca1af;
423
+ margin-bottom: 15px;
424
+ font-size: 2.5vw;
425
+ white-space: nowrap;
426
+ overflow: hidden;
427
+ text-overflow: ellipsis;
428
+ text-align: center;
429
+ }
430
 
431
  @media (max-width: 768px) {
432
  .congratulation-message h2 {
433
+ font-size: 2vw;
434
  }
435
  }
436
 
437
  @media (max-width: 480px) {
438
  .congratulation-message h2 {
439
+ font-size: 3vw;
440
  }
441
  }
442
 
443
 
444
+ .congratulation-message p {
445
+ color: #333;
446
+ margin-bottom: 20px;
447
+ font-size: 1.8vw;
448
+ }
449
 
450
+ .congratulation-message button {
451
+ padding: 20px 40px;
452
+ font-size: 1.7em;
453
+ background: #006780;
454
+ color: white;
455
+ border: none;
456
+ border-radius: 10px;
457
+ cursor: pointer;
458
+ margin-top: 20px;
459
+ transition: all 0.3s ease;
460
+ font-family: 'Open Sans', sans-serif;
461
+ font-weight: bold;
462
+ }
463
+
464
+ .congratulation-message button:hover {
465
+ background-color: #006780;
466
+ transform: scale(1.03);
467
  }
468
 
 
 
 
 
469
 
470
 
471
 
 
472
 
473
  .hint-toggle-btn {
474
  background-color: #5c67f2;
 
497
  }
498
 
499
  .hint-toggle-btn::before {
500
+ content: "i";
501
  font-weight: bold;
502
  font-size: 2rem;
503
  color: white;
 
510
  .side-menu {
511
  position: fixed;
512
  top: 0;
513
+ right: -100%;
514
  width: 50%;
515
  height: 100%;
516
  background-color: #ffffff;
 
518
  padding: 20px;
519
  box-sizing: border-box;
520
  overflow-y: auto;
521
+ transition: right 0.3s ease;
522
  z-index: 999;
523
  }
524
 
525
  .side-menu.active {
526
+ right: 0;
527
  }
528
 
529
 
 
534
  margin-bottom: 20px;
535
  }
536
 
537
+ .side-menu-header h3 {
538
+ font-size: 1.5rem;
539
+ color: #4ca1af;
540
+ }
541
 
542
+ .side-menu-header .close-btn {
543
+ background-color: #ff5c5c;
544
+ color: white;
545
+ border: none;
546
+ border-radius: 50%;
547
+ font-size: 1.2rem;
548
+ width: 30px;
549
+ height: 30px;
550
+ cursor: pointer;
551
+ }
552
 
553
  .side-menu ul {
554
  list-style: none;
 
593
 
594
  .logo-container {
595
  width: 10vw; /* Adjust logo width */
 
596
  }
597
 
598
  .back-button {
 
634
 
635
 
636
  .side-menu {
637
+ width: 40%;
638
  }
639
 
640
  .congratulation-message {
 
667
  }
668
 
669
 
670
+ .congratulation-message p {
671
+ font-size: 1.4rem;
672
+ }
673
 
674
+ .congratulation-message button {
675
+ padding: 15px 30px;
676
+ font-size: 1.4rem;
677
+ }
678
  }
679
 
680
 
 
740
  left: 0;
741
  width: 100%;
742
  height: 100%;
743
+ /* background: rgba(0, 0, 0, 0.6);*/
744
  display: flex;
745
  flex-direction: column;
746
  justify-content: center;
 
883
 
884
  /* Button Colors */
885
  .btn-generate {
886
+ background-color: #006780;
887
  color: white;
888
  }
889
 
890
  .reset-btn {
891
+ background-color: #006780;
892
  color: white;
893
  }
894
 
 
959
  transform: translateY(-50%);
960
  display: flex;
961
  justify-content: space-between;
962
+ /* z-index: 2;*/
963
  padding: 0 8px;
964
  }
965
 
 
992
  border-radius: 6px;
993
  white-space: nowrap;
994
  box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2);
995
+ /* z-index: 5;*/
996
  opacity: 1;
997
  }
998
 
 
1024
  /*hint button css start here */
1025
  /* Anchor the i button on the right side of the screen (adjust as you like) */
1026
  .hint-anchor {
1027
+ position: fixed;
1028
+ top: 15.6vw;
1029
+ right: 3vw;
1030
  z-index: 1101;
1031
  }
1032
 
 
1176
  transform: rotate(45deg);
1177
  }
1178
  }
1179
+
1180
+
1181
+ /* Circular countdown */
1182
+ .ring {
1183
+ position: relative;
1184
+ width: 220px;
1185
+ height: 220px;
1186
+ margin: 0 auto 8px;
1187
+ }
1188
+
1189
+ .ring-svg {
1190
+ width: 220px;
1191
+ height: 220px;
1192
+ display: block;
1193
+ }
1194
+
1195
+ /* static track */
1196
+ .ring-track {
1197
+ fill: none;
1198
+ stroke: rgba(15, 23, 42, 0.18); /* slate-900 @ 18% */
1199
+ stroke-width: 16;
1200
+ }
1201
+
1202
+ /* animated arc */
1203
+ .ring-progress {
1204
+ fill: none;
1205
+ stroke: #006780; /* accent */
1206
+ stroke-width: 16;
1207
+ stroke-linecap: round;
1208
+ transform: rotate(-90deg); /* start at 12 o’clock */
1209
+ transform-origin: 50% 50%;
1210
+ transition: stroke-dashoffset 0.1s linear; /* smooth update */
1211
+ }
1212
+
1213
+ /* big number in the center */
1214
+ .ring-number {
1215
+ position: absolute;
1216
+ inset: 0;
1217
+ display: grid;
1218
+ place-items: center;
1219
+ font-weight: 800;
1220
+ font-size: 56px;
1221
+ color: #0f172a;
1222
+ }
1223
+
1224
+
1225
+ .global-countdown-overlay {
1226
+ position: absolute; /* not fixed */
1227
+ inset: 0;
1228
+ display: grid;
1229
+ place-items: center;
1230
+ background: transparent;
1231
+ pointer-events: none;
1232
+ z-index: 20;
1233
+ }
1234
+
1235
+
1236
+ /* Keep overlay centered and always on top */
1237
+ :host ::ng-deep .global-countdown-overlay {
1238
+ position: fixed;
1239
+ inset: 0;
1240
+ display: grid;
1241
+ place-items: center;
1242
+ background: transparent;
1243
+ pointer-events: none;
1244
+ z-index: 2000; /* higher to avoid being covered */
1245
+ }
1246
+
1247
+ /* Frosted card so text never blends with page content */
1248
+ :host ::ng-deep .overlay-card {
1249
+ background: rgba(255,255,255,0.98);
1250
+ backdrop-filter: blur(2px);
1251
+ -webkit-backdrop-filter: blur(2px);
1252
+ border: 1px solid rgba(15,23,42,0.10);
1253
+ border-radius: 16px;
1254
+ padding: 18px 22px;
1255
+ box-shadow: 0 10px 32px rgba(0,0,0,.18);
1256
+ text-align: center;
1257
+ pointer-events: none;
1258
+ }
1259
+
1260
+ /* Ensure the ring forms a stacking context for the disk/number */
1261
+ :host ::ng-deep .ring {
1262
+ position: relative;
1263
+ width: 220px;
1264
+ height: 220px;
1265
+ margin: 0 auto 8px;
1266
+ }
1267
+
1268
+ /* Place the SVG above the disk, below the number */
1269
+ :host ::ng-deep .ring-svg {
1270
+ position: relative;
1271
+ z-index: 2;
1272
+ }
1273
+
1274
+ /* ADD a white center disk behind the number (no HTML changes) */
1275
+ :host ::ng-deep .ring::before {
1276
+ content: "";
1277
+ position: absolute;
1278
+ inset: 0;
1279
+ margin: auto;
1280
+ width: 120px; /* adjust if you scale the ring */
1281
+ height: 120px;
1282
+ border-radius: 50%;
1283
+ background: #ffffff;
1284
+ border: 1px solid rgba(15,23,42,.08);
1285
+ box-shadow: inset 0 0 0 4px rgba(255,255,255,.9), 0 2px 10px rgba(0,0,0,.08);
1286
+ z-index: 1; /* under number, over background */
1287
+ }
1288
+
1289
+ /* Stronger, crisper number; always readable */
1290
+ :host ::ng-deep .ring-number {
1291
+ position: absolute;
1292
+ inset: 0;
1293
+ display: grid;
1294
+ place-items: center;
1295
+ z-index: 3; /* above everything in the ring */
1296
+ font-weight: 900;
1297
+ font-size: 64px; /* bump if you increased ring size */
1298
+ line-height: 1;
1299
+ color: #0b1220; /* deep slate */
1300
+ text-shadow: 0 0 2px #fff, 0 0 8px rgba(255,255,255,.85), 0 2px 8px rgba(0,0,0,.35);
1301
+ }
1302
+
1303
+ /* Keep the ring thicker for visibility (optional) */
1304
+ :host ::ng-deep .ring-track,
1305
+ :host ::ng-deep .ring-progress {
1306
+ stroke-width: 18px; /* increase if needed */
1307
+ }
1308
+
1309
+ /* Caption pill with high contrast */
1310
+ :host ::ng-deep .caption-pill {
1311
+ display: inline-block;
1312
+ margin-top: 10px;
1313
+ padding: 8px 14px;
1314
+ border-radius: 999px;
1315
+ background: #0ea5e9 !important;
1316
+ color: #ffffff !important;
1317
+ font-weight: 700;
1318
+ font-size: 15px;
1319
+ letter-spacing: .2px;
1320
+ filter: drop-shadow(0 3px 10px rgba(14,165,233,.25));
1321
+ }
src/app/generate-questions/generate-questions.component.html CHANGED
@@ -3,7 +3,7 @@
3
  <div *ngIf="isLoading" class="loading-overlay">
4
  <span class="loader"></span>
5
  </div>
6
-
7
 
8
  <div class="header-container">
9
  <!-- Logo Section -->
@@ -38,7 +38,7 @@
38
  (focus)="showSuggestions = true"
39
  onclick="showSuggestions = true"
40
  (blur)="hideSuggestions()" />
41
-
42
 
43
 
44
 
@@ -99,39 +99,47 @@
99
  <div class="questions-container" *ngIf="questions.length > 0 && !isAllLevelsCompleted">
100
  <div class="quesHintbtn">
101
  <h2 class="section-title">📝 Questions</h2>
102
- <!--<button *ngIf="isFirstAttemptDone && hints.length > 0"
103
- class="hint-toggle-btn"
104
- [class.blink]="hasNewHints"
105
- (click)="toggleHintMenu()">
106
- </button>-->
107
  </div>
108
 
109
-
110
-
111
-
112
-
113
  <ul class="questions-list">
114
  <li *ngFor="let question of questions" class="question-item">
115
  <div class="question-wrapper">
116
  <div class="question-text-wrapper">
117
  <span class="question-text" *ngFor="let part of question.parts; let i = index">
118
  {{ part }}
119
-
120
-
121
- <input *ngIf="i < question.parts.length - 1"
122
- [(ngModel)]="userAnswers[question.index]"
123
- [readonly]="isValidationInProgress || readonlyAnswers[question.index]"
124
- placeholder=""
125
- class="answer-input"
126
- [ngClass]="{
 
127
  'correct-answer': !isLoading && isChecked && userAnswers[question.index] === questionsWithAnswers[question.index].correctAnswer,
128
  'wrong-answer': !isLoading && isChecked && userAnswers[question.index] !== questionsWithAnswers[question.index].correctAnswer
129
  }"
130
- (input)="onAnswerChange(question.index)" />
131
 
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
 
 
135
 
136
 
137
  </span>
@@ -139,39 +147,21 @@
139
  </div>
140
  </li>
141
  </ul>
142
- <!-- Show the i button ONLY after first validation and when there are hints -->
143
  <ng-container *ngIf="isFirstAttemptDone && hints?.length">
144
  <div class="hint-anchor" (click)="$event.stopPropagation()">
145
- <!-- i button -->
146
- <button type="button"
147
- class="hint-toggle-btn"
148
- aria-haspopup="dialog"
149
- [attr.aria-expanded]="isHintMenuVisible ? 'true' : 'false'"
150
- aria-controls="hintsPanel"
151
- (click)="toggleHintMenu()">
152
-
153
  </button>
154
 
155
- <!-- Popover opening to the LEFT -->
156
- <section *ngIf="isHintMenuVisible"
157
- id="hintsPanel"
158
- class="hint-popover"
159
- role="dialog"
160
- aria-modal="true"
161
- aria-label="Hints"
162
- (click)="$event.stopPropagation()">
163
- <button type="button"
164
- class="hint-close"
165
- aria-label="Close hints"
166
- (click)="closeHints()">
167
  ×
168
  </button>
169
 
170
  <h3 class="hint-title">Hints</h3>
171
  <ul class="hint-list" role="list">
172
  <ng-container *ngFor="let hint of hints; let i = index">
173
- <li *ngIf="hint && hint.trim() !== '' && userAnswers[i] !== questionsWithAnswers[i]?.correctAnswer"
174
- class="hint-item">
175
  <strong>Hint for Question {{ i + 1 }}:</strong> {{ hint }}
176
  </li>
177
  </ng-container>
@@ -179,14 +169,8 @@
179
  </section>
180
  </div>
181
 
182
- <!-- Outside click closes it -->
183
- <div *ngIf="isHintMenuVisible"
184
- class="hint-backdrop"
185
- (click)="closeHints()"></div>
186
  </ng-container>
187
-
188
-
189
-
190
  </div>
191
  </div>
192
 
 
3
  <div *ngIf="isLoading" class="loading-overlay">
4
  <span class="loader"></span>
5
  </div>
6
+
7
 
8
  <div class="header-container">
9
  <!-- Logo Section -->
 
38
  (focus)="showSuggestions = true"
39
  onclick="showSuggestions = true"
40
  (blur)="hideSuggestions()" />
41
+
42
 
43
 
44
 
 
99
  <div class="questions-container" *ngIf="questions.length > 0 && !isAllLevelsCompleted">
100
  <div class="quesHintbtn">
101
  <h2 class="section-title">📝 Questions</h2>
 
 
 
 
 
102
  </div>
103
 
 
 
 
 
104
  <ul class="questions-list">
105
  <li *ngFor="let question of questions" class="question-item">
106
  <div class="question-wrapper">
107
  <div class="question-text-wrapper">
108
  <span class="question-text" *ngFor="let part of question.parts; let i = index">
109
  {{ part }}
110
+ <span class="answer-slot">
111
+ <input *ngIf="i < question.parts.length - 1"
112
+ [(ngModel)]="userAnswers[question.index]"
113
+ [readonly]="isValidationInProgress || readonlyAnswers[question.index]"
114
+ placeholder=""
115
+ class="answer-input"
116
+ pattern="[A-Za-z]*"
117
+ (input)="onInput($event, question.index)"
118
+ [ngClass]="{
119
  'correct-answer': !isLoading && isChecked && userAnswers[question.index] === questionsWithAnswers[question.index].correctAnswer,
120
  'wrong-answer': !isLoading && isChecked && userAnswers[question.index] !== questionsWithAnswers[question.index].correctAnswer
121
  }"
122
+ (input)="onAnswerChange(question.index)" />
123
 
124
 
125
+ <div *ngIf="showGlobalCountdown" class="global-countdown-overlay" role="status" aria-live="assertive">
126
+ <div class="overlay-card">
127
+ <div class="ring">
128
+ <svg class="ring-svg" viewBox="0 0 220 220" aria-hidden="true">
129
+ <circle class="ring-track" cx="110" cy="110" r="90"></circle>
130
+ <circle class="ring-progress"
131
+ cx="110" cy="110" r="90"
132
+ [attr.stroke-dasharray]="ringCircumference"
133
+ [attr.stroke-dashoffset]="ringDashoffset"></circle>
134
+ </svg>
135
+ <div class="ring-number">{{ globalCountdown }}</div>
136
+ </div>
137
+ <div class="caption-pill">{{ overlayCaption }} {{ globalCountdown }}s</div>
138
+ </div>
139
+ </div>
140
 
141
 
142
+ </span>
143
 
144
 
145
  </span>
 
147
  </div>
148
  </li>
149
  </ul>
150
+
151
  <ng-container *ngIf="isFirstAttemptDone && hints?.length">
152
  <div class="hint-anchor" (click)="$event.stopPropagation()">
153
+ <button type="button" class="hint-toggle-btn" aria-haspopup="dialog" [attr.aria-expanded]="isHintMenuVisible ? 'true' : 'false'" aria-controls="hintsPanel" (click)="toggleHintMenu()">
 
 
 
 
 
 
 
154
  </button>
155
 
156
+ <section *ngIf="isHintMenuVisible" id="hintsPanel" class="hint-popover" role="dialog" aria-modal="true" aria-label="Hints" (click)="$event.stopPropagation()">
157
+ <button type="button" class="hint-close" aria-label="Close hints" (click)="closeHints()">
 
 
 
 
 
 
 
 
 
 
158
  ×
159
  </button>
160
 
161
  <h3 class="hint-title">Hints</h3>
162
  <ul class="hint-list" role="list">
163
  <ng-container *ngFor="let hint of hints; let i = index">
164
+ <li *ngIf="hint && hint.trim() !== '' && userAnswers[i] !== questionsWithAnswers[i]?.correctAnswer" class="hint-item">
 
165
  <strong>Hint for Question {{ i + 1 }}:</strong> {{ hint }}
166
  </li>
167
  </ng-container>
 
169
  </section>
170
  </div>
171
 
172
+ <div *ngIf="isHintMenuVisible" class="hint-backdrop" (click)="closeHints()"></div>
 
 
 
173
  </ng-container>
 
 
 
174
  </div>
175
  </div>
176
 
src/app/generate-questions/generate-questions.component.ts CHANGED
@@ -25,27 +25,33 @@ interface Question {
25
  styleUrls: ['./generate-questions.component.css']
26
  })
27
  export class GenerateQuestionsComponent {
28
- topic: string = '';
29
- hardcodedTopics: string[] = ['Noun', 'Verb', 'Past Tense', 'Adjective', 'Present Continuous'];
 
 
 
 
 
 
30
 
31
  showSuggestions: boolean = false;
32
- //questions: string[] = [];
33
- userAnswers: string[] = []; // Array to store user answers
34
- feedback: string[] = []; // Array to store feedback for each answer
35
- error: string = '';
36
- isChecked: boolean = false; // Flag to disable button after clicking
37
  isAnswerModified: boolean = false;
38
  questions: { parts: string[]; index: number }[] = [];
39
  attemptCounts: { [key: number]: number } = {};
40
  correctAnswers: string[] = [];
41
- questionsWithAnswers: QuestionWithAnswer[] = []; // Track questions with answers
42
- currentDifficulty: string = 'basic';
43
- difficultyLevels: string[] = ['basic', 'elementary', 'pre-intermediate', 'intermediate', 'upper-intermediate', 'medium', 'hard'];
44
- //difficultyLevels: string[] = ['basic', 'hard'];
45
  isDropdownDisabled: boolean = false;
46
  isValidationInProgress: boolean = false;
47
  isTopicLocked: boolean = false;
48
- isAllLevelsCompleted: boolean = false; // Tracks if all levels are completed
49
  readonlyAnswers: boolean[] = [];
50
  hints: string[] = [];
51
  attempts: number[] = [];
@@ -53,178 +59,150 @@ export class GenerateQuestionsComponent {
53
  isGenerateDisabled: boolean = true;
54
  isResetDisabled: boolean = true;
55
  isFirstAttemptDone: boolean = false;
56
- hasNewHints: boolean = false; // Track new hints for animation
57
  isLoading: boolean = false;
58
  isValidationCompleted = false;
59
 
60
  showLevelTooltip: boolean = false;
61
  activateLevelDot: boolean = false;
62
 
 
 
63
 
64
-
65
-
66
-
67
-
68
- isTyping: boolean = false; // Track if the user is typing
69
- validated: boolean = false; // Track if validation has been triggered
70
-
71
-
72
-
73
- constructor(private generateQuestionsService: GenerateQuestionsService, private router: Router, private cdr: ChangeDetectorRef) { }
74
-
75
-
76
 
77
  onTopicChange() {
78
  if (this.topic.trim().length > 0) {
79
- this.isGenerateDisabled = false; // Enable the Generate button when there's input
80
  } else {
81
- this.isGenerateDisabled = true; // Disable the Generate button if input is empty
82
  }
83
  if (this.topic.trim().length === 0) {
84
- this.showSuggestions = true; // Hide suggestions when the topic is empty
85
  } else {
86
- this.showSuggestions = false; // Show suggestions when there's text in the input field
87
  }
88
  }
89
-
90
-
91
 
92
  getProgressWidth(): string {
93
  const index = this.difficultyLevels.indexOf(this.currentDifficulty);
94
- if (index === -1 || this.difficultyLevels.length <= 1) return '0%';
95
  const percent = (index / (this.difficultyLevels.length - 1)) * 100;
96
  return `${percent}%`;
97
  }
98
 
99
-
100
-
101
- getProgressGradient(): string {
102
- const index = this.difficultyLevels.indexOf(this.currentDifficulty);
103
- const gradientColors = [
104
- "#4caf50", // Basic (Green)
105
- "#8bc34a", // Elementary
106
- "#ffc107", // Pre-Intermediate (Yellow)
107
- "#ff9800", // Intermediate
108
- "#ff5722", // Upper-Intermediate (Orange)
109
- "#f44336", // Medium (Red)
110
- "#b71c1c" // Hard (Dark Red)
111
- ];
112
-
113
- if (index === 0) {
114
- return gradientColors[0];
115
- }
116
-
117
- const gradient = gradientColors.slice(0, index + 1).join(', ');
118
- return `linear-gradient(to right, ${gradient})`;
119
- }
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
 
129
  generateQuestions() {
130
  this.hints = [];
131
- this.isQuestionsGenerated = false; // Reset the flag before generating
132
  this.showLevelTooltip = true;
133
  this.activateLevelDot = true;
134
 
135
-
136
  if (this.isAllLevelsCompleted) {
137
- this.error = 'Please reset to start over.';
138
  return;
139
  }
140
- this.isTopicLocked = true; // Lock the topic field
141
- this.showSuggestions = false; // Hide the dropdown after Generate is clicked
142
- this.readonlyAnswers = []; // Reset readonlyAnswers state
143
- this.isDropdownDisabled = true; // Disable the dropdown
144
- console.log(`Generating questions for difficulty level: ${this.currentDifficulty}`);
145
 
146
- this.generateQuestionsService.generateQuestions(this.topic, this.currentDifficulty).subscribe(
147
- (response) => {
148
- console.log('Response from backend:', response);
149
- if (response.generations && response.generations.length > 0) {
150
- const rawQuestions = response.generations[0].text;
151
- console.log('Raw Questions:', rawQuestions);
152
- this.isQuestionsGenerated = true; // Set flag to true after questions are generated
153
 
154
- // Disable the Generate button once questions are generated
155
- this.isGenerateDisabled = true;
156
 
157
- // Enable the Reset button when questions are displayed
158
- this.isResetDisabled = false;
 
 
 
159
 
160
- this.questionsWithAnswers = [];
 
 
 
161
 
162
- const regex = /(\d+\.\s*.+?_______)\s*(.*?)\s*\(([^)]+)\)\s*$/gm;
163
- let match;
164
 
165
- while ((match = regex.exec(rawQuestions)) !== null) {
166
- console.log('Match:', match);
167
 
168
- const questionText = match[1]?.trim() || ''; // Full question text up to the blank
169
- const afterBlank = match[2]?.trim() || ''; // Text after the blank
170
- const correctAnswer = match[3]?.trim() || ''; // Correct answer inside the parentheses
171
 
172
- if (questionText && correctAnswer) {
173
- this.questionsWithAnswers.push({
174
- question: `${questionText}${afterBlank}`, // Combine parts for the full question
175
- correctAnswer,
176
- });
177
- }
178
- }
179
 
180
- console.log('Extracted Questions with Answers:', this.questionsWithAnswers);
 
 
181
 
182
- this.questions = this.questionsWithAnswers.map((q, index) => {
183
- const cleanedQuestion = q.question.replace(/\s*\(.*?\)/g, ''); // Remove parentheses content
184
- const parts = cleanedQuestion.split(/_______/);
 
 
185
 
186
- if (parts.length === 2) {
187
- parts[0] = parts[0].trim(); // Text before the blank
188
- parts[1] = parts[1].trim(); // Text after the blank
189
- } else if (parts.length === 1) {
190
- parts[0] = parts[0].trim();
191
- parts.push(''); // Ensure a second part exists
192
  }
193
 
194
- return { parts, index };
195
- });
196
 
197
- this.userAnswers = new Array(this.questions.length).fill('');
198
- this.feedback = new Array(this.questions.length).fill('');
199
- this.readonlyAnswers = new Array(this.questions.length).fill(false);
200
- this.isChecked = false;
 
201
 
202
- this.attemptCounts = this.questions.reduce((acc, question, index) => {
203
- acc[index] = 0; // Set initial attempt count for each question
204
- return acc;
205
- }, {} as { [key: number]: number });
206
- console.log('Questions without answers:', this.questions);
207
- } else {
208
- this.error = 'No questions generated. Please try again.';
209
- }
210
- },
211
- (error) => {
212
- if (error.status === 400 && error.error.message) {
213
- this.error = error.error.message; // Display the message from the backend
214
- } else {
215
- this.error = 'Failed to fetch questions. Please try again later.';
216
- }
217
- console.error('Error fetching questions:', error);
218
- }
219
- );
220
- }
221
 
 
 
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
 
225
 
226
  resetTopic() {
227
- this.topic = '';
228
  this.questions = [];
229
  this.questionsWithAnswers = [];
230
  this.userAnswers = [];
@@ -240,44 +218,45 @@ export class GenerateQuestionsComponent {
240
  this.isQuestionsGenerated = false;
241
  this.isTopicLocked = false;
242
  this.validated = false;
243
- this.currentDifficulty = 'basic';
244
- this.error = '';
245
  this.isGenerateDisabled = false;
246
  this.isResetDisabled = true;
247
  this.showSuggestions = false;
248
  this.showLevelTooltip = false;
249
  this.activateLevelDot = false;
250
  this.showSuggestions = false;
251
-
252
- this.isDropdownDisabled = false; // ✅ Ensure dropdown is not disabled
253
- this.isTopicLocked = false; // ✅ Ensure the topic can be typed again
254
-
255
-
256
-
257
  this.stopConfetti();
258
- }
259
-
260
-
261
-
 
 
 
262
 
 
263
 
264
  areAllAnswersFilled(): boolean {
265
- return this.userAnswers.every(answer => answer?.trim() !== '');
266
  }
267
 
268
-
269
-
270
-
271
  closeErrorPopup() {
272
- this.error = ''; // Clear the error message
273
- this.isGenerateDisabled = true; // Disable Generate button
274
- this.isResetDisabled = false; // Enable Reset button
275
  }
276
 
 
277
 
 
 
 
278
 
279
-
280
-
281
 
282
 
283
  checkAllAnswers() {
@@ -290,27 +269,38 @@ export class GenerateQuestionsComponent {
290
 
291
  const requestPayload = this.questions.map((questionObj, index) => ({
292
  topic: this.topic,
293
- question: questionObj.parts.join('_______'),
294
  user_answer: this.userAnswers[index],
295
  }));
296
 
297
  console.log("Checking answers...");
298
  console.log("User Answers:", this.userAnswers);
299
- console.log("Correct Answers:", this.questionsWithAnswers.map(q => q.correctAnswer));
 
 
 
300
 
301
  this.generateQuestionsService.validateAllAnswers(requestPayload).subscribe(
302
  (response) => {
303
- console.log('Response from backend for all answers validation:', response);
304
 
305
  if (response.results && response.results.length > 0) {
306
  let allAttemptsCompleted = true;
307
 
 
 
 
 
308
  response.results.forEach((result: any, index: number) => {
309
  this.attemptCounts[index]++;
310
  const validationResponse = result.validation_response;
311
- const hint = result.hint ? result.hint.trim() : '';
312
- const userAnswer = this.userAnswers[index].trim().toLowerCase();
313
- const correctAnswer = this.questionsWithAnswers[index].correctAnswer.trim().toLowerCase();
 
 
 
 
314
 
315
  console.log(`Validation Response for Question ${index + 1}: ${validationResponse}`);
316
  console.log(`User Answer: ${userAnswer}, Correct Answer: ${correctAnswer}`);
@@ -318,38 +308,60 @@ export class GenerateQuestionsComponent {
318
  if (userAnswer === correctAnswer) {
319
  console.log(`✅ Correct answer for question ${index + 1}: ${this.userAnswers[index]}`);
320
  this.readonlyAnswers[index] = true;
321
- this.hints[index] = '';
322
- //this.userAnswers[index] = correctAnswer;
323
  this.userAnswers[index] = this.questionsWithAnswers[index].correctAnswer;
324
  } else {
325
  console.log(`❌ Incorrect answer for question ${index + 1}: ${this.userAnswers[index]}`);
326
 
327
- setTimeout(() => {
328
- if (this.attemptCounts[index] === 1) {
 
 
 
 
 
 
329
  console.log(`🔄 Resetting incorrect answer for Question ${index + 1}`);
330
- this.userAnswers[index] = '';
 
 
 
 
 
 
 
 
331
  }
332
 
333
- if (this.attemptCounts[index] >= 2) {
 
334
  if (!this.userAnswers[index] || this.userAnswers[index].trim() === "" || userAnswer !== correctAnswer) {
335
  this.userAnswers[index] = this.questionsWithAnswers[index].correctAnswer;
336
  console.log(`✔️ Auto-filled correct answer for Question ${index + 1}: ${this.userAnswers[index]}`);
337
  }
 
 
 
338
  if (this.isLastLevel() && index === this.questions.length - 1) {
 
 
339
  setTimeout(() => {
 
340
  this.isAllLevelsCompleted = true;
341
- console.log('🎉 You have completed all difficulty levels.');
342
  this.triggerConfetti();
343
- }, 2000);
344
  }
345
- }
346
- }, 2000);
 
347
 
348
  this.hints[index] = hint.length > 0 ? hint : null;
349
  }
350
 
351
- this.feedback[index] = validationResponse || 'No feedback provided.';
352
 
 
353
  setTimeout(() => {
354
  if (userAnswer === correctAnswer) {
355
  this.userAnswers[index] = this.userAnswers[index];
@@ -363,57 +375,85 @@ export class GenerateQuestionsComponent {
363
  }
364
  });
365
 
366
- this.hasNewHints = this.hints.some(hint => hint && hint.trim() !== "");
367
-
368
  if (this.hasNewHints) {
369
- setTimeout(() => {
370
- this.hasNewHints = false;
371
- }, 5000);
372
  }
373
 
374
- if (this.areAllCorrectAnswersDisplayed() || allAttemptsCompleted) {
375
- this.triggerNextLevelWithDelay();
 
 
 
 
 
 
 
 
376
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  } else {
378
- this.error = 'Failed to validate answers. Please try again.';
379
  }
380
 
381
  this.isValidationInProgress = false;
382
  this.isLoading = false;
383
  },
384
  (error) => {
385
- console.error('Error validating answers:', error);
386
- this.error = 'Error validating answers. Please try again later.';
387
  this.isValidationInProgress = false;
388
  this.isLoading = false;
389
- }
390
  );
391
  }
392
 
393
 
394
 
395
-
396
-
397
-
398
-
399
  isLastLevel(): boolean {
400
- return this.currentDifficulty === this.difficultyLevels[this.difficultyLevels.length - 1];
 
 
 
401
  }
402
 
403
-
404
-
405
  triggerNextLevelWithDelay() {
406
  setTimeout(() => {
407
  this.transitionDifficulty();
408
- }, 2000); // Introduce a delay before generating the next level
409
  }
410
 
411
  areAllCorrectAnswersDisplayed(): boolean {
412
- return this.userAnswers.every((answer, index) =>
413
- (answer ?? '').trim().toLowerCase() ===
414
- (this.questionsWithAnswers[index]?.correctAnswer ?? '').trim().toLowerCase()
 
 
 
415
  );
416
-
417
  }
418
 
419
  transitionDifficulty() {
@@ -421,30 +461,27 @@ export class GenerateQuestionsComponent {
421
  if (currentIndex < this.difficultyLevels.length - 1) {
422
  this.currentDifficulty = this.difficultyLevels[currentIndex + 1];
423
  console.log(`Switching to ${this.currentDifficulty} difficulty level.`);
424
- this.generateQuestions(); // Fetch new set of questions for the next difficulty level
425
  } else {
426
  setTimeout(() => {
427
- this.questions = []; // Clear the list
428
  this.isAllLevelsCompleted = true;
429
  this.isChecked = false;
430
  this.isValidationInProgress = false;
431
  this.isLoading = false;
432
- console.log('🎉 You have completed all difficulty levels.');
433
  this.triggerConfetti();
434
- }, 3000); // Ensure a small delay before showing the message
435
  }
436
  }
437
 
438
  shouldShowHint(index: number): boolean {
439
- return this.attemptCounts[index] < 1; // Show hint only on the first attempt
440
  }
441
 
442
-
443
-
444
-
445
  resetAllLevels() {
446
  this.isAllLevelsCompleted = false;
447
- this.currentDifficulty = 'basic';
448
  this.questions = [];
449
  this.userAnswers = [];
450
  this.feedback = [];
@@ -453,48 +490,39 @@ export class GenerateQuestionsComponent {
453
  this.isValidationInProgress = false;
454
  this.isTopicLocked = false;
455
  this.hints = [];
456
- this.error = '';
457
  this.isGenerateDisabled = true;
458
  this.isResetDisabled = false;
459
- console.log('All levels reset. Starting from basic level.');
460
  }
461
 
462
-
463
-
464
-
465
-
466
  onAnswerChange(index: number) {
467
  if (!this.isValidationInProgress) {
468
  this.isChecked = false;
469
  this.validated = false;
470
  }
471
  const userAnswer = this.userAnswers[index]?.trim();
472
- console.log('User Answers:', this.userAnswers);
473
- console.log('Are All Answers Filled:', this.areAllAnswersFilled());
474
  if (userAnswer) {
475
  this.userAnswers[index] = userAnswer;
476
  } else {
477
- this.userAnswers[index] = '';
478
  }
479
  this.userAnswers = [...this.userAnswers];
480
  }
481
 
482
-
483
  goToHome() {
484
- this.router.navigate(['/home']);
485
  }
486
 
487
  private confettiInterval: any;
488
 
489
-
490
-
491
  triggerConfetti() {
492
-
493
  if (this.confettiInterval) {
494
  clearInterval(this.confettiInterval);
495
  }
496
 
497
-
498
  this.confettiInterval = setInterval(() => {
499
  confetti({
500
  startVelocity: 30,
@@ -508,54 +536,29 @@ export class GenerateQuestionsComponent {
508
  }, 250);
509
  }
510
 
511
-
512
-
513
  stopConfetti() {
514
- this.isAllLevelsCompleted = false; // ✅ Hides congratulatory message
515
 
516
- // Clear interval if using confetti animations
517
  if (this.confettiInterval) {
518
  clearInterval(this.confettiInterval);
519
  this.confettiInterval = null;
520
  }
521
 
522
- // Remove canvas or any extra DOM elements related to confetti
523
- const canvas = document.querySelector('canvas.confetti-canvas');
524
  if (canvas) {
525
  canvas.remove();
526
  }
527
 
528
- const confettiDivs = document.querySelectorAll('.confetti, .ts-confetti');
529
- confettiDivs.forEach(el => el.remove());
530
  }
531
 
532
- // Handler for "Start Over" button in congratulation message
533
  stopConfettiAndReset() {
534
  this.stopConfetti();
535
  this.resetTopic();
536
  }
537
 
538
 
539
-
540
-
541
- isHintMenuVisible: boolean = false; // Track visibility of the hint menu
542
-
543
-
544
-
545
- //toggleHintMenu() {
546
- // if (this.hints.length > 0) {
547
- // this.isHintMenuVisible = !this.isHintMenuVisible; // Toggle only if hints exist
548
- // console.log("Hint Menu Toggled:", this.isHintMenuVisible); // Debugging
549
- // }
550
- //}
551
-
552
-
553
-
554
-
555
-
556
-
557
-
558
-
559
  selectTopic(suggestion: string): void {
560
  this.topic = suggestion;
561
  this.showSuggestions = false;
@@ -564,42 +567,27 @@ export class GenerateQuestionsComponent {
564
  this.isDropdownDisabled = true;
565
  }
566
 
567
-
568
-
569
-
570
- // Hide suggestions when clicking outside
571
  hideSuggestions(): void {
572
  setTimeout(() => {
573
  this.showSuggestions = false;
574
  }, 200);
575
  }
576
 
577
-
578
-
579
-
580
- /* // Add this property to your component
581
- hasNewHints: boolean = false;*/
582
-
583
- // Call this function when hints are generated
584
  processHints(hints: string[]) {
585
  this.hints = hints;
586
 
587
- // Set `hasNewHints` to true if there are hints
588
  this.hasNewHints = hints.length > 0;
589
 
590
- // Optionally, stop the blinking after a delay
591
  if (this.hasNewHints) {
592
  setTimeout(() => {
593
- this.hasNewHints = false; // Stop blinking after 5 seconds
594
- }, 5000); // Adjust the delay as needed
595
  }
596
  }
597
 
598
-
599
-
600
  closeHints(): void {
601
  this.isHintMenuVisible = false;
602
- this.hasNewHints = false; // optional: mark hints as seen
603
  }
604
 
605
  openHints(): void {
@@ -609,18 +597,161 @@ export class GenerateQuestionsComponent {
609
  }
610
  }
611
 
612
- /* Keep your existing toggle if you’re calling it from the i button */
613
- toggleHintMenu(): void {
614
- this.isHintMenuVisible ? this.closeHints() : this.openHints();
615
  }
616
 
617
- ///* Close on Esc key */
618
- //@HostListener('document:keydown.escape')
619
- //onEscClose(): void {
620
- // if (this.isHintMenuVisible) this.closeHints();
621
- //}
622
 
623
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
 
625
  }
626
 
 
25
  styleUrls: ['./generate-questions.component.css']
26
  })
27
  export class GenerateQuestionsComponent {
28
+ topic: string = "";
29
+ hardcodedTopics: string[] = [
30
+ "Noun",
31
+ "Verb",
32
+ "Past Tense",
33
+ "Adjective",
34
+ "Present Continuous",
35
+ ];
36
 
37
  showSuggestions: boolean = false;
38
+
39
+ userAnswers: string[] = [];
40
+ feedback: string[] = [];
41
+ error: string = "";
42
+ isChecked: boolean = false;
43
  isAnswerModified: boolean = false;
44
  questions: { parts: string[]; index: number }[] = [];
45
  attemptCounts: { [key: number]: number } = {};
46
  correctAnswers: string[] = [];
47
+ questionsWithAnswers: QuestionWithAnswer[] = [];
48
+ currentDifficulty: string = "basic";
49
+ // difficultyLevels: string[] = ['basic', 'elementary', 'pre-intermediate', 'intermediate', 'upper-intermediate', 'advanced', 'hard'];
50
+ difficultyLevels: string[] = ["basic", "intermediate", "expert"];
51
  isDropdownDisabled: boolean = false;
52
  isValidationInProgress: boolean = false;
53
  isTopicLocked: boolean = false;
54
+ isAllLevelsCompleted: boolean = false;
55
  readonlyAnswers: boolean[] = [];
56
  hints: string[] = [];
57
  attempts: number[] = [];
 
59
  isGenerateDisabled: boolean = true;
60
  isResetDisabled: boolean = true;
61
  isFirstAttemptDone: boolean = false;
62
+ hasNewHints: boolean = false;
63
  isLoading: boolean = false;
64
  isValidationCompleted = false;
65
 
66
  showLevelTooltip: boolean = false;
67
  activateLevelDot: boolean = false;
68
 
69
+ isTyping: boolean = false;
70
+ validated: boolean = false;
71
 
72
+ constructor(
73
+ private generateQuestionsService: GenerateQuestionsService,
74
+ private router: Router,
75
+ private cdr: ChangeDetectorRef,
76
+ ) { }
 
 
 
 
 
 
 
77
 
78
  onTopicChange() {
79
  if (this.topic.trim().length > 0) {
80
+ this.isGenerateDisabled = false;
81
  } else {
82
+ this.isGenerateDisabled = true;
83
  }
84
  if (this.topic.trim().length === 0) {
85
+ this.showSuggestions = true;
86
  } else {
87
+ this.showSuggestions = false;
88
  }
89
  }
 
 
90
 
91
  getProgressWidth(): string {
92
  const index = this.difficultyLevels.indexOf(this.currentDifficulty);
93
+ if (index === -1 || this.difficultyLevels.length <= 1) return "0%";
94
  const percent = (index / (this.difficultyLevels.length - 1)) * 100;
95
  return `${percent}%`;
96
  }
97
 
98
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  generateQuestions() {
101
  this.hints = [];
102
+ this.isQuestionsGenerated = false;
103
  this.showLevelTooltip = true;
104
  this.activateLevelDot = true;
105
 
 
106
  if (this.isAllLevelsCompleted) {
107
+ this.error = "Please reset to start over.";
108
  return;
109
  }
 
 
 
 
 
110
 
111
+ this.isTopicLocked = true;
112
+ this.showSuggestions = false;
113
+ this.readonlyAnswers = [];
114
+ this.isDropdownDisabled = true;
 
 
 
115
 
116
+ console.log(`Generating questions for difficulty level: ${this.currentDifficulty}`);
 
117
 
118
+ this.generateQuestionsService
119
+ .generateQuestions(this.topic, this.currentDifficulty)
120
+ .subscribe(
121
+ (response) => {
122
+ console.log("Response from backend:", response);
123
 
124
+ if (response.generations && response.generations.length > 0) {
125
+ const rawQuestions = response.generations[0].text;
126
+ console.log("Raw Questions:", rawQuestions);
127
+ this.isQuestionsGenerated = true;
128
 
129
+ this.isGenerateDisabled = true;
130
+ this.isResetDisabled = false;
131
 
132
+ this.questionsWithAnswers = [];
 
133
 
134
+ // same regex you already use
135
+ const regex = /(\d+\.\s*.+?_______)\s*(.*?)\s*\(([^)]+)\)\s*$/gm;
136
+ let match;
137
 
138
+ while ((match = regex.exec(rawQuestions)) !== null) {
139
+ console.log("Match:", match);
 
 
 
 
 
140
 
141
+ const questionText = (match[1] ?? "").trim(); // up to _______
142
+ const afterBlankRaw = (match[2] ?? ""); // text after the blank
143
+ const correctAnswer = (match[3] ?? "").trim(); // answer in parentheses
144
 
145
+ // NEW: clean any stray "_" or "-" that sometimes appear right after the blank
146
+ const afterBlankClean = afterBlankRaw
147
+ .replace(/^[_-]+/, "") // drop leading underscores/dashes like "_ moment"
148
+ .replace(/^\s+/, " ") // ensure exactly one leading space if any
149
+ .replace(/\s{2,}/g, " "); // collapse multiple spaces
150
 
151
+ if (questionText && correctAnswer) {
152
+ this.questionsWithAnswers.push({
153
+ question: `${questionText}${afterBlankClean}`,
154
+ correctAnswer,
155
+ });
156
+ }
157
  }
158
 
159
+ console.log("Extracted Questions with Answers:", this.questionsWithAnswers);
 
160
 
161
+ // Build renderable questions (parts before/after the blank)
162
+ this.questions = this.questionsWithAnswers.map((q, index) => {
163
+ // NEW: normalize any odd spacing around the marker
164
+ const normalized = q.question.replace(/\s*_{3,}\s*/g, "_______");
165
+ const parts = normalized.split("_______");
166
 
167
+ if (parts.length === 2) {
168
+ // NEW: remove any stray underscores touching the gap
169
+ parts[0] = parts[0].replace(/_+$/, "").trim();
170
+ parts[1] = parts[1].replace(/^_+/, "").trim();
171
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
+ return { parts, index };
174
+ });
175
 
176
+ this.userAnswers = new Array(this.questions.length).fill("");
177
+ this.feedback = new Array(this.questions.length).fill("");
178
+ this.readonlyAnswers = new Array(this.questions.length).fill(false);
179
+ this.isChecked = false;
180
+
181
+ this.attemptCounts = this.questions.reduce((acc, _q, i) => {
182
+ acc[i] = 0;
183
+ return acc;
184
+ }, {} as { [key: number]: number });
185
+
186
+ console.log("Questions without answers:", this.questions);
187
+ } else {
188
+ this.error = "No questions generated. Please try again.";
189
+ }
190
+ },
191
+ (error) => {
192
+ if (error.status === 400 && error.error.message) {
193
+ this.error = error.error.message;
194
+ } else {
195
+ this.error = "Failed to fetch questions. Please try again later.";
196
+ }
197
+ console.error("Error fetching questions:", error);
198
+ },
199
+ );
200
+ }
201
 
202
 
203
 
204
  resetTopic() {
205
+ this.topic = "";
206
  this.questions = [];
207
  this.questionsWithAnswers = [];
208
  this.userAnswers = [];
 
218
  this.isQuestionsGenerated = false;
219
  this.isTopicLocked = false;
220
  this.validated = false;
221
+ this.currentDifficulty = "basic";
222
+ this.error = "";
223
  this.isGenerateDisabled = false;
224
  this.isResetDisabled = true;
225
  this.showSuggestions = false;
226
  this.showLevelTooltip = false;
227
  this.activateLevelDot = false;
228
  this.showSuggestions = false;
229
+ this.isDropdownDisabled = false;
230
+ this.isTopicLocked = false;
 
 
 
 
231
  this.stopConfetti();
232
+ // Stop any active countdowns
233
+ Object.keys(this.countdownTimers).forEach(k => clearInterval(this.countdownTimers[+k]));
234
+ this.countdownTimers = {};
235
+ this.countdowns = [];
236
+ this.showCountdown = [];
237
+ this.stopGlobalCountdown();
238
+ this.stopOverlay()
239
 
240
+ }
241
 
242
  areAllAnswersFilled(): boolean {
243
+ return this.userAnswers.every((answer) => answer?.trim() !== "");
244
  }
245
 
 
 
 
246
  closeErrorPopup() {
247
+ this.error = "";
248
+ this.isGenerateDisabled = true;
249
+ this.isResetDisabled = false;
250
  }
251
 
252
+
253
 
254
+ // Hint icon one-time flags (ADD THESE)
255
+ showHintIcon: boolean = false; // controls rendering of the “i” button
256
+ hasShownHintIcon: boolean = false; // remembers if it was shown once
257
 
258
+ // If you call toggleHintMenu(), also ensure this exists:
259
+ isHintMenuVisible: boolean = false; // remove if you already have it
260
 
261
 
262
  checkAllAnswers() {
 
269
 
270
  const requestPayload = this.questions.map((questionObj, index) => ({
271
  topic: this.topic,
272
+ question: questionObj.parts.join("_______"),
273
  user_answer: this.userAnswers[index],
274
  }));
275
 
276
  console.log("Checking answers...");
277
  console.log("User Answers:", this.userAnswers);
278
+ console.log(
279
+ "Correct Answers:",
280
+ this.questionsWithAnswers.map((q) => q.correctAnswer),
281
+ );
282
 
283
  this.generateQuestionsService.validateAllAnswers(requestPayload).subscribe(
284
  (response) => {
285
+ console.log("Response from backend for all answers validation:", response);
286
 
287
  if (response.results && response.results.length > 0) {
288
  let allAttemptsCompleted = true;
289
 
290
+ // Track what happened this validation cycle
291
+ let hadFirstAttemptWrong = false; // someone is on first wrong attempt
292
+ let hadSecondAttemptWrong = false; // someone is on second wrong attempt (autofill path)
293
+
294
  response.results.forEach((result: any, index: number) => {
295
  this.attemptCounts[index]++;
296
  const validationResponse = result.validation_response;
297
+ const hint = result.hint ? result.hint.trim() : "";
298
+
299
+ const userAnswerRaw = this.userAnswers[index] ?? '';
300
+ const userAnswer = userAnswerRaw.trim().toLowerCase();
301
+
302
+ const correctAnswerRaw = this.questionsWithAnswers[index].correctAnswer ?? '';
303
+ const correctAnswer = correctAnswerRaw.trim().toLowerCase();
304
 
305
  console.log(`Validation Response for Question ${index + 1}: ${validationResponse}`);
306
  console.log(`User Answer: ${userAnswer}, Correct Answer: ${correctAnswer}`);
 
308
  if (userAnswer === correctAnswer) {
309
  console.log(`✅ Correct answer for question ${index + 1}: ${this.userAnswers[index]}`);
310
  this.readonlyAnswers[index] = true;
311
+ this.hints[index] = "";
 
312
  this.userAnswers[index] = this.questionsWithAnswers[index].correctAnswer;
313
  } else {
314
  console.log(`❌ Incorrect answer for question ${index + 1}: ${this.userAnswers[index]}`);
315
 
316
+ // ====== 1) FIRST WRONG ATTEMPT: show "Try again" countdown, then clear input ======
317
+ if (this.attemptCounts[index] === 1) {
318
+ hadFirstAttemptWrong = true;
319
+ if (!this.globalCountdownActive) {
320
+ this.startOverlay("Try again in");
321
+ }
322
+
323
+ setTimeout(() => {
324
  console.log(`🔄 Resetting incorrect answer for Question ${index + 1}`);
325
+ this.userAnswers[index] = ""; // give user a clean slate
326
+ this.stopOverlay(); // ensure the HUD disappears
327
+ }, this.COUNTDOWN_SECS * 1000);
328
+ }
329
+ // ====== 2) SECOND WRONG ATTEMPT: show "Showing correct answer" countdown, then autofill ======
330
+ else if (this.attemptCounts[index] >= 2) {
331
+ hadSecondAttemptWrong = true;
332
+ if (!this.globalCountdownActive) {
333
+ this.startOverlay("Showing correct answer in");
334
  }
335
 
336
+ setTimeout(() => {
337
+ // after countdown, force the correct answer
338
  if (!this.userAnswers[index] || this.userAnswers[index].trim() === "" || userAnswer !== correctAnswer) {
339
  this.userAnswers[index] = this.questionsWithAnswers[index].correctAnswer;
340
  console.log(`✔️ Auto-filled correct answer for Question ${index + 1}: ${this.userAnswers[index]}`);
341
  }
342
+ this.stopOverlay(); // hide the first overlay (showing correct answer)
343
+
344
+ // ====== 3) LAST LEVEL: show a second countdown before congratulations ======
345
  if (this.isLastLevel() && index === this.questions.length - 1) {
346
+ // Give time to read the just-filled correct answer
347
+ this.startOverlay("Finishing in");
348
  setTimeout(() => {
349
+ this.stopOverlay();
350
  this.isAllLevelsCompleted = true;
351
+ console.log("🎉 You have completed all difficulty levels.");
352
  this.triggerConfetti();
353
+ }, this.COUNTDOWN_SECS * 1000);
354
  }
355
+
356
+ }, this.COUNTDOWN_SECS * 1000);
357
+ }
358
 
359
  this.hints[index] = hint.length > 0 ? hint : null;
360
  }
361
 
362
+ this.feedback[index] = validationResponse || "No feedback provided.";
363
 
364
+ // minor 2s reflection (kept as-is)
365
  setTimeout(() => {
366
  if (userAnswer === correctAnswer) {
367
  this.userAnswers[index] = this.userAnswers[index];
 
375
  }
376
  });
377
 
378
+ // Hints auto-hide (unchanged)
379
+ this.hasNewHints = this.hints.some((h) => h && h.trim() !== "");
380
  if (this.hasNewHints) {
381
+ setTimeout(() => { this.hasNewHints = false; }, 5000);
 
 
382
  }
383
 
384
+ // --- ONE-TIME “i” icon reveal right after the first validation ---
385
+ // Show once if there are any hints OR there were wrong attempts this cycle.
386
+ const hasAnyHint = Array.isArray(this.hints) && this.hints.some(h => !!h && h.trim() !== "");
387
+ const shouldRevealIconOnce = hasAnyHint || hadFirstAttemptWrong || hadSecondAttemptWrong;
388
+
389
+ if (!this.hasShownHintIcon && shouldRevealIconOnce) {
390
+ this.showHintIcon = true; // reveal once
391
+ this.hasShownHintIcon = true; // lock it
392
+ } else {
393
+ this.showHintIcon = false; // never show again in later validations
394
  }
395
+ // --- END ONE-TIME “i” icon reveal ---
396
+
397
+ if (this.isLastLevel() && this.areAllCorrectAnswersDisplayed()) {
398
+ // Last level, all correct: show finish counter, then congratulations
399
+ this.stopOverlay(); // ensure no old timer remains
400
+ this.startOverlay("Finishing in");
401
+ setTimeout(() => {
402
+ this.stopOverlay();
403
+ this.isAllLevelsCompleted = true;
404
+ this.triggerConfetti();
405
+ }, this.COUNTDOWN_SECS * 1000);
406
+
407
+ } else if (!this.isLastLevel() && (this.areAllCorrectAnswersDisplayed() || allAttemptsCompleted)) {
408
+ // Not last level: even if all answers were correct on first try,
409
+ // show the counter before moving to the next level
410
+ this.stopOverlay(); // ensure fresh HUD
411
+ this.startOverlay("Moving to next level in");
412
+ setTimeout(() => {
413
+ this.stopOverlay();
414
+ this.triggerNextLevelWithDelay();
415
+ }, this.COUNTDOWN_SECS * 1000);
416
+ }
417
+
418
  } else {
419
+ this.error = "Failed to validate answers. Please try again.";
420
  }
421
 
422
  this.isValidationInProgress = false;
423
  this.isLoading = false;
424
  },
425
  (error) => {
426
+ console.error("Error validating answers:", error);
427
+ this.error = "Error validating answers. Please try again later.";
428
  this.isValidationInProgress = false;
429
  this.isLoading = false;
430
+ },
431
  );
432
  }
433
 
434
 
435
 
 
 
 
 
436
  isLastLevel(): boolean {
437
+ return (
438
+ this.currentDifficulty ===
439
+ this.difficultyLevels[this.difficultyLevels.length - 1]
440
+ );
441
  }
442
 
 
 
443
  triggerNextLevelWithDelay() {
444
  setTimeout(() => {
445
  this.transitionDifficulty();
446
+ }, 2000);
447
  }
448
 
449
  areAllCorrectAnswersDisplayed(): boolean {
450
+ return this.userAnswers.every(
451
+ (answer, index) =>
452
+ (answer ?? "").trim().toLowerCase() ===
453
+ (this.questionsWithAnswers[index]?.correctAnswer ?? "")
454
+ .trim()
455
+ .toLowerCase(),
456
  );
 
457
  }
458
 
459
  transitionDifficulty() {
 
461
  if (currentIndex < this.difficultyLevels.length - 1) {
462
  this.currentDifficulty = this.difficultyLevels[currentIndex + 1];
463
  console.log(`Switching to ${this.currentDifficulty} difficulty level.`);
464
+ this.generateQuestions();
465
  } else {
466
  setTimeout(() => {
467
+ this.questions = [];
468
  this.isAllLevelsCompleted = true;
469
  this.isChecked = false;
470
  this.isValidationInProgress = false;
471
  this.isLoading = false;
472
+ console.log("🎉 You have completed all difficulty levels.");
473
  this.triggerConfetti();
474
+ }, 3000);
475
  }
476
  }
477
 
478
  shouldShowHint(index: number): boolean {
479
+ return this.attemptCounts[index] < 1;
480
  }
481
 
 
 
 
482
  resetAllLevels() {
483
  this.isAllLevelsCompleted = false;
484
+ this.currentDifficulty = "basic";
485
  this.questions = [];
486
  this.userAnswers = [];
487
  this.feedback = [];
 
490
  this.isValidationInProgress = false;
491
  this.isTopicLocked = false;
492
  this.hints = [];
493
+ this.error = "";
494
  this.isGenerateDisabled = true;
495
  this.isResetDisabled = false;
496
+ console.log("All levels reset. Starting from basic level.");
497
  }
498
 
 
 
 
 
499
  onAnswerChange(index: number) {
500
  if (!this.isValidationInProgress) {
501
  this.isChecked = false;
502
  this.validated = false;
503
  }
504
  const userAnswer = this.userAnswers[index]?.trim();
505
+ console.log("User Answers:", this.userAnswers);
506
+ console.log("Are All Answers Filled:", this.areAllAnswersFilled());
507
  if (userAnswer) {
508
  this.userAnswers[index] = userAnswer;
509
  } else {
510
+ this.userAnswers[index] = "";
511
  }
512
  this.userAnswers = [...this.userAnswers];
513
  }
514
 
 
515
  goToHome() {
516
+ this.router.navigate(["/home"]);
517
  }
518
 
519
  private confettiInterval: any;
520
 
 
 
521
  triggerConfetti() {
 
522
  if (this.confettiInterval) {
523
  clearInterval(this.confettiInterval);
524
  }
525
 
 
526
  this.confettiInterval = setInterval(() => {
527
  confetti({
528
  startVelocity: 30,
 
536
  }, 250);
537
  }
538
 
 
 
539
  stopConfetti() {
540
+ this.isAllLevelsCompleted = false;
541
 
 
542
  if (this.confettiInterval) {
543
  clearInterval(this.confettiInterval);
544
  this.confettiInterval = null;
545
  }
546
 
547
+ const canvas = document.querySelector("canvas.confetti-canvas");
 
548
  if (canvas) {
549
  canvas.remove();
550
  }
551
 
552
+ const confettiDivs = document.querySelectorAll(".confetti, .ts-confetti");
553
+ confettiDivs.forEach((el) => el.remove());
554
  }
555
 
 
556
  stopConfettiAndReset() {
557
  this.stopConfetti();
558
  this.resetTopic();
559
  }
560
 
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  selectTopic(suggestion: string): void {
563
  this.topic = suggestion;
564
  this.showSuggestions = false;
 
567
  this.isDropdownDisabled = true;
568
  }
569
 
 
 
 
 
570
  hideSuggestions(): void {
571
  setTimeout(() => {
572
  this.showSuggestions = false;
573
  }, 200);
574
  }
575
 
 
 
 
 
 
 
 
576
  processHints(hints: string[]) {
577
  this.hints = hints;
578
 
 
579
  this.hasNewHints = hints.length > 0;
580
 
 
581
  if (this.hasNewHints) {
582
  setTimeout(() => {
583
+ this.hasNewHints = false;
584
+ }, 5000);
585
  }
586
  }
587
 
 
 
588
  closeHints(): void {
589
  this.isHintMenuVisible = false;
590
+ this.hasNewHints = false;
591
  }
592
 
593
  openHints(): void {
 
597
  }
598
  }
599
 
600
+ toggleHintMenu() {
601
+ this.isHintMenuVisible = !this.isHintMenuVisible;
602
+ if (this.isHintMenuVisible) this.showHintIcon = false; // hide after first use
603
  }
604
 
 
 
 
 
 
605
 
606
+ onInput(event: any, index: number) {
607
+ const value = event.target.value;
608
+ const filteredValue = value.replace(/[^a-zA-Z]/g, "");
609
+ this.userAnswers[index] = filteredValue;
610
+ event.target.value = filteredValue;
611
+ }
612
+
613
+
614
+
615
+
616
+ //timer implemetation
617
+
618
+ // --- Countdown state (per question) ---
619
+ countdowns: number[] = []; // current number (5..0) for each question
620
+ showCountdown: boolean[] = []; // whether to show the badge
621
+ private countdownTimers: { [idx: number]: any } = {}; // interval handles
622
+ private readonly COUNTDOWN_SECS = 10;
623
+ // === Global countdown overlay state ===
624
+ showGlobalCountdown = false; // controls the centered overlay
625
+ globalCountdown = 0; // 5..1
626
+ private globalCountdownActive = false;
627
+ private globalTimer: any = null;
628
+
629
+ overlayCaption = '';
630
+
631
+
632
+
633
+
634
+ private beginResetCountdown(index: number, seconds: number = this.COUNTDOWN_SECS): void {
635
+ // avoid duplicate timers for the same question
636
+ if (this.countdownTimers[index]) { return; }
637
+
638
+ this.countdowns[index] = seconds;
639
+ this.showCountdown[index] = true;
640
+
641
+ this.countdownTimers[index] = setInterval(() => {
642
+ this.countdowns[index] = this.countdowns[index] - 1;
643
+
644
+ if (this.countdowns[index] <= 0) {
645
+ this.finishCountdown(index);
646
+ }
647
+ }, 1000);
648
+ }
649
+
650
+ /** Stop timer, hide badge, clear the wrong input, and allow retry. */
651
+ private finishCountdown(index: number): void {
652
+ const t = this.countdownTimers[index];
653
+ if (t) {
654
+ clearInterval(t);
655
+ delete this.countdownTimers[index];
656
+ }
657
+
658
+ this.showCountdown[index] = false;
659
+ this.countdowns[index] = 0;
660
+
661
+ // Clear only the wrong answer and allow the learner to try again.
662
+ this.userAnswers[index] = '';
663
+
664
+ // Turn off the global "checked" flag so inputs are not stuck red.
665
+ // (If you track per-question flags, toggle those instead.)
666
+ this.isChecked = false;
667
+ }
668
+
669
+ private startGlobalCountdown(): void {
670
+ if (this.globalCountdownActive) return; // prevent duplicates
671
+ this.globalCountdownActive = true;
672
+ this.showGlobalCountdown = true;
673
+ this.globalCountdown = this.COUNTDOWN_SECS;
674
+
675
+ this.globalTimer = setInterval(() => {
676
+ this.globalCountdown--;
677
+ if (this.globalCountdown <= 0) {
678
+ this.stopGlobalCountdown();
679
+ }
680
+ }, 1000);
681
+ }
682
+
683
+ private stopGlobalCountdown(): void {
684
+ if (this.globalTimer) {
685
+ clearInterval(this.globalTimer);
686
+ this.globalTimer = null;
687
+ }
688
+ this.showGlobalCountdown = false;
689
+ this.globalCountdownActive = false;
690
+ this.globalCountdown = 0;
691
+ }
692
+
693
+ ngOnDestroy(): void {
694
+ this.stopGlobalCountdown();
695
+ }
696
+ private startOverlay(baseText: string, seconds: number = this.COUNTDOWN_SECS): void {
697
+ if (this.globalTimer) { clearTimeout(this.globalTimer as any); this.globalTimer = null; }
698
+ if (this.overlayTicker) { clearInterval(this.overlayTicker); this.overlayTicker = null; }
699
+
700
+ this.globalCountdownActive = true;
701
+ this.showGlobalCountdown = true;
702
+ this.overlayCaption = baseText;
703
+
704
+ this.globalCountdown = seconds;
705
+ this.overlayEndTs = Date.now() + seconds * 1000;
706
+ this.ringDashoffset = this.ringCircumference;
707
+
708
+ this.overlayTicker = setInterval(() => {
709
+ const now = Date.now();
710
+ const remainingMs = Math.max(0, this.overlayEndTs - now);
711
+ const elapsedMs = seconds * 1000 - remainingMs;
712
+
713
+ this.globalCountdown = Math.ceil(remainingMs / 1000);
714
+
715
+ const progress = Math.min(1, elapsedMs / (seconds * 1000));
716
+ this.ringDashoffset = this.ringCircumference * (1 - progress);
717
+
718
+ if (remainingMs <= 0) this.stopOverlay();
719
+ }, 50) as any;
720
+
721
+ this.globalTimer = setTimeout(() => this.stopOverlay(), seconds * 1000) as any;
722
+ }
723
+
724
+ private stopOverlay(): void {
725
+ if (this.overlayTicker) { clearInterval(this.overlayTicker); this.overlayTicker = null; }
726
+ if (this.globalTimer) { clearTimeout(this.globalTimer as any); this.globalTimer = null; }
727
+
728
+ this.showGlobalCountdown = false;
729
+ this.globalCountdownActive = false;
730
+ this.overlayCaption = '';
731
+ this.globalCountdown = 0;
732
+ this.ringDashoffset = this.ringCircumference;
733
+ }
734
+
735
+
736
+
737
+
738
+
739
+ ringRadius = 90;
740
+ ringCircumference = 2 * Math.PI * this.ringRadius;
741
+ ringDashoffset = this.ringCircumference;
742
+
743
+
744
+ private overlayTicker: any = null;
745
+ private overlayEndTs = 0;
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
 
756
  }
757