@thanh01.pmt/interactive-quiz-kit 1.0.23 → 1.0.25
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/dist/ai-ecosystem-BJ5RR5Ys.d.ts +228 -0
- package/dist/ai-ecosystem-CL30v1Lg.d.cts +228 -0
- package/dist/ai.cjs +181 -227
- package/dist/ai.d.cts +1881 -0
- package/dist/ai.d.ts +1881 -0
- package/dist/ai.js +181 -227
- package/dist/authoring.cjs +6401 -2528
- package/dist/authoring.d.cts +12 -0
- package/dist/authoring.d.ts +12 -0
- package/dist/authoring.js +6281 -2425
- package/dist/index.cjs +374 -174
- package/dist/index.d.cts +444 -0
- package/dist/index.d.ts +444 -0
- package/dist/index.js +373 -175
- package/dist/player.cjs +1491 -1086
- package/dist/player.d.cts +13 -0
- package/dist/player.d.ts +13 -0
- package/dist/player.js +1424 -1019
- package/dist/quiz-config-1gNNhljP.d.cts +197 -0
- package/dist/quiz-config-1gNNhljP.d.ts +197 -0
- package/dist/react-ui.cjs +8621 -3688
- package/dist/react-ui.d.cts +207 -0
- package/dist/react-ui.d.ts +207 -0
- package/dist/react-ui.js +8321 -3404
- package/dist/toaster-CtnxWhfE.d.ts +133 -0
- package/dist/toaster-DUq851l_.d.cts +133 -0
- package/package.json +13 -11
package/dist/player.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React9 = require('react');
|
|
4
4
|
var ReactDOM = require('react-dom/client');
|
|
5
5
|
var reactI18next = require('react-i18next');
|
|
6
6
|
var zod = require('zod');
|
|
@@ -10,6 +10,7 @@ var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
|
|
|
10
10
|
var lucideReact = require('lucide-react');
|
|
11
11
|
var clsx = require('clsx');
|
|
12
12
|
var tailwindMerge = require('tailwind-merge');
|
|
13
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
13
14
|
var LabelPrimitive = require('@radix-ui/react-label');
|
|
14
15
|
var classVarianceAuthority = require('class-variance-authority');
|
|
15
16
|
var ReactMarkdown = require('react-markdown');
|
|
@@ -49,7 +50,7 @@ function _interopNamespace(e) {
|
|
|
49
50
|
return Object.freeze(n);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
var
|
|
53
|
+
var React9__namespace = /*#__PURE__*/_interopNamespace(React9);
|
|
53
54
|
var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
|
|
54
55
|
var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
|
|
55
56
|
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
@@ -66,37 +67,7 @@ var ProgressPrimitive__namespace = /*#__PURE__*/_interopNamespace(ProgressPrimit
|
|
|
66
67
|
var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrimitive);
|
|
67
68
|
var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
var __defProps = Object.defineProperties;
|
|
71
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
72
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
73
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
74
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
75
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
76
|
-
var __spreadValues = (a, b) => {
|
|
77
|
-
for (var prop in b || (b = {}))
|
|
78
|
-
if (__hasOwnProp.call(b, prop))
|
|
79
|
-
__defNormalProp(a, prop, b[prop]);
|
|
80
|
-
if (__getOwnPropSymbols)
|
|
81
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
82
|
-
if (__propIsEnum.call(b, prop))
|
|
83
|
-
__defNormalProp(a, prop, b[prop]);
|
|
84
|
-
}
|
|
85
|
-
return a;
|
|
86
|
-
};
|
|
87
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
88
|
-
var __objRest = (source, exclude) => {
|
|
89
|
-
var target = {};
|
|
90
|
-
for (var prop in source)
|
|
91
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
92
|
-
target[prop] = source[prop];
|
|
93
|
-
if (source != null && __getOwnPropSymbols)
|
|
94
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
95
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
96
|
-
target[prop] = source[prop];
|
|
97
|
-
}
|
|
98
|
-
return target;
|
|
99
|
-
};
|
|
70
|
+
// src/player.ts
|
|
100
71
|
|
|
101
72
|
// src/services/SCORMService.ts
|
|
102
73
|
var SCORM_TRUE = "true";
|
|
@@ -117,11 +88,12 @@ var SCORMService = class {
|
|
|
117
88
|
this.isInitialized = false;
|
|
118
89
|
this.isTerminated = false;
|
|
119
90
|
this.studentName = null;
|
|
120
|
-
this.settings =
|
|
91
|
+
this.settings = {
|
|
121
92
|
setCompletionOnFinish: true,
|
|
122
93
|
setSuccessOnPass: true,
|
|
123
|
-
autoCommit: true
|
|
124
|
-
|
|
94
|
+
autoCommit: true,
|
|
95
|
+
...settings
|
|
96
|
+
};
|
|
125
97
|
if (typeof window !== "undefined") {
|
|
126
98
|
this._findAPI();
|
|
127
99
|
}
|
|
@@ -227,14 +199,13 @@ var SCORMService = class {
|
|
|
227
199
|
}
|
|
228
200
|
}
|
|
229
201
|
getValue(element) {
|
|
230
|
-
var _a;
|
|
231
202
|
if (!this.hasAPI() || !this.isInitialized) return null;
|
|
232
203
|
const value = this.scormVersionFound === "2004" ? this.scormAPI.GetValue(element) : this.scormAPI.LMSGetValue(element);
|
|
233
204
|
const error = this.getLastError();
|
|
234
205
|
if (error.code !== SCORM_NO_ERROR && error.code !== "403" && error.code !== "0") {
|
|
235
206
|
console.warn(`SCORMService: GetValue for ${element} produced an error ${error.code}: ${error.message}. Returning raw value:`, value);
|
|
236
207
|
}
|
|
237
|
-
return
|
|
208
|
+
return value?.toString() ?? null;
|
|
238
209
|
}
|
|
239
210
|
commit() {
|
|
240
211
|
if (!this.hasAPI() || !this.isInitialized) {
|
|
@@ -306,7 +277,6 @@ var SCORMService = class {
|
|
|
306
277
|
}
|
|
307
278
|
}
|
|
308
279
|
getLastError() {
|
|
309
|
-
var _a, _b;
|
|
310
280
|
if (!this.hasAPI()) return { code: "-1", message: "SCORM API not found." };
|
|
311
281
|
const errorCode = this.scormVersionFound === "2004" ? this.scormAPI.GetLastError() : this.scormAPI.LMSGetLastError();
|
|
312
282
|
if (errorCode === SCORM_NO_ERROR || errorCode === 0 || errorCode === "0") {
|
|
@@ -316,8 +286,8 @@ var SCORMService = class {
|
|
|
316
286
|
const diagnostic = this.scormVersionFound === "2004" ? this.scormAPI.GetDiagnostic(errorCode.toString()) : this.scormAPI.LMSGetDiagnostic(errorCode.toString());
|
|
317
287
|
return {
|
|
318
288
|
code: errorCode.toString(),
|
|
319
|
-
message:
|
|
320
|
-
diagnostic:
|
|
289
|
+
message: errorMessage?.toString() ?? "Unknown error.",
|
|
290
|
+
diagnostic: diagnostic?.toString() ?? void 0
|
|
321
291
|
};
|
|
322
292
|
}
|
|
323
293
|
formatCMITime(totalSeconds) {
|
|
@@ -348,14 +318,13 @@ var SCORMService = class {
|
|
|
348
318
|
// src/services/evaluators/multiple-choice-evaluator.ts
|
|
349
319
|
var MultipleChoiceEvaluator = class {
|
|
350
320
|
async evaluate(question, answer) {
|
|
351
|
-
|
|
352
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
321
|
+
const points = question.points ?? 0;
|
|
353
322
|
const correctAnswerId = question.correctAnswerId;
|
|
354
323
|
const isCorrect = answer === correctAnswerId;
|
|
355
324
|
const correctOption = question.options.find((opt) => opt.id === correctAnswerId);
|
|
356
325
|
const correctAnswerDetail = {
|
|
357
326
|
id: correctAnswerId,
|
|
358
|
-
value:
|
|
327
|
+
value: correctOption?.text || ""
|
|
359
328
|
};
|
|
360
329
|
return Promise.resolve({
|
|
361
330
|
isCorrect,
|
|
@@ -368,8 +337,7 @@ var MultipleChoiceEvaluator = class {
|
|
|
368
337
|
// src/services/evaluators/multiple-response-evaluator.ts
|
|
369
338
|
var MultipleResponseEvaluator = class {
|
|
370
339
|
async evaluate(question, answer) {
|
|
371
|
-
|
|
372
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
340
|
+
const points = question.points ?? 0;
|
|
373
341
|
const correctAnswerIds = question.correctAnswerIds;
|
|
374
342
|
let isCorrect = false;
|
|
375
343
|
if (Array.isArray(answer)) {
|
|
@@ -378,10 +346,7 @@ var MultipleResponseEvaluator = class {
|
|
|
378
346
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
379
347
|
}
|
|
380
348
|
const correctValues = correctAnswerIds.map(
|
|
381
|
-
(id) =>
|
|
382
|
-
var _a2;
|
|
383
|
-
return ((_a2 = question.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
384
|
-
}
|
|
349
|
+
(id) => question.options.find((opt) => opt.id === id)?.text || ""
|
|
385
350
|
);
|
|
386
351
|
const correctAnswerDetail = {
|
|
387
352
|
id: correctAnswerIds,
|
|
@@ -398,8 +363,7 @@ var MultipleResponseEvaluator = class {
|
|
|
398
363
|
// src/services/evaluators/true-false-evaluator.ts
|
|
399
364
|
var TrueFalseEvaluator = class {
|
|
400
365
|
async evaluate(question, answer) {
|
|
401
|
-
|
|
402
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
366
|
+
const points = question.points ?? 0;
|
|
403
367
|
const correctAnswer = question.correctAnswer;
|
|
404
368
|
let userAnswer = answer;
|
|
405
369
|
if (typeof answer === "string") {
|
|
@@ -421,12 +385,11 @@ var TrueFalseEvaluator = class {
|
|
|
421
385
|
// src/services/evaluators/short-answer-evaluator.ts
|
|
422
386
|
var ShortAnswerEvaluator = class {
|
|
423
387
|
async evaluate(question, answer) {
|
|
424
|
-
|
|
425
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
388
|
+
const points = question.points ?? 0;
|
|
426
389
|
let isCorrect = false;
|
|
427
390
|
if (typeof answer === "string") {
|
|
428
391
|
const userAnswerTrimmed = answer.trim();
|
|
429
|
-
const caseSensitive =
|
|
392
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
430
393
|
isCorrect = question.acceptedAnswers.some(
|
|
431
394
|
(accAns) => caseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
|
|
432
395
|
);
|
|
@@ -446,8 +409,7 @@ var ShortAnswerEvaluator = class {
|
|
|
446
409
|
// src/services/evaluators/numeric-evaluator.ts
|
|
447
410
|
var NumericEvaluator = class {
|
|
448
411
|
async evaluate(question, answer) {
|
|
449
|
-
|
|
450
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
412
|
+
const points = question.points ?? 0;
|
|
451
413
|
let isCorrect = false;
|
|
452
414
|
if (typeof answer === "string" || typeof answer === "number") {
|
|
453
415
|
const userAnswerNum = parseFloat(String(answer));
|
|
@@ -470,17 +432,13 @@ var NumericEvaluator = class {
|
|
|
470
432
|
// src/services/evaluators/sequence-evaluator.ts
|
|
471
433
|
var SequenceEvaluator = class {
|
|
472
434
|
async evaluate(question, answer) {
|
|
473
|
-
|
|
474
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
435
|
+
const points = question.points ?? 0;
|
|
475
436
|
let isCorrect = false;
|
|
476
437
|
if (Array.isArray(answer) && answer.length === question.correctOrder.length) {
|
|
477
438
|
isCorrect = answer.every((itemId, index) => itemId === question.correctOrder[index]);
|
|
478
439
|
}
|
|
479
440
|
const correctValues = question.correctOrder.map(
|
|
480
|
-
(id) =>
|
|
481
|
-
var _a2;
|
|
482
|
-
return ((_a2 = question.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
483
|
-
}
|
|
441
|
+
(id) => question.items.find((item) => item.id === id)?.content || ""
|
|
484
442
|
);
|
|
485
443
|
const correctAnswerDetail = {
|
|
486
444
|
id: question.correctOrder,
|
|
@@ -497,17 +455,15 @@ var SequenceEvaluator = class {
|
|
|
497
455
|
// src/services/evaluators/matching-evaluator.ts
|
|
498
456
|
var MatchingEvaluator = class {
|
|
499
457
|
async evaluate(question, answer) {
|
|
500
|
-
|
|
501
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
458
|
+
const points = question.points ?? 0;
|
|
502
459
|
let isCorrect = false;
|
|
503
460
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
504
461
|
const userAnswerMap = answer;
|
|
505
462
|
isCorrect = question.correctAnswerMap.length === Object.keys(userAnswerMap).length && question.correctAnswerMap.every((map) => userAnswerMap[map.promptId] === map.optionId);
|
|
506
463
|
}
|
|
507
464
|
const correctMap = question.correctAnswerMap.reduce((acc, curr) => {
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
const optionText = ((_b = question.options.find((o) => o.id === curr.optionId)) == null ? void 0 : _b.content) || "";
|
|
465
|
+
const promptText = question.prompts.find((p) => p.id === curr.promptId)?.content || "";
|
|
466
|
+
const optionText = question.options.find((o) => o.id === curr.optionId)?.content || "";
|
|
511
467
|
acc[promptText] = optionText;
|
|
512
468
|
return acc;
|
|
513
469
|
}, {});
|
|
@@ -526,16 +482,14 @@ var MatchingEvaluator = class {
|
|
|
526
482
|
// src/services/evaluators/fill-in-the-blanks-evaluator.ts
|
|
527
483
|
var FillInTheBlanksEvaluator = class {
|
|
528
484
|
async evaluate(question, answer) {
|
|
529
|
-
|
|
530
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
485
|
+
const points = question.points ?? 0;
|
|
531
486
|
let isCorrect = false;
|
|
532
487
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
533
488
|
const userAnswerMap = answer;
|
|
534
489
|
isCorrect = question.answers.length > 0 && question.answers.every((correctAnsDef) => {
|
|
535
|
-
|
|
536
|
-
const userValForBlank = (_a2 = userAnswerMap[correctAnsDef.blankId]) == null ? void 0 : _a2.trim();
|
|
490
|
+
const userValForBlank = userAnswerMap[correctAnsDef.blankId]?.trim();
|
|
537
491
|
if (userValForBlank === void 0) return false;
|
|
538
|
-
const caseSensitive =
|
|
492
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
539
493
|
return correctAnsDef.acceptedValues.some(
|
|
540
494
|
(accVal) => caseSensitive ? accVal.trim() === userValForBlank : accVal.trim().toLowerCase() === userValForBlank.toLowerCase()
|
|
541
495
|
);
|
|
@@ -560,17 +514,15 @@ var FillInTheBlanksEvaluator = class {
|
|
|
560
514
|
// src/services/evaluators/drag-and-drop-evaluator.ts
|
|
561
515
|
var DragAndDropEvaluator = class {
|
|
562
516
|
async evaluate(question, answer) {
|
|
563
|
-
|
|
564
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
517
|
+
const points = question.points ?? 0;
|
|
565
518
|
let isCorrect = false;
|
|
566
519
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
567
520
|
const userAnswerMap = answer;
|
|
568
521
|
isCorrect = question.answerMap.length === Object.keys(userAnswerMap).length && question.answerMap.every((map) => userAnswerMap[map.draggableId] === map.dropZoneId);
|
|
569
522
|
}
|
|
570
523
|
const correctMap = question.answerMap.reduce((acc, curr) => {
|
|
571
|
-
|
|
572
|
-
const
|
|
573
|
-
const dropZoneText = ((_b = question.dropZones.find((z3) => z3.id === curr.dropZoneId)) == null ? void 0 : _b.label) || "";
|
|
524
|
+
const draggableText = question.draggableItems.find((d) => d.id === curr.draggableId)?.content || "";
|
|
525
|
+
const dropZoneText = question.dropZones.find((z3) => z3.id === curr.dropZoneId)?.label || "";
|
|
574
526
|
acc[draggableText] = dropZoneText;
|
|
575
527
|
return acc;
|
|
576
528
|
}, {});
|
|
@@ -589,8 +541,7 @@ var DragAndDropEvaluator = class {
|
|
|
589
541
|
// src/services/evaluators/hotspot-evaluator.ts
|
|
590
542
|
var HotspotEvaluator = class {
|
|
591
543
|
async evaluate(question, answer) {
|
|
592
|
-
|
|
593
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
544
|
+
const points = question.points ?? 0;
|
|
594
545
|
let isCorrect = false;
|
|
595
546
|
if (Array.isArray(answer)) {
|
|
596
547
|
const userAnswerSet = new Set(answer);
|
|
@@ -598,10 +549,7 @@ var HotspotEvaluator = class {
|
|
|
598
549
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
599
550
|
}
|
|
600
551
|
const correctValues = question.correctHotspotIds.map(
|
|
601
|
-
(id) =>
|
|
602
|
-
var _a2;
|
|
603
|
-
return ((_a2 = question.hotspots.find((h) => h.id === id)) == null ? void 0 : _a2.description) || id;
|
|
604
|
-
}
|
|
552
|
+
(id) => question.hotspots.find((h) => h.id === id)?.description || id
|
|
605
553
|
);
|
|
606
554
|
const correctAnswerDetail = {
|
|
607
555
|
id: question.correctHotspotIds,
|
|
@@ -618,11 +566,10 @@ var HotspotEvaluator = class {
|
|
|
618
566
|
// src/services/evaluators/programming-evaluator.ts
|
|
619
567
|
var ProgrammingEvaluator = class {
|
|
620
568
|
async evaluate(question, answer) {
|
|
621
|
-
|
|
622
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
569
|
+
const points = question.points ?? 0;
|
|
623
570
|
let isCorrect = false;
|
|
624
571
|
if (typeof answer === "string" && typeof question.solutionGeneratedCode === "string") {
|
|
625
|
-
if (typeof window !== "undefined" &&
|
|
572
|
+
if (typeof window !== "undefined" && window.Blockly?.JavaScript) {
|
|
626
573
|
const LocalBlockly = window.Blockly;
|
|
627
574
|
let generatedUserCode = "";
|
|
628
575
|
try {
|
|
@@ -696,10 +643,10 @@ var JsonRepairEngine = class {
|
|
|
696
643
|
if (breakIndex !== -1) {
|
|
697
644
|
const stringContent = afterUnterminated.substring(0, breakIndex);
|
|
698
645
|
const remainder = afterUnterminated.substring(breakIndex);
|
|
699
|
-
const escapedContent = stringContent.replace(
|
|
646
|
+
const escapedContent = stringContent.replace(/(?<!\\)"/g, '\\"');
|
|
700
647
|
repaired = beforeUnterminated + escapedContent + '"' + remainder;
|
|
701
648
|
} else {
|
|
702
|
-
const escapedContent = afterUnterminated.replace(
|
|
649
|
+
const escapedContent = afterUnterminated.replace(/(?<!\\)"/g, '\\"');
|
|
703
650
|
repaired = beforeUnterminated + escapedContent + '"';
|
|
704
651
|
}
|
|
705
652
|
}
|
|
@@ -775,7 +722,6 @@ var JsonRepairEngine = class {
|
|
|
775
722
|
* Main repair function that attempts multiple strategies.
|
|
776
723
|
*/
|
|
777
724
|
static repairJson(jsonStr) {
|
|
778
|
-
var _a;
|
|
779
725
|
let current = jsonStr.trim();
|
|
780
726
|
const maxAttempts = 5;
|
|
781
727
|
let lastError = "";
|
|
@@ -805,7 +751,7 @@ var JsonRepairEngine = class {
|
|
|
805
751
|
}
|
|
806
752
|
lastError = validation.error || "";
|
|
807
753
|
lastPosition = validation.position;
|
|
808
|
-
if (
|
|
754
|
+
if (validation.error?.includes("Unterminated string")) {
|
|
809
755
|
current = this.repairUnterminatedStrings(current);
|
|
810
756
|
} else {
|
|
811
757
|
current = this.applyCommonFixes(current);
|
|
@@ -1100,9 +1046,10 @@ var CodeEvaluationService = class {
|
|
|
1100
1046
|
userCode,
|
|
1101
1047
|
testCase
|
|
1102
1048
|
}, this.apiKey);
|
|
1103
|
-
return
|
|
1104
|
-
testCaseId: testCase.id
|
|
1105
|
-
|
|
1049
|
+
return {
|
|
1050
|
+
testCaseId: testCase.id,
|
|
1051
|
+
...aiResult
|
|
1052
|
+
};
|
|
1106
1053
|
}
|
|
1107
1054
|
/**
|
|
1108
1055
|
* Evaluates user's code against all test cases for a given question.
|
|
@@ -1139,8 +1086,7 @@ var CodeEvaluationService = class {
|
|
|
1139
1086
|
// src/services/evaluators/coding-evaluator.ts
|
|
1140
1087
|
var CodingEvaluator = class {
|
|
1141
1088
|
async evaluate(question, answer) {
|
|
1142
|
-
|
|
1143
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
1089
|
+
const points = question.points ?? 0;
|
|
1144
1090
|
if (typeof answer !== "string" || !answer.trim()) {
|
|
1145
1091
|
return {
|
|
1146
1092
|
isCorrect: false,
|
|
@@ -1197,17 +1143,16 @@ var QuizEngine = class {
|
|
|
1197
1143
|
this.quizResultState = { scormStatus: "idle" };
|
|
1198
1144
|
this.questionStartTime = null;
|
|
1199
1145
|
this.questionTimings = /* @__PURE__ */ new Map();
|
|
1200
|
-
var _a, _b, _c, _d, _e;
|
|
1201
1146
|
this.config = options.config;
|
|
1202
1147
|
this.callbacks = options.callbacks || {};
|
|
1203
|
-
this.questions =
|
|
1148
|
+
this.questions = this.config.settings?.shuffleQuestions ? [...this.config.questions].sort(() => Math.random() - 0.5) : this.config.questions;
|
|
1204
1149
|
this.overallStartTime = Date.now();
|
|
1205
1150
|
this.evaluators = /* @__PURE__ */ new Map();
|
|
1206
1151
|
this.registerEvaluators();
|
|
1207
|
-
if (
|
|
1152
|
+
if (this.config.settings?.timeLimitMinutes && this.config.settings.timeLimitMinutes > 0) {
|
|
1208
1153
|
this.timeLeftInSeconds = this.config.settings.timeLimitMinutes * 60;
|
|
1209
1154
|
}
|
|
1210
|
-
if (
|
|
1155
|
+
if (this.config.settings?.scorm) {
|
|
1211
1156
|
this.quizResultState.scormStatus = "initializing";
|
|
1212
1157
|
this.scormService = new SCORMService(this.config.settings.scorm);
|
|
1213
1158
|
if (this.scormService.hasAPI()) {
|
|
@@ -1240,7 +1185,7 @@ var QuizEngine = class {
|
|
|
1240
1185
|
if (this.timeLeftInSeconds !== null) {
|
|
1241
1186
|
this.startTimer();
|
|
1242
1187
|
}
|
|
1243
|
-
|
|
1188
|
+
this.callbacks.onQuestionChange?.(initialQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1244
1189
|
}
|
|
1245
1190
|
registerEvaluators() {
|
|
1246
1191
|
this.evaluators.set("multiple_choice", new MultipleChoiceEvaluator());
|
|
@@ -1278,15 +1223,14 @@ var QuizEngine = class {
|
|
|
1278
1223
|
}
|
|
1279
1224
|
}
|
|
1280
1225
|
handleTick() {
|
|
1281
|
-
var _a, _b, _c, _d;
|
|
1282
1226
|
if (this.timeLeftInSeconds === null) return;
|
|
1283
1227
|
if (this.timeLeftInSeconds > 0) {
|
|
1284
1228
|
this.timeLeftInSeconds--;
|
|
1285
|
-
|
|
1229
|
+
this.callbacks.onTimeTick?.(this.timeLeftInSeconds);
|
|
1286
1230
|
}
|
|
1287
1231
|
if (this.timeLeftInSeconds <= 0) {
|
|
1288
1232
|
this.stopTimer();
|
|
1289
|
-
|
|
1233
|
+
this.callbacks.onQuizTimeUp?.();
|
|
1290
1234
|
this.calculateResults();
|
|
1291
1235
|
}
|
|
1292
1236
|
}
|
|
@@ -1309,43 +1253,39 @@ var QuizEngine = class {
|
|
|
1309
1253
|
return this.quizResultState.score !== void 0;
|
|
1310
1254
|
}
|
|
1311
1255
|
submitAnswer(questionId, answer) {
|
|
1312
|
-
var _a, _b;
|
|
1313
1256
|
this.userAnswers.set(questionId, answer);
|
|
1314
1257
|
const question = this.questions.find((q) => q.id === questionId);
|
|
1315
|
-
if (question)
|
|
1258
|
+
if (question) this.callbacks.onAnswerSubmit?.(question, answer);
|
|
1316
1259
|
}
|
|
1317
1260
|
nextQuestion() {
|
|
1318
|
-
var _a, _b;
|
|
1319
1261
|
this._recordCurrentQuestionTime();
|
|
1320
1262
|
if (this.currentQuestionIndex < this.questions.length - 1) {
|
|
1321
1263
|
this.currentQuestionIndex++;
|
|
1322
1264
|
const currentQ = this.getCurrentQuestion();
|
|
1323
1265
|
this.questionStartTime = Date.now();
|
|
1324
|
-
|
|
1266
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1325
1267
|
return currentQ;
|
|
1326
1268
|
}
|
|
1327
1269
|
return null;
|
|
1328
1270
|
}
|
|
1329
1271
|
previousQuestion() {
|
|
1330
|
-
var _a, _b;
|
|
1331
1272
|
this._recordCurrentQuestionTime();
|
|
1332
1273
|
if (this.currentQuestionIndex > 0) {
|
|
1333
1274
|
this.currentQuestionIndex--;
|
|
1334
1275
|
const currentQ = this.getCurrentQuestion();
|
|
1335
1276
|
this.questionStartTime = Date.now();
|
|
1336
|
-
|
|
1277
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1337
1278
|
return currentQ;
|
|
1338
1279
|
}
|
|
1339
1280
|
return null;
|
|
1340
1281
|
}
|
|
1341
1282
|
goToQuestion(index) {
|
|
1342
|
-
var _a, _b;
|
|
1343
1283
|
if (index >= 0 && index < this.questions.length && index !== this.currentQuestionIndex) {
|
|
1344
1284
|
this._recordCurrentQuestionTime();
|
|
1345
1285
|
this.currentQuestionIndex = index;
|
|
1346
1286
|
const currentQ = this.getCurrentQuestion();
|
|
1347
1287
|
this.questionStartTime = Date.now();
|
|
1348
|
-
|
|
1288
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1349
1289
|
return currentQ;
|
|
1350
1290
|
}
|
|
1351
1291
|
return this.getCurrentQuestion();
|
|
@@ -1371,7 +1311,6 @@ var QuizEngine = class {
|
|
|
1371
1311
|
}
|
|
1372
1312
|
// (Tiếp theo từ Phần 1)
|
|
1373
1313
|
async calculateResults() {
|
|
1374
|
-
var _a, _b, _c, _d, _e;
|
|
1375
1314
|
this.stopTimer();
|
|
1376
1315
|
this._recordCurrentQuestionTime();
|
|
1377
1316
|
let totalScore = 0;
|
|
@@ -1380,7 +1319,7 @@ var QuizEngine = class {
|
|
|
1380
1319
|
let accumulatedTotalTimeSpent = 0;
|
|
1381
1320
|
for (const question of this.questions) {
|
|
1382
1321
|
const userAnswerRaw = this.userAnswers.get(question.id) || null;
|
|
1383
|
-
maxScore +=
|
|
1322
|
+
maxScore += question.points ?? 0;
|
|
1384
1323
|
const evaluator = this.evaluators.get(question.questionType);
|
|
1385
1324
|
if (!evaluator) {
|
|
1386
1325
|
console.warn(`No evaluator found for question type: ${question.questionType}`);
|
|
@@ -1420,13 +1359,13 @@ var QuizEngine = class {
|
|
|
1420
1359
|
}
|
|
1421
1360
|
const percentage = maxScore > 0 ? parseFloat((totalScore / maxScore * 100).toFixed(2)) : 0;
|
|
1422
1361
|
let passed = void 0;
|
|
1423
|
-
if (
|
|
1362
|
+
if (this.config.settings?.passingScorePercent != null) {
|
|
1424
1363
|
passed = percentage >= this.config.settings.passingScorePercent;
|
|
1425
1364
|
}
|
|
1426
1365
|
const totalQuizTimeSpentSeconds = parseFloat(accumulatedTotalTimeSpent.toFixed(2));
|
|
1427
1366
|
const averageTimePerQuestionSeconds = this.questions.length > 0 ? parseFloat((totalQuizTimeSpentSeconds / this.questions.length).toFixed(2)) : 0;
|
|
1428
1367
|
const metadataPerformance = await this._calculateMetadataPerformance();
|
|
1429
|
-
const finalResults =
|
|
1368
|
+
const finalResults = {
|
|
1430
1369
|
score: totalScore,
|
|
1431
1370
|
maxScore,
|
|
1432
1371
|
percentage,
|
|
@@ -1438,39 +1377,33 @@ var QuizEngine = class {
|
|
|
1438
1377
|
scormError: this.quizResultState.scormError,
|
|
1439
1378
|
studentName: this.quizResultState.studentName,
|
|
1440
1379
|
totalTimeSpentSeconds: totalQuizTimeSpentSeconds,
|
|
1441
|
-
averageTimePerQuestionSeconds
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1380
|
+
averageTimePerQuestionSeconds,
|
|
1381
|
+
...metadataPerformance
|
|
1382
|
+
};
|
|
1383
|
+
this.quizResultState = { ...this.quizResultState, ...finalResults };
|
|
1384
|
+
if (this.config.settings?.scorm) this._sendResultsToSCORM(finalResults);
|
|
1445
1385
|
await this._sendResultsToWebhook(finalResults);
|
|
1446
|
-
|
|
1386
|
+
this.callbacks.onQuizFinish?.(finalResults);
|
|
1447
1387
|
return finalResults;
|
|
1448
1388
|
}
|
|
1449
1389
|
formatUserAnswerDetail(question, userAnswerRaw) {
|
|
1450
|
-
var _a, _b, _c, _d, _e;
|
|
1451
1390
|
if (userAnswerRaw === null) return null;
|
|
1452
1391
|
switch (question.questionType) {
|
|
1453
1392
|
case "multiple_choice": {
|
|
1454
1393
|
const q = question;
|
|
1455
1394
|
const id = userAnswerRaw;
|
|
1456
|
-
return { id, value:
|
|
1395
|
+
return { id, value: q.options.find((opt) => opt.id === id)?.text || "" };
|
|
1457
1396
|
}
|
|
1458
1397
|
case "multiple_response": {
|
|
1459
1398
|
const q = question;
|
|
1460
1399
|
const ids = userAnswerRaw;
|
|
1461
|
-
const values = ids.map((id) =>
|
|
1462
|
-
var _a2;
|
|
1463
|
-
return ((_a2 = q.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
1464
|
-
});
|
|
1400
|
+
const values = ids.map((id) => q.options.find((opt) => opt.id === id)?.text || "");
|
|
1465
1401
|
return { id: ids, value: values };
|
|
1466
1402
|
}
|
|
1467
1403
|
case "sequence": {
|
|
1468
1404
|
const q = question;
|
|
1469
1405
|
const ids = userAnswerRaw;
|
|
1470
|
-
const values = ids.map((id) =>
|
|
1471
|
-
var _a2;
|
|
1472
|
-
return ((_a2 = q.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
1473
|
-
});
|
|
1406
|
+
const values = ids.map((id) => q.items.find((item) => item.id === id)?.content || "");
|
|
1474
1407
|
return { id: ids, value: values };
|
|
1475
1408
|
}
|
|
1476
1409
|
case "matching": {
|
|
@@ -1479,8 +1412,8 @@ var QuizEngine = class {
|
|
|
1479
1412
|
const valueMap = {};
|
|
1480
1413
|
for (const promptId in userAnswerMap) {
|
|
1481
1414
|
const optionId = userAnswerMap[promptId];
|
|
1482
|
-
const promptText =
|
|
1483
|
-
const optionText =
|
|
1415
|
+
const promptText = q.prompts.find((p) => p.id === promptId)?.content || "";
|
|
1416
|
+
const optionText = q.options.find((o) => o.id === optionId)?.content || "";
|
|
1484
1417
|
valueMap[promptText] = optionText;
|
|
1485
1418
|
}
|
|
1486
1419
|
return { id: null, value: valueMap };
|
|
@@ -1492,8 +1425,8 @@ var QuizEngine = class {
|
|
|
1492
1425
|
const enrichedUserAnswerMap = {};
|
|
1493
1426
|
for (const draggableId in userAnswerMapByIds) {
|
|
1494
1427
|
const dropZoneId = userAnswerMapByIds[draggableId];
|
|
1495
|
-
const draggableText =
|
|
1496
|
-
const dropZoneText =
|
|
1428
|
+
const draggableText = q.draggableItems.find((d) => d.id === draggableId)?.content || `(ID: ${draggableId})`;
|
|
1429
|
+
const dropZoneText = q.dropZones.find((z3) => z3.id === dropZoneId)?.label || `(ID: ${dropZoneId})`;
|
|
1497
1430
|
enrichedUserAnswerMap[draggableText] = dropZoneText;
|
|
1498
1431
|
}
|
|
1499
1432
|
return { id: null, value: enrichedUserAnswerMap };
|
|
@@ -1505,7 +1438,6 @@ var QuizEngine = class {
|
|
|
1505
1438
|
}
|
|
1506
1439
|
}
|
|
1507
1440
|
async _calculateMetadataPerformance() {
|
|
1508
|
-
var _a;
|
|
1509
1441
|
const loPerformanceMap = /* @__PURE__ */ new Map();
|
|
1510
1442
|
const categoryPerformanceMap = /* @__PURE__ */ new Map();
|
|
1511
1443
|
const topicPerformanceMap = /* @__PURE__ */ new Map();
|
|
@@ -1527,7 +1459,7 @@ var QuizEngine = class {
|
|
|
1527
1459
|
const evaluator = this.evaluators.get(q.questionType);
|
|
1528
1460
|
if (evaluator) {
|
|
1529
1461
|
const { isCorrect } = await evaluator.evaluate(q, userAnswer);
|
|
1530
|
-
const pointsForThisQuestion =
|
|
1462
|
+
const pointsForThisQuestion = q.points ?? 0;
|
|
1531
1463
|
updateMap(loPerformanceMap, q.learningObjective, pointsForThisQuestion, isCorrect);
|
|
1532
1464
|
updateMap(categoryPerformanceMap, q.category, pointsForThisQuestion, isCorrect);
|
|
1533
1465
|
updateMap(topicPerformanceMap, q.topic, pointsForThisQuestion, isCorrect);
|
|
@@ -1554,8 +1486,7 @@ var QuizEngine = class {
|
|
|
1554
1486
|
};
|
|
1555
1487
|
}
|
|
1556
1488
|
async _sendResultsToWebhook(results) {
|
|
1557
|
-
|
|
1558
|
-
if (!((_a = this.config.settings) == null ? void 0 : _a.webhookUrl)) {
|
|
1489
|
+
if (!this.config.settings?.webhookUrl) {
|
|
1559
1490
|
results.webhookStatus = "idle";
|
|
1560
1491
|
return;
|
|
1561
1492
|
}
|
|
@@ -1583,12 +1514,11 @@ var QuizEngine = class {
|
|
|
1583
1514
|
}
|
|
1584
1515
|
}
|
|
1585
1516
|
_sendResultsToSCORM(results) {
|
|
1586
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1587
1517
|
if (!this.scormService || !this.scormService.hasAPI() || this.quizResultState.scormStatus === "no_api") {
|
|
1588
1518
|
results.scormStatus = this.quizResultState.scormStatus || "idle";
|
|
1589
1519
|
return;
|
|
1590
1520
|
}
|
|
1591
|
-
if (this.quizResultState.scormStatus === "error" &&
|
|
1521
|
+
if (this.quizResultState.scormStatus === "error" && this.quizResultState.scormError?.includes("initialization failed")) {
|
|
1592
1522
|
results.scormStatus = "error";
|
|
1593
1523
|
results.scormError = this.quizResultState.scormError;
|
|
1594
1524
|
return;
|
|
@@ -1597,15 +1527,15 @@ var QuizEngine = class {
|
|
|
1597
1527
|
try {
|
|
1598
1528
|
this.scormService.setScore(results.score, results.maxScore, 0);
|
|
1599
1529
|
let lessonStatusSetting = "completed";
|
|
1600
|
-
if (
|
|
1530
|
+
if (this.config.settings?.passingScorePercent !== void 0 && this.config.settings?.passingScorePercent !== null) {
|
|
1601
1531
|
lessonStatusSetting = results.passed ? "passed" : "failed";
|
|
1602
|
-
} else if (
|
|
1532
|
+
} else if (this.config.settings?.scorm?.setCompletionOnFinish) {
|
|
1603
1533
|
lessonStatusSetting = "completed";
|
|
1604
1534
|
}
|
|
1605
1535
|
this.scormService.setLessonStatus(lessonStatusSetting, results.passed);
|
|
1606
1536
|
if (results.totalTimeSpentSeconds !== void 0 && this.scormService.formatCMITime) {
|
|
1607
1537
|
const cmiTime = this.scormService.formatCMITime(results.totalTimeSpentSeconds);
|
|
1608
|
-
const sessionTimeVar =
|
|
1538
|
+
const sessionTimeVar = this.config.settings?.scorm?.sessionTimeVar || (this.scormService.getSCORMVersion() === "2004" ? "cmi.session_time" : "cmi.core.session_time");
|
|
1609
1539
|
if (sessionTimeVar) this.scormService.setValue(sessionTimeVar, cmiTime);
|
|
1610
1540
|
}
|
|
1611
1541
|
const commitResult = this.scormService.commit();
|
|
@@ -1624,114 +1554,96 @@ var QuizEngine = class {
|
|
|
1624
1554
|
function cn(...inputs) {
|
|
1625
1555
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
1626
1556
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
var RadioGroup = React28__namespace.forwardRef((_a, ref) => {
|
|
1630
|
-
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1631
|
-
return /* @__PURE__ */ React28__namespace.createElement(
|
|
1557
|
+
var RadioGroup = React9__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
1558
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1632
1559
|
RadioGroupPrimitive__namespace.Root,
|
|
1633
|
-
|
|
1634
|
-
className: cn("grid gap-2", className)
|
|
1635
|
-
|
|
1560
|
+
{
|
|
1561
|
+
className: cn("grid gap-2", className),
|
|
1562
|
+
...props,
|
|
1636
1563
|
ref
|
|
1637
|
-
}
|
|
1564
|
+
}
|
|
1638
1565
|
);
|
|
1639
1566
|
});
|
|
1640
1567
|
RadioGroup.displayName = RadioGroupPrimitive__namespace.Root.displayName;
|
|
1641
|
-
var RadioGroupItem =
|
|
1642
|
-
|
|
1643
|
-
return /* @__PURE__ */ React28__namespace.createElement(
|
|
1568
|
+
var RadioGroupItem = React9__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
1569
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1644
1570
|
RadioGroupPrimitive__namespace.Item,
|
|
1645
|
-
|
|
1571
|
+
{
|
|
1646
1572
|
ref,
|
|
1647
1573
|
className: cn(
|
|
1648
1574
|
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1649
1575
|
className
|
|
1650
|
-
)
|
|
1651
|
-
|
|
1652
|
-
|
|
1576
|
+
),
|
|
1577
|
+
...props,
|
|
1578
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(RadioGroupPrimitive__namespace.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
|
|
1579
|
+
}
|
|
1653
1580
|
);
|
|
1654
1581
|
});
|
|
1655
1582
|
RadioGroupItem.displayName = RadioGroupPrimitive__namespace.Item.displayName;
|
|
1656
1583
|
var labelVariants = classVarianceAuthority.cva(
|
|
1657
1584
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1658
1585
|
);
|
|
1659
|
-
var Label =
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
);
|
|
1668
|
-
});
|
|
1586
|
+
var Label = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1587
|
+
LabelPrimitive__namespace.Root,
|
|
1588
|
+
{
|
|
1589
|
+
ref,
|
|
1590
|
+
className: cn(labelVariants(), className),
|
|
1591
|
+
...props
|
|
1592
|
+
}
|
|
1593
|
+
));
|
|
1669
1594
|
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
1670
|
-
var Card =
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
className
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
);
|
|
1682
|
-
});
|
|
1595
|
+
var Card = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1596
|
+
"div",
|
|
1597
|
+
{
|
|
1598
|
+
ref,
|
|
1599
|
+
className: cn(
|
|
1600
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
1601
|
+
className
|
|
1602
|
+
),
|
|
1603
|
+
...props
|
|
1604
|
+
}
|
|
1605
|
+
));
|
|
1683
1606
|
Card.displayName = "Card";
|
|
1684
|
-
var CardHeader =
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
);
|
|
1693
|
-
});
|
|
1607
|
+
var CardHeader = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1608
|
+
"div",
|
|
1609
|
+
{
|
|
1610
|
+
ref,
|
|
1611
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1612
|
+
...props
|
|
1613
|
+
}
|
|
1614
|
+
));
|
|
1694
1615
|
CardHeader.displayName = "CardHeader";
|
|
1695
|
-
var CardTitle =
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
className
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
);
|
|
1707
|
-
});
|
|
1616
|
+
var CardTitle = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1617
|
+
"div",
|
|
1618
|
+
{
|
|
1619
|
+
ref,
|
|
1620
|
+
className: cn(
|
|
1621
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
1622
|
+
className
|
|
1623
|
+
),
|
|
1624
|
+
...props
|
|
1625
|
+
}
|
|
1626
|
+
));
|
|
1708
1627
|
CardTitle.displayName = "CardTitle";
|
|
1709
|
-
var CardDescription =
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
);
|
|
1718
|
-
});
|
|
1628
|
+
var CardDescription = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1629
|
+
"div",
|
|
1630
|
+
{
|
|
1631
|
+
ref,
|
|
1632
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1633
|
+
...props
|
|
1634
|
+
}
|
|
1635
|
+
));
|
|
1719
1636
|
CardDescription.displayName = "CardDescription";
|
|
1720
|
-
var CardContent =
|
|
1721
|
-
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1722
|
-
return /* @__PURE__ */ React28__namespace.createElement("div", __spreadValues({ ref, className: cn("p-6 pt-0", className) }, props));
|
|
1723
|
-
});
|
|
1637
|
+
var CardContent = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1724
1638
|
CardContent.displayName = "CardContent";
|
|
1725
|
-
var CardFooter =
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
);
|
|
1734
|
-
});
|
|
1639
|
+
var CardFooter = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1640
|
+
"div",
|
|
1641
|
+
{
|
|
1642
|
+
ref,
|
|
1643
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
1644
|
+
...props
|
|
1645
|
+
}
|
|
1646
|
+
));
|
|
1735
1647
|
CardFooter.displayName = "CardFooter";
|
|
1736
1648
|
var MarkdownRenderer = ({
|
|
1737
1649
|
content,
|
|
@@ -1775,20 +1687,19 @@ var MarkdownRenderer = ({
|
|
|
1775
1687
|
const processedContent = processContentForVideos(content);
|
|
1776
1688
|
return (
|
|
1777
1689
|
// Using Tailwind Typography for beautiful default styling of markdown content
|
|
1778
|
-
/* @__PURE__ */
|
|
1690
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `prose dark:prose-invert max-w-none ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1779
1691
|
ReactMarkdown__default.default,
|
|
1780
1692
|
{
|
|
1781
1693
|
remarkPlugins: [remarkGfm__default.default, remarkMath__default.default],
|
|
1782
1694
|
rehypePlugins: [rehypeHighlight__default.default, rehypeKatex__default.default],
|
|
1783
1695
|
components: {
|
|
1784
1696
|
// Override the default image component to handle videos and responsive images
|
|
1785
|
-
img: (
|
|
1786
|
-
var _b = _a, { node } = _b, props = __objRest(_b, ["node"]);
|
|
1697
|
+
img: ({ node, ...props }) => {
|
|
1787
1698
|
const src = props.src || "";
|
|
1788
1699
|
const { platform, id } = getVideoId(src);
|
|
1789
1700
|
if (platform && id) {
|
|
1790
1701
|
const videoSrc = platform === "youtube" ? `https://www.youtube.com/embed/${id}` : `https://player.vimeo.com/video/${id}`;
|
|
1791
|
-
return /* @__PURE__ */
|
|
1702
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-w-16 aspect-h-9 my-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1792
1703
|
"iframe",
|
|
1793
1704
|
{
|
|
1794
1705
|
src: videoSrc,
|
|
@@ -1797,13 +1708,14 @@ var MarkdownRenderer = ({
|
|
|
1797
1708
|
allowFullScreen: true,
|
|
1798
1709
|
className: "w-full h-full rounded-md"
|
|
1799
1710
|
}
|
|
1800
|
-
));
|
|
1711
|
+
) });
|
|
1801
1712
|
}
|
|
1802
1713
|
return (
|
|
1803
1714
|
// eslint-disable-next-line @next/next/no-img-element
|
|
1804
|
-
/* @__PURE__ */
|
|
1715
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1805
1716
|
"img",
|
|
1806
|
-
|
|
1717
|
+
{
|
|
1718
|
+
...props,
|
|
1807
1719
|
style: {
|
|
1808
1720
|
maxWidth: "100%",
|
|
1809
1721
|
height: "auto",
|
|
@@ -1811,33 +1723,26 @@ var MarkdownRenderer = ({
|
|
|
1811
1723
|
margin: "1rem 0"
|
|
1812
1724
|
},
|
|
1813
1725
|
alt: props.alt || ""
|
|
1814
|
-
}
|
|
1726
|
+
}
|
|
1815
1727
|
)
|
|
1816
1728
|
);
|
|
1817
1729
|
},
|
|
1818
1730
|
// Override the default table to add responsive wrapper
|
|
1819
|
-
table: (
|
|
1820
|
-
var _d = _c, { node } = _d, props = __objRest(_d, ["node"]);
|
|
1821
|
-
return /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React28__namespace.default.createElement("table", __spreadProps(__spreadValues({}, props), { className: "my-4 w-full text-sm" })));
|
|
1822
|
-
},
|
|
1731
|
+
table: ({ node, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("table", { ...props, className: "my-4 w-full text-sm" }) }),
|
|
1823
1732
|
// Override default blockquote for better styling
|
|
1824
|
-
blockquote: (
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
processedContent
|
|
1836
|
-
))
|
|
1733
|
+
blockquote: ({ node, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1734
|
+
"blockquote",
|
|
1735
|
+
{
|
|
1736
|
+
...props,
|
|
1737
|
+
className: "border-l-4 border-primary bg-muted/50 p-4 my-4 italic"
|
|
1738
|
+
}
|
|
1739
|
+
)
|
|
1740
|
+
},
|
|
1741
|
+
children: processedContent
|
|
1742
|
+
}
|
|
1743
|
+
) })
|
|
1837
1744
|
);
|
|
1838
1745
|
};
|
|
1839
|
-
|
|
1840
|
-
// src/react-ui/components/ui/MultipleChoiceQuestionUI.tsx
|
|
1841
1746
|
var MultipleChoiceQuestionUI = ({
|
|
1842
1747
|
question,
|
|
1843
1748
|
onAnswerChange,
|
|
@@ -1848,40 +1753,58 @@ var MultipleChoiceQuestionUI = ({
|
|
|
1848
1753
|
const handleSelection = (value) => {
|
|
1849
1754
|
onAnswerChange(value);
|
|
1850
1755
|
};
|
|
1851
|
-
return /* @__PURE__ */
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
},
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1863
|
-
if (showCorrectAnswer) {
|
|
1864
|
-
if (isCorrect) {
|
|
1865
|
-
itemClassName += " border-green-500 bg-green-500/10";
|
|
1866
|
-
} else if (isSelected && !isCorrect) {
|
|
1867
|
-
itemClassName += " border-destructive bg-destructive/10";
|
|
1868
|
-
} else {
|
|
1869
|
-
itemClassName += " border-muted";
|
|
1870
|
-
}
|
|
1871
|
-
} else {
|
|
1872
|
-
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1873
|
-
}
|
|
1874
|
-
return /* @__PURE__ */ React28__namespace.default.createElement(
|
|
1875
|
-
Label,
|
|
1756
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1757
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1758
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1759
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1760
|
+
"Points: ",
|
|
1761
|
+
points
|
|
1762
|
+
] })
|
|
1763
|
+
] }),
|
|
1764
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
1765
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1766
|
+
RadioGroup,
|
|
1876
1767
|
{
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
className:
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1768
|
+
value: userAnswer || void 0,
|
|
1769
|
+
onValueChange: handleSelection,
|
|
1770
|
+
className: "space-y-3",
|
|
1771
|
+
"aria-labelledby": `question-prompt-${questionId}`,
|
|
1772
|
+
children: options.map((option) => {
|
|
1773
|
+
const isSelected = userAnswer === option.id;
|
|
1774
|
+
const isCorrect = option.id === correctAnswerId;
|
|
1775
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1776
|
+
if (showCorrectAnswer) {
|
|
1777
|
+
if (isCorrect) {
|
|
1778
|
+
itemClassName += " border-green-500 bg-green-500/10";
|
|
1779
|
+
} else if (isSelected && !isCorrect) {
|
|
1780
|
+
itemClassName += " border-destructive bg-destructive/10";
|
|
1781
|
+
} else {
|
|
1782
|
+
itemClassName += " border-muted";
|
|
1783
|
+
}
|
|
1784
|
+
} else {
|
|
1785
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1786
|
+
}
|
|
1787
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1788
|
+
Label,
|
|
1789
|
+
{
|
|
1790
|
+
htmlFor: option.id,
|
|
1791
|
+
className: itemClassName,
|
|
1792
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
1793
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: option.id, id: option.id, className: "mr-3" }),
|
|
1794
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: option.text }) })
|
|
1795
|
+
] })
|
|
1796
|
+
},
|
|
1797
|
+
option.id
|
|
1798
|
+
);
|
|
1799
|
+
})
|
|
1800
|
+
}
|
|
1801
|
+
),
|
|
1802
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1803
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1804
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1805
|
+
] })
|
|
1806
|
+
] })
|
|
1807
|
+
] });
|
|
1885
1808
|
};
|
|
1886
1809
|
var TrueFalseQuestionUI = ({
|
|
1887
1810
|
question,
|
|
@@ -1897,64 +1820,78 @@ var TrueFalseQuestionUI = ({
|
|
|
1897
1820
|
const handleSelection = (value) => {
|
|
1898
1821
|
onAnswerChange(value);
|
|
1899
1822
|
};
|
|
1900
|
-
return /* @__PURE__ */
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
},
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1912
|
-
if (showCorrectAnswer) {
|
|
1913
|
-
if (isOptionCorrect) {
|
|
1914
|
-
itemClassName += " border-green-500 bg-green-500/10";
|
|
1915
|
-
} else if (isSelected && !isOptionCorrect) {
|
|
1916
|
-
itemClassName += " border-destructive bg-destructive/10";
|
|
1917
|
-
} else {
|
|
1918
|
-
itemClassName += " border-muted";
|
|
1919
|
-
}
|
|
1920
|
-
} else {
|
|
1921
|
-
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1922
|
-
}
|
|
1923
|
-
return /* @__PURE__ */ React28__namespace.default.createElement(
|
|
1924
|
-
Label,
|
|
1823
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1824
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1825
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1826
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1827
|
+
"Points: ",
|
|
1828
|
+
points
|
|
1829
|
+
] })
|
|
1830
|
+
] }),
|
|
1831
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
1832
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1833
|
+
RadioGroup,
|
|
1925
1834
|
{
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
className:
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1835
|
+
value: userAnswer || void 0,
|
|
1836
|
+
onValueChange: handleSelection,
|
|
1837
|
+
className: "space-y-3",
|
|
1838
|
+
"aria-labelledby": `question-prompt-${questionId}`,
|
|
1839
|
+
children: options.map((option) => {
|
|
1840
|
+
const isSelected = userAnswer === option.value;
|
|
1841
|
+
const isOptionCorrect = option.value === "true" && correctAnswer === true || option.value === "false" && correctAnswer === false;
|
|
1842
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1843
|
+
if (showCorrectAnswer) {
|
|
1844
|
+
if (isOptionCorrect) {
|
|
1845
|
+
itemClassName += " border-green-500 bg-green-500/10";
|
|
1846
|
+
} else if (isSelected && !isOptionCorrect) {
|
|
1847
|
+
itemClassName += " border-destructive bg-destructive/10";
|
|
1848
|
+
} else {
|
|
1849
|
+
itemClassName += " border-muted";
|
|
1850
|
+
}
|
|
1851
|
+
} else {
|
|
1852
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1853
|
+
}
|
|
1854
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1855
|
+
Label,
|
|
1856
|
+
{
|
|
1857
|
+
htmlFor: option.id,
|
|
1858
|
+
className: itemClassName,
|
|
1859
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
1860
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: option.value, id: option.id, className: "mr-3" }),
|
|
1861
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base", children: option.label })
|
|
1862
|
+
] })
|
|
1863
|
+
},
|
|
1864
|
+
option.id
|
|
1865
|
+
);
|
|
1866
|
+
})
|
|
1867
|
+
}
|
|
1868
|
+
),
|
|
1869
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1870
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1871
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1872
|
+
] })
|
|
1873
|
+
] })
|
|
1874
|
+
] });
|
|
1934
1875
|
};
|
|
1935
|
-
var Checkbox =
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
className
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
}, props),
|
|
1946
|
-
/* @__PURE__ */ React28__namespace.createElement(
|
|
1876
|
+
var Checkbox = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1877
|
+
CheckboxPrimitive__namespace.Root,
|
|
1878
|
+
{
|
|
1879
|
+
ref,
|
|
1880
|
+
className: cn(
|
|
1881
|
+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
1882
|
+
className
|
|
1883
|
+
),
|
|
1884
|
+
...props,
|
|
1885
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1947
1886
|
CheckboxPrimitive__namespace.Indicator,
|
|
1948
1887
|
{
|
|
1949
|
-
className: cn("flex items-center justify-center text-current")
|
|
1950
|
-
|
|
1951
|
-
|
|
1888
|
+
className: cn("flex items-center justify-center text-current"),
|
|
1889
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" })
|
|
1890
|
+
}
|
|
1952
1891
|
)
|
|
1953
|
-
|
|
1954
|
-
|
|
1892
|
+
}
|
|
1893
|
+
));
|
|
1955
1894
|
Checkbox.displayName = CheckboxPrimitive__namespace.Root.displayName;
|
|
1956
|
-
|
|
1957
|
-
// src/react-ui/components/ui/MultipleResponseQuestionUI.tsx
|
|
1958
1895
|
var MultipleResponseQuestionUI = ({
|
|
1959
1896
|
question,
|
|
1960
1897
|
onAnswerChange,
|
|
@@ -1976,59 +1913,74 @@ var MultipleResponseQuestionUI = ({
|
|
|
1976
1913
|
}
|
|
1977
1914
|
onAnswerChange(newAnswers.length > 0 ? newAnswers : null);
|
|
1978
1915
|
};
|
|
1979
|
-
return /* @__PURE__ */
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
Checkbox,
|
|
2001
|
-
{
|
|
2002
|
-
id: option.id,
|
|
2003
|
-
checked: isSelected,
|
|
2004
|
-
onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
|
|
2005
|
-
className: "mr-3",
|
|
2006
|
-
"aria-label": option.text.replace(/<[^>]*>?/gm, "")
|
|
1916
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1917
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1918
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1919
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1920
|
+
"Points: ",
|
|
1921
|
+
points
|
|
1922
|
+
] })
|
|
1923
|
+
] }),
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
1925
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", role: "group", "aria-labelledby": `question-prompt-${questionId}`, children: options.map((option) => {
|
|
1926
|
+
const isSelected = Array.isArray(userAnswer) && userAnswer.includes(option.id);
|
|
1927
|
+
const isCorrectOption = correctAnswerIds.includes(option.id);
|
|
1928
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary flex items-center";
|
|
1929
|
+
if (showCorrectAnswer) {
|
|
1930
|
+
if (isCorrectOption) {
|
|
1931
|
+
itemClassName += isSelected ? " border-green-500 bg-green-500/10" : " border-green-500";
|
|
1932
|
+
} else {
|
|
1933
|
+
itemClassName += isSelected ? " border-destructive bg-destructive/10" : " border-muted";
|
|
1934
|
+
}
|
|
1935
|
+
} else {
|
|
1936
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
2007
1937
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
1938
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1939
|
+
Label,
|
|
1940
|
+
{
|
|
1941
|
+
htmlFor: option.id,
|
|
1942
|
+
className: itemClassName,
|
|
1943
|
+
children: [
|
|
1944
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1945
|
+
Checkbox,
|
|
1946
|
+
{
|
|
1947
|
+
id: option.id,
|
|
1948
|
+
checked: isSelected,
|
|
1949
|
+
onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
|
|
1950
|
+
className: "mr-3",
|
|
1951
|
+
"aria-label": option.text.replace(/<[^>]*>?/gm, "")
|
|
1952
|
+
}
|
|
1953
|
+
),
|
|
1954
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: option.text }) })
|
|
1955
|
+
]
|
|
1956
|
+
},
|
|
1957
|
+
option.id
|
|
1958
|
+
);
|
|
1959
|
+
}) }),
|
|
1960
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1961
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1962
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1963
|
+
] })
|
|
1964
|
+
] })
|
|
1965
|
+
] });
|
|
2012
1966
|
};
|
|
2013
|
-
var Input =
|
|
2014
|
-
(
|
|
2015
|
-
|
|
2016
|
-
return /* @__PURE__ */ React28__namespace.createElement(
|
|
1967
|
+
var Input = React9__namespace.forwardRef(
|
|
1968
|
+
({ className, type, ...props }, ref) => {
|
|
1969
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2017
1970
|
"input",
|
|
2018
|
-
|
|
1971
|
+
{
|
|
2019
1972
|
type,
|
|
2020
1973
|
className: cn(
|
|
2021
1974
|
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
2022
1975
|
className
|
|
2023
1976
|
),
|
|
2024
|
-
ref
|
|
2025
|
-
|
|
1977
|
+
ref,
|
|
1978
|
+
...props
|
|
1979
|
+
}
|
|
2026
1980
|
);
|
|
2027
1981
|
}
|
|
2028
1982
|
);
|
|
2029
1983
|
Input.displayName = "Input";
|
|
2030
|
-
|
|
2031
|
-
// src/react-ui/components/ui/ShortAnswerQuestionUI.tsx
|
|
2032
1984
|
var ShortAnswerQuestionUI = ({
|
|
2033
1985
|
question,
|
|
2034
1986
|
onAnswerChange,
|
|
@@ -2047,20 +1999,44 @@ var ShortAnswerQuestionUI = ({
|
|
|
2047
1999
|
(accAns) => isCaseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
|
|
2048
2000
|
);
|
|
2049
2001
|
}
|
|
2050
|
-
return /* @__PURE__ */
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
className:
|
|
2002
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2003
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2004
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2005
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2006
|
+
"Points: ",
|
|
2007
|
+
points
|
|
2008
|
+
] })
|
|
2009
|
+
] }),
|
|
2010
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
2011
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `short-answer-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
|
|
2012
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2013
|
+
Input,
|
|
2014
|
+
{
|
|
2015
|
+
id: `short-answer-input-${questionId}`,
|
|
2016
|
+
type: "text",
|
|
2017
|
+
value: displayUserAnswer,
|
|
2018
|
+
onChange: handleInputChange,
|
|
2019
|
+
placeholder: "Type your answer here...",
|
|
2020
|
+
"aria-describedby": explanation ? `explanation-${questionId}` : void 0,
|
|
2021
|
+
className: `
|
|
2060
2022
|
${showCorrectAnswer && userAnswer ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
|
|
2061
2023
|
`
|
|
2062
|
-
|
|
2063
|
-
|
|
2024
|
+
}
|
|
2025
|
+
),
|
|
2026
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2027
|
+
userAnswer && !isActuallyCorrect && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
|
|
2028
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2029
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Accepted Answers:" }),
|
|
2030
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside text-sm text-accent-foreground/80", children: acceptedAnswers.map((ans, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: ans }, idx)) }),
|
|
2031
|
+
isCaseSensitive && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "(Case-sensitive)" })
|
|
2032
|
+
] }),
|
|
2033
|
+
explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
2034
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
|
|
2035
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
2036
|
+
] })
|
|
2037
|
+
] })
|
|
2038
|
+
] })
|
|
2039
|
+
] });
|
|
2064
2040
|
};
|
|
2065
2041
|
var NumericQuestionUI = ({
|
|
2066
2042
|
question,
|
|
@@ -2083,22 +2059,48 @@ var NumericQuestionUI = ({
|
|
|
2083
2059
|
isActuallyCorrect = tolerance !== void 0 && tolerance !== null ? Math.abs(userAnswerNum - correctAnswerValue) <= tolerance : userAnswerNum === correctAnswerValue;
|
|
2084
2060
|
}
|
|
2085
2061
|
}
|
|
2086
|
-
return /* @__PURE__ */
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2062
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2063
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2064
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2065
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2066
|
+
"Points: ",
|
|
2067
|
+
points
|
|
2068
|
+
] })
|
|
2069
|
+
] }),
|
|
2070
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
2071
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `numeric-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
|
|
2072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2073
|
+
Input,
|
|
2074
|
+
{
|
|
2075
|
+
id: `numeric-input-${questionId}`,
|
|
2076
|
+
type: "text",
|
|
2077
|
+
inputMode: "numeric",
|
|
2078
|
+
pattern: "[0-9]*\\.?[0-9]*",
|
|
2079
|
+
value: displayUserAnswer,
|
|
2080
|
+
onChange: handleInputChange,
|
|
2081
|
+
placeholder: "Enter a number",
|
|
2082
|
+
"aria-describedby": explanation ? `explanation-${questionId}` : void 0,
|
|
2083
|
+
className: `
|
|
2098
2084
|
${showCorrectAnswer && userAnswer !== null && userAnswer !== "" ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
|
|
2099
2085
|
`
|
|
2100
|
-
|
|
2101
|
-
|
|
2086
|
+
}
|
|
2087
|
+
),
|
|
2088
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2089
|
+
userAnswer !== null && userAnswer !== "" && !isActuallyCorrect && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
|
|
2090
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2091
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Correct Answer:" }),
|
|
2092
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-accent-foreground/80", children: [
|
|
2093
|
+
correctAnswerValue,
|
|
2094
|
+
tolerance !== void 0 && tolerance !== null && tolerance > 0 && ` (Tolerance: \xB1${tolerance}, Accepted range: ${correctAnswerValue - tolerance} to ${correctAnswerValue + tolerance})`
|
|
2095
|
+
] })
|
|
2096
|
+
] }),
|
|
2097
|
+
explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
2100
|
+
] })
|
|
2101
|
+
] })
|
|
2102
|
+
] })
|
|
2103
|
+
] });
|
|
2102
2104
|
};
|
|
2103
2105
|
var FillInTheBlanksQuestionUI = ({
|
|
2104
2106
|
question,
|
|
@@ -2107,8 +2109,8 @@ var FillInTheBlanksQuestionUI = ({
|
|
|
2107
2109
|
showCorrectAnswer = false
|
|
2108
2110
|
}) => {
|
|
2109
2111
|
const { prompt, segments, answers: correctAnswersMap, points, explanation, id: questionId, isCaseSensitive } = question;
|
|
2110
|
-
const [userInputs, setUserInputs] =
|
|
2111
|
-
|
|
2112
|
+
const [userInputs, setUserInputs] = React9.useState({});
|
|
2113
|
+
React9.useEffect(() => {
|
|
2112
2114
|
if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
|
|
2113
2115
|
setUserInputs(userAnswer);
|
|
2114
2116
|
} else {
|
|
@@ -2122,15 +2124,14 @@ var FillInTheBlanksQuestionUI = ({
|
|
|
2122
2124
|
}
|
|
2123
2125
|
}, [segments, userAnswer]);
|
|
2124
2126
|
const handleInputChange = (blankId, value) => {
|
|
2125
|
-
const newInputs =
|
|
2127
|
+
const newInputs = { ...userInputs, [blankId]: value };
|
|
2126
2128
|
setUserInputs(newInputs);
|
|
2127
2129
|
const hasValue = Object.values(newInputs).some((val) => val.trim() !== "");
|
|
2128
2130
|
onAnswerChange(hasValue ? newInputs : null);
|
|
2129
2131
|
};
|
|
2130
2132
|
const getCorrectnessForBlank = (blankId) => {
|
|
2131
|
-
var _a;
|
|
2132
2133
|
if (!showCorrectAnswer || !userInputs[blankId]) return null;
|
|
2133
|
-
const userAnswerForBlank =
|
|
2134
|
+
const userAnswerForBlank = userInputs[blankId]?.trim();
|
|
2134
2135
|
const correctAnswerDef = correctAnswersMap.find((a) => a.blankId === blankId);
|
|
2135
2136
|
if (!correctAnswerDef || !userAnswerForBlank) return false;
|
|
2136
2137
|
const caseSensitive = isCaseSensitive === void 0 ? false : isCaseSensitive;
|
|
@@ -2138,49 +2139,78 @@ var FillInTheBlanksQuestionUI = ({
|
|
|
2138
2139
|
(accVal) => caseSensitive ? accVal.trim() === userAnswerForBlank : accVal.trim().toLowerCase() === userAnswerForBlank.toLowerCase()
|
|
2139
2140
|
);
|
|
2140
2141
|
};
|
|
2141
|
-
return /* @__PURE__ */
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2142
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2143
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2144
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2145
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2146
|
+
"Points: ",
|
|
2147
|
+
points
|
|
2148
|
+
] })
|
|
2149
|
+
] }),
|
|
2150
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
2151
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base leading-relaxed flex flex-wrap items-center gap-x-1", "aria-labelledby": `question-prompt-${questionId}`, children: segments.map((segment, index) => {
|
|
2152
|
+
if (segment.type === "text") {
|
|
2153
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2154
|
+
MarkdownRenderer,
|
|
2155
|
+
{
|
|
2156
|
+
content: segment.content || "",
|
|
2157
|
+
className: "inline"
|
|
2158
|
+
},
|
|
2159
|
+
`text-${index}`
|
|
2160
|
+
);
|
|
2150
2161
|
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2162
|
+
if (segment.type === "blank" && segment.id) {
|
|
2163
|
+
const blankId = segment.id;
|
|
2164
|
+
const isCorrect = getCorrectnessForBlank(blankId);
|
|
2165
|
+
let inputClassName = "inline-block w-auto min-w-[100px] max-w-[200px] h-8 mx-1 align-baseline text-base";
|
|
2166
|
+
if (showCorrectAnswer && userInputs[blankId]?.trim()) {
|
|
2167
|
+
inputClassName += isCorrect ? " border-green-500 focus-visible:ring-green-500" : " border-destructive focus-visible:ring-destructive";
|
|
2168
|
+
} else {
|
|
2169
|
+
inputClassName += " border-input";
|
|
2170
|
+
}
|
|
2171
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2172
|
+
Input,
|
|
2173
|
+
{
|
|
2174
|
+
id: blankId,
|
|
2175
|
+
type: "text",
|
|
2176
|
+
value: userInputs[blankId] || "",
|
|
2177
|
+
onChange: (e) => handleInputChange(blankId, e.target.value),
|
|
2178
|
+
placeholder: "\u0110i\u1EC1n...",
|
|
2179
|
+
className: inputClassName,
|
|
2180
|
+
"aria-label": `Blank ${index + 1}`,
|
|
2181
|
+
disabled: showCorrectAnswer
|
|
2182
|
+
},
|
|
2183
|
+
blankId
|
|
2184
|
+
);
|
|
2174
2185
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2186
|
+
return null;
|
|
2187
|
+
}) }),
|
|
2188
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2189
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch chung:" }),
|
|
2190
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2191
|
+
] }),
|
|
2192
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 space-y-3", children: correctAnswersMap.map((ansDef) => {
|
|
2193
|
+
const isBlankCorrect = getCorrectnessForBlank(ansDef.blankId);
|
|
2194
|
+
const userAnswerDisplay = userInputs[ansDef.blankId] || "Ch\u01B0a tr\u1EA3 l\u1EDDi";
|
|
2195
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `p-2 border rounded-md ${isBlankCorrect ? "border-green-500/50 bg-green-500/10" : "border-destructive/50 bg-destructive/10"}`, children: [
|
|
2196
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
|
|
2197
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
|
|
2198
|
+
"\xD4 tr\u1ED1ng '",
|
|
2199
|
+
segments.find((s) => s.id === ansDef.blankId && s.type === "blank")?.id || ansDef.blankId,
|
|
2200
|
+
"':"
|
|
2201
|
+
] }),
|
|
2202
|
+
' B\u1EA1n \u0111\xE3 \u0111i\u1EC1n: "',
|
|
2203
|
+
userAnswerDisplay,
|
|
2204
|
+
'".'
|
|
2205
|
+
] }),
|
|
2206
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs", children: [
|
|
2207
|
+
"\u0110\xE1p \xE1n ch\u1EA5p nh\u1EADn: ",
|
|
2208
|
+
ansDef.acceptedValues.join(", ")
|
|
2209
|
+
] })
|
|
2210
|
+
] }, `feedback-${ansDef.blankId}`);
|
|
2211
|
+
}) })
|
|
2212
|
+
] })
|
|
2213
|
+
] });
|
|
2184
2214
|
};
|
|
2185
2215
|
var buttonVariants = classVarianceAuthority.cva(
|
|
2186
2216
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
@@ -2207,16 +2237,16 @@ var buttonVariants = classVarianceAuthority.cva(
|
|
|
2207
2237
|
}
|
|
2208
2238
|
}
|
|
2209
2239
|
);
|
|
2210
|
-
var Button =
|
|
2211
|
-
(
|
|
2212
|
-
var _b = _a, { className, variant, size, asChild = false } = _b, props = __objRest(_b, ["className", "variant", "size", "asChild"]);
|
|
2240
|
+
var Button = React9__namespace.forwardRef(
|
|
2241
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
2213
2242
|
const Comp = asChild ? reactSlot.Slot : "button";
|
|
2214
|
-
return /* @__PURE__ */
|
|
2243
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2215
2244
|
Comp,
|
|
2216
|
-
|
|
2245
|
+
{
|
|
2217
2246
|
className: cn(buttonVariants({ variant, size, className })),
|
|
2218
|
-
ref
|
|
2219
|
-
|
|
2247
|
+
ref,
|
|
2248
|
+
...props
|
|
2249
|
+
}
|
|
2220
2250
|
);
|
|
2221
2251
|
}
|
|
2222
2252
|
);
|
|
@@ -2228,9 +2258,9 @@ var SequenceQuestionUI = ({
|
|
|
2228
2258
|
showCorrectAnswer = false
|
|
2229
2259
|
}) => {
|
|
2230
2260
|
const { prompt, items, points, explanation, id: questionId, correctOrder } = question;
|
|
2231
|
-
const [selectedSequence, setSelectedSequence] =
|
|
2232
|
-
const [availableItems, setAvailableItems] =
|
|
2233
|
-
|
|
2261
|
+
const [selectedSequence, setSelectedSequence] = React9.useState([]);
|
|
2262
|
+
const [availableItems, setAvailableItems] = React9.useState([]);
|
|
2263
|
+
React9.useEffect(() => {
|
|
2234
2264
|
const initialUserOrder = Array.isArray(userAnswer) ? userAnswer : [];
|
|
2235
2265
|
const initialSelected = [];
|
|
2236
2266
|
const initialAvailable = [...items];
|
|
@@ -2271,145 +2301,180 @@ var SequenceQuestionUI = ({
|
|
|
2271
2301
|
if (!showCorrectAnswer || !Array.isArray(userAnswer) || userAnswer.length <= index) return null;
|
|
2272
2302
|
const userItemId = userAnswer[index];
|
|
2273
2303
|
const correctItemId = correctOrder[index];
|
|
2274
|
-
return userItemId === correctItemId ? /* @__PURE__ */
|
|
2304
|
+
return userItemId === correctItemId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 ml-2 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-destructive ml-2 flex-shrink-0" });
|
|
2275
2305
|
};
|
|
2276
|
-
return /* @__PURE__ */
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2306
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2307
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2308
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2309
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2310
|
+
"Points: ",
|
|
2311
|
+
points
|
|
2312
|
+
] })
|
|
2313
|
+
] }),
|
|
2314
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-6", children: [
|
|
2315
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2316
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "S\u1EAFp x\u1EBFp c\xE1c m\u1EE5c sau theo \u0111\xFAng th\u1EE9 t\u1EF1:" }),
|
|
2317
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2", children: availableItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2318
|
+
Button,
|
|
2319
|
+
{
|
|
2320
|
+
variant: "outline",
|
|
2321
|
+
onClick: () => handleSelectItem(item),
|
|
2322
|
+
className: "justify-start text-left h-auto py-2 px-3 whitespace-normal",
|
|
2323
|
+
disabled: showCorrectAnswer,
|
|
2324
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content })
|
|
2325
|
+
},
|
|
2326
|
+
item.id
|
|
2327
|
+
)) }),
|
|
2328
|
+
availableItems.length === 0 && selectedSequence.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: 'T\u1EA5t c\u1EA3 c\xE1c m\u1EE5c \u0111\xE3 \u0111\u01B0\u1EE3c ch\u1ECDn. Nh\u1EA5p v\xE0o m\u1EE5c trong "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn" \u0111\u1EC3 b\u1ECF ch\u1ECDn.' })
|
|
2329
|
+
] }),
|
|
2330
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2331
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
2332
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn:" }),
|
|
2333
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: handleResetSequence, disabled: showCorrectAnswer || selectedSequence.length === 0, children: [
|
|
2334
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "mr-2 h-3.5 w-3.5" }),
|
|
2335
|
+
" \u0110\u1EB7t l\u1EA1i"
|
|
2336
|
+
] })
|
|
2337
|
+
] }),
|
|
2338
|
+
selectedSequence.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground p-3 border border-dashed rounded-md", children: "Ch\u01B0a ch\u1ECDn m\u1EE5c n\xE0o. Nh\u1EA5p v\xE0o c\xE1c m\u1EE5c \u1EDF tr\xEAn \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-2", children: selectedSequence.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2339
|
+
"li",
|
|
2340
|
+
{
|
|
2341
|
+
onClick: () => handleRemoveFromSequence(item, index),
|
|
2342
|
+
className: `flex items-center justify-between p-3 border rounded-md whitespace-normal ${showCorrectAnswer ? userAnswer?.[index] === correctOrder[index] ? "border-green-500 bg-green-500/10" : "border-destructive bg-destructive/10" : "bg-muted/30 cursor-pointer hover:border-destructive/50"} transition-colors`,
|
|
2343
|
+
children: [
|
|
2344
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow flex items-center", children: [
|
|
2345
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold mr-2", children: [
|
|
2346
|
+
index + 1,
|
|
2347
|
+
"."
|
|
2348
|
+
] }),
|
|
2349
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content })
|
|
2350
|
+
] }),
|
|
2351
|
+
showCorrectAnswer ? getFeedbackIcon(index) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-muted-foreground hover:text-destructive flex-shrink-0" })
|
|
2352
|
+
]
|
|
2353
|
+
},
|
|
2354
|
+
item.id
|
|
2355
|
+
)) })
|
|
2356
|
+
] }),
|
|
2357
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2358
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2359
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Th\u1EE9 t\u1EF1 \u0111\xFAng:" }),
|
|
2360
|
+
/* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside text-sm text-accent-foreground/80 space-y-1 mt-1", children: correctOrder.map((itemId) => {
|
|
2361
|
+
const item = items.find((i) => i.id === itemId);
|
|
2362
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item ? item.content : "Kh\xF4ng t\xECm th\u1EA5y m\u1EE5c" }) }, itemId);
|
|
2363
|
+
}) })
|
|
2364
|
+
] }),
|
|
2365
|
+
explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
2366
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2367
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
2368
|
+
] })
|
|
2369
|
+
] })
|
|
2370
|
+
] })
|
|
2371
|
+
] });
|
|
2299
2372
|
};
|
|
2300
2373
|
var Select = SelectPrimitive__namespace.Root;
|
|
2301
2374
|
var SelectValue = SelectPrimitive__namespace.Value;
|
|
2302
|
-
var SelectTrigger =
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
className
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2375
|
+
var SelectTrigger = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2376
|
+
SelectPrimitive__namespace.Trigger,
|
|
2377
|
+
{
|
|
2378
|
+
ref,
|
|
2379
|
+
className: cn(
|
|
2380
|
+
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
2381
|
+
className
|
|
2382
|
+
),
|
|
2383
|
+
...props,
|
|
2384
|
+
children: [
|
|
2385
|
+
children,
|
|
2386
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
2387
|
+
]
|
|
2388
|
+
}
|
|
2389
|
+
));
|
|
2317
2390
|
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
2318
|
-
var SelectScrollUpButton =
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
className
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
);
|
|
2331
|
-
});
|
|
2391
|
+
var SelectScrollUpButton = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2392
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
2393
|
+
{
|
|
2394
|
+
ref,
|
|
2395
|
+
className: cn(
|
|
2396
|
+
"flex cursor-default items-center justify-center py-1",
|
|
2397
|
+
className
|
|
2398
|
+
),
|
|
2399
|
+
...props,
|
|
2400
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
|
|
2401
|
+
}
|
|
2402
|
+
));
|
|
2332
2403
|
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
2333
|
-
var SelectScrollDownButton =
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
className
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
);
|
|
2346
|
-
});
|
|
2404
|
+
var SelectScrollDownButton = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2405
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
2406
|
+
{
|
|
2407
|
+
ref,
|
|
2408
|
+
className: cn(
|
|
2409
|
+
"flex cursor-default items-center justify-center py-1",
|
|
2410
|
+
className
|
|
2411
|
+
),
|
|
2412
|
+
...props,
|
|
2413
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
|
|
2414
|
+
}
|
|
2415
|
+
));
|
|
2347
2416
|
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
2348
|
-
var SelectContent =
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
2357
|
-
className
|
|
2358
|
-
),
|
|
2359
|
-
position
|
|
2360
|
-
}, props),
|
|
2361
|
-
/* @__PURE__ */ React28__namespace.createElement(SelectScrollUpButton, null),
|
|
2362
|
-
/* @__PURE__ */ React28__namespace.createElement(
|
|
2363
|
-
SelectPrimitive__namespace.Viewport,
|
|
2364
|
-
{
|
|
2365
|
-
className: cn(
|
|
2366
|
-
"p-1",
|
|
2367
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
2368
|
-
)
|
|
2369
|
-
},
|
|
2370
|
-
children
|
|
2417
|
+
var SelectContent = React9__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2418
|
+
SelectPrimitive__namespace.Content,
|
|
2419
|
+
{
|
|
2420
|
+
ref,
|
|
2421
|
+
className: cn(
|
|
2422
|
+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
2423
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
2424
|
+
className
|
|
2371
2425
|
),
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2426
|
+
position,
|
|
2427
|
+
...props,
|
|
2428
|
+
children: [
|
|
2429
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
2430
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2431
|
+
SelectPrimitive__namespace.Viewport,
|
|
2432
|
+
{
|
|
2433
|
+
className: cn(
|
|
2434
|
+
"p-1",
|
|
2435
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
2436
|
+
),
|
|
2437
|
+
children
|
|
2438
|
+
}
|
|
2439
|
+
),
|
|
2440
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
2441
|
+
]
|
|
2442
|
+
}
|
|
2443
|
+
) }));
|
|
2375
2444
|
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
2376
|
-
var SelectLabel =
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
);
|
|
2385
|
-
});
|
|
2445
|
+
var SelectLabel = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2446
|
+
SelectPrimitive__namespace.Label,
|
|
2447
|
+
{
|
|
2448
|
+
ref,
|
|
2449
|
+
className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
|
|
2450
|
+
...props
|
|
2451
|
+
}
|
|
2452
|
+
));
|
|
2386
2453
|
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
2387
|
-
var SelectItem =
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
className
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2454
|
+
var SelectItem = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2455
|
+
SelectPrimitive__namespace.Item,
|
|
2456
|
+
{
|
|
2457
|
+
ref,
|
|
2458
|
+
className: cn(
|
|
2459
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
2460
|
+
className
|
|
2461
|
+
),
|
|
2462
|
+
...props,
|
|
2463
|
+
children: [
|
|
2464
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
2465
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
2466
|
+
]
|
|
2467
|
+
}
|
|
2468
|
+
));
|
|
2402
2469
|
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
2403
|
-
var SelectSeparator =
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
);
|
|
2412
|
-
});
|
|
2470
|
+
var SelectSeparator = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2471
|
+
SelectPrimitive__namespace.Separator,
|
|
2472
|
+
{
|
|
2473
|
+
ref,
|
|
2474
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
2475
|
+
...props
|
|
2476
|
+
}
|
|
2477
|
+
));
|
|
2413
2478
|
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
2414
2479
|
var MatchingQuestionUI = ({
|
|
2415
2480
|
question,
|
|
@@ -2418,16 +2483,16 @@ var MatchingQuestionUI = ({
|
|
|
2418
2483
|
showCorrectAnswer = false
|
|
2419
2484
|
}) => {
|
|
2420
2485
|
const { prompt, prompts, options: initialOptions, points, explanation, correctAnswerMap, id: questionId } = question;
|
|
2421
|
-
const [currentAnswers, setCurrentAnswers] =
|
|
2422
|
-
const [shuffledOptions, setShuffledOptions] =
|
|
2423
|
-
|
|
2486
|
+
const [currentAnswers, setCurrentAnswers] = React9.useState({});
|
|
2487
|
+
const [shuffledOptions, setShuffledOptions] = React9.useState(initialOptions);
|
|
2488
|
+
React9.useEffect(() => {
|
|
2424
2489
|
if (question.shuffleOptions) {
|
|
2425
2490
|
setShuffledOptions([...initialOptions].sort(() => Math.random() - 0.5));
|
|
2426
2491
|
} else {
|
|
2427
2492
|
setShuffledOptions(initialOptions);
|
|
2428
2493
|
}
|
|
2429
2494
|
}, [initialOptions, question.shuffleOptions]);
|
|
2430
|
-
|
|
2495
|
+
React9.useEffect(() => {
|
|
2431
2496
|
if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
|
|
2432
2497
|
setCurrentAnswers(userAnswer);
|
|
2433
2498
|
} else {
|
|
@@ -2437,14 +2502,13 @@ var MatchingQuestionUI = ({
|
|
|
2437
2502
|
}
|
|
2438
2503
|
}, [userAnswer, prompts]);
|
|
2439
2504
|
const handleSelectChange = (promptId, optionId) => {
|
|
2440
|
-
const newAnswers =
|
|
2505
|
+
const newAnswers = { ...currentAnswers, [promptId]: optionId };
|
|
2441
2506
|
setCurrentAnswers(newAnswers);
|
|
2442
2507
|
const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
|
|
2443
2508
|
onAnswerChange(hasSelection ? newAnswers : null);
|
|
2444
2509
|
};
|
|
2445
2510
|
const getCorrectOptionIdForPrompt = (promptId) => {
|
|
2446
|
-
|
|
2447
|
-
return (_a = correctAnswerMap.find((map) => map.promptId === promptId)) == null ? void 0 : _a.optionId;
|
|
2511
|
+
return correctAnswerMap.find((map) => map.promptId === promptId)?.optionId;
|
|
2448
2512
|
};
|
|
2449
2513
|
const getPlainText = (htmlString) => {
|
|
2450
2514
|
if (!htmlString) return "";
|
|
@@ -2455,31 +2519,58 @@ var MatchingQuestionUI = ({
|
|
|
2455
2519
|
}
|
|
2456
2520
|
return htmlString.replace(/<[^>]*>?/gm, "");
|
|
2457
2521
|
};
|
|
2458
|
-
return /* @__PURE__ */
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2522
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2523
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2524
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2525
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2526
|
+
"Points: ",
|
|
2527
|
+
points
|
|
2528
|
+
] })
|
|
2529
|
+
] }),
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-4", children: prompts.map((promptItem) => {
|
|
2532
|
+
const selectedOptionId = currentAnswers[promptItem.id] || "";
|
|
2533
|
+
const correctOptionId = getCorrectOptionIdForPrompt(promptItem.id);
|
|
2534
|
+
const isSelectionCorrect = showCorrectAnswer && selectedOptionId ? selectedOptionId === correctOptionId : null;
|
|
2535
|
+
let borderColor = "border-muted";
|
|
2536
|
+
if (showCorrectAnswer && selectedOptionId) {
|
|
2537
|
+
borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
|
|
2538
|
+
}
|
|
2539
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `p-3 border rounded-md ${borderColor} transition-colors bg-background`, children: [
|
|
2540
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: promptItem.content }) }),
|
|
2541
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2542
|
+
Select,
|
|
2543
|
+
{
|
|
2544
|
+
value: selectedOptionId,
|
|
2545
|
+
onValueChange: (value) => handleSelectChange(promptItem.id, value),
|
|
2546
|
+
disabled: showCorrectAnswer,
|
|
2547
|
+
children: [
|
|
2548
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: `select-prompt-${promptItem.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Ch\u1ECDn \u0111\xE1p \xE1n..." }) }),
|
|
2549
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: shuffledOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.id, className: "whitespace-normal", children: getPlainText(option.content) }, option.id)) })
|
|
2550
|
+
]
|
|
2551
|
+
}
|
|
2552
|
+
),
|
|
2553
|
+
showCorrectAnswer && selectedOptionId && (isSelectionCorrect ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 mt-2 inline-block" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-5 w-5 text-destructive mt-2 inline-block" })),
|
|
2554
|
+
showCorrectAnswer && !selectedOptionId && correctOptionId && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
2555
|
+
"Ch\u01B0a ch\u1ECDn. \u0110\xE1p \xE1n \u0111\xFAng: ",
|
|
2556
|
+
getPlainText(shuffledOptions.find((o) => o.id === correctOptionId)?.content)
|
|
2557
|
+
] })
|
|
2558
|
+
] }, promptItem.id);
|
|
2559
|
+
}) }),
|
|
2560
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2561
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2562
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2563
|
+
] }),
|
|
2564
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2565
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
|
|
2566
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: correctAnswerMap.map((map) => {
|
|
2567
|
+
const promptText = prompts.find((p) => p.id === map.promptId)?.content;
|
|
2568
|
+
const optionText = initialOptions.find((o) => o.id === map.optionId)?.content;
|
|
2569
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: `<strong>${getPlainText(promptText) || "N/A"}</strong> gh\xE9p v\u1EDBi <strong>${getPlainText(optionText) || "N/A"}</strong>` }) }, map.promptId);
|
|
2570
|
+
}) })
|
|
2571
|
+
] })
|
|
2572
|
+
] })
|
|
2573
|
+
] });
|
|
2483
2574
|
};
|
|
2484
2575
|
var DragAndDropQuestionUI = ({
|
|
2485
2576
|
question,
|
|
@@ -2488,8 +2579,8 @@ var DragAndDropQuestionUI = ({
|
|
|
2488
2579
|
showCorrectAnswer = false
|
|
2489
2580
|
}) => {
|
|
2490
2581
|
const { prompt, draggableItems, dropZones, points, explanation, answerMap, id: questionId, backgroundImageUrl } = question;
|
|
2491
|
-
const [currentAnswers, setCurrentAnswers] =
|
|
2492
|
-
|
|
2582
|
+
const [currentAnswers, setCurrentAnswers] = React9.useState({});
|
|
2583
|
+
React9.useEffect(() => {
|
|
2493
2584
|
if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
|
|
2494
2585
|
setCurrentAnswers(userAnswer);
|
|
2495
2586
|
} else {
|
|
@@ -2499,41 +2590,70 @@ var DragAndDropQuestionUI = ({
|
|
|
2499
2590
|
}
|
|
2500
2591
|
}, [userAnswer, draggableItems]);
|
|
2501
2592
|
const handleSelectChange = (draggableItemId, dropZoneId) => {
|
|
2502
|
-
const newAnswers =
|
|
2593
|
+
const newAnswers = { ...currentAnswers, [draggableItemId]: dropZoneId };
|
|
2503
2594
|
setCurrentAnswers(newAnswers);
|
|
2504
2595
|
const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
|
|
2505
2596
|
onAnswerChange(hasSelection ? newAnswers : null);
|
|
2506
2597
|
};
|
|
2507
2598
|
const getCorrectDropZoneIdForDraggable = (draggableItemId) => {
|
|
2508
|
-
|
|
2509
|
-
return (_a = answerMap.find((map) => map.draggableId === draggableItemId)) == null ? void 0 : _a.dropZoneId;
|
|
2599
|
+
return answerMap.find((map) => map.draggableId === draggableItemId)?.dropZoneId;
|
|
2510
2600
|
};
|
|
2511
|
-
return /* @__PURE__ */
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2601
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2602
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2603
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2604
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2605
|
+
"Points: ",
|
|
2606
|
+
points
|
|
2607
|
+
] })
|
|
2608
|
+
] }),
|
|
2609
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2610
|
+
backgroundImageUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 overflow-hidden rounded-md border", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: backgroundImageUrl, alt: question.imageAltText || "Drag and drop background", className: "w-full h-auto object-contain max-h-[300px]", "data-ai-hint": "abstract pattern" }) }),
|
|
2611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2612
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "Gh\xE9p c\xE1c m\u1EE5c sau v\xE0o \u0111\xFAng v\u1ECB tr\xED:" }),
|
|
2613
|
+
draggableItems.map((item) => {
|
|
2614
|
+
const selectedDropZoneId = currentAnswers[item.id] || "";
|
|
2615
|
+
const correctDropZoneId = getCorrectDropZoneIdForDraggable(item.id);
|
|
2616
|
+
const isSelectionCorrect = showCorrectAnswer && selectedDropZoneId ? selectedDropZoneId === correctDropZoneId : null;
|
|
2617
|
+
let itemStyle = "flex flex-col sm:flex-row items-start sm:items-center justify-between p-3 border rounded-md transition-colors bg-background";
|
|
2618
|
+
if (showCorrectAnswer && selectedDropZoneId) {
|
|
2619
|
+
itemStyle += isSelectionCorrect ? " border-green-500" : " border-destructive";
|
|
2620
|
+
} else {
|
|
2621
|
+
itemStyle += " border-muted";
|
|
2622
|
+
}
|
|
2623
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemStyle, children: [
|
|
2624
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `select-draggable-${item.id}`, className: "font-medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content }) }),
|
|
2625
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 w-full sm:w-auto", children: [
|
|
2626
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2627
|
+
Select,
|
|
2628
|
+
{
|
|
2629
|
+
value: selectedDropZoneId,
|
|
2630
|
+
onValueChange: (value) => handleSelectChange(item.id, value),
|
|
2631
|
+
disabled: showCorrectAnswer,
|
|
2632
|
+
children: [
|
|
2633
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: `select-draggable-${item.id}`, className: "w-full sm:min-w-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Ch\u1ECDn v\u1ECB tr\xED..." }) }),
|
|
2634
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: dropZones.map((zone) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: zone.id, children: zone.label.replace(/<[^>]*>?/gm, "") }, zone.id)) })
|
|
2635
|
+
]
|
|
2636
|
+
}
|
|
2637
|
+
),
|
|
2638
|
+
showCorrectAnswer && selectedDropZoneId && (isSelectionCorrect ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-5 w-5 text-destructive flex-shrink-0" }))
|
|
2639
|
+
] })
|
|
2640
|
+
] }, item.id);
|
|
2641
|
+
})
|
|
2642
|
+
] }),
|
|
2643
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2644
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2645
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2646
|
+
] }),
|
|
2647
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2648
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
|
|
2649
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: answerMap.map((map) => {
|
|
2650
|
+
const draggableText = draggableItems.find((d) => d.id === map.draggableId)?.content;
|
|
2651
|
+
const dropZoneText = dropZones.find((z3) => z3.id === map.dropZoneId)?.label;
|
|
2652
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: `<strong>${draggableText || "N/A"}</strong> v\xE0o <strong>${dropZoneText || "N/A"}</strong>` }) }, map.draggableId);
|
|
2653
|
+
}) })
|
|
2654
|
+
] })
|
|
2655
|
+
] })
|
|
2656
|
+
] });
|
|
2537
2657
|
};
|
|
2538
2658
|
var HotspotQuestionUI = ({
|
|
2539
2659
|
question,
|
|
@@ -2542,8 +2662,8 @@ var HotspotQuestionUI = ({
|
|
|
2542
2662
|
showCorrectAnswer = false
|
|
2543
2663
|
}) => {
|
|
2544
2664
|
const { prompt, imageUrl, imageAltText, hotspots, points, explanation, correctAnswerIds, id: questionId } = question;
|
|
2545
|
-
const [selectedIds, setSelectedIds] =
|
|
2546
|
-
|
|
2665
|
+
const [selectedIds, setSelectedIds] = React9.useState([]);
|
|
2666
|
+
React9.useEffect(() => {
|
|
2547
2667
|
if (Array.isArray(userAnswer)) {
|
|
2548
2668
|
setSelectedIds(userAnswer);
|
|
2549
2669
|
} else {
|
|
@@ -2603,32 +2723,54 @@ var HotspotQuestionUI = ({
|
|
|
2603
2723
|
tempDiv.innerHTML = htmlString;
|
|
2604
2724
|
return tempDiv.textContent || tempDiv.innerText || "";
|
|
2605
2725
|
};
|
|
2606
|
-
return /* @__PURE__ */
|
|
2607
|
-
"
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2726
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2727
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2728
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2729
|
+
points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2730
|
+
"Points: ",
|
|
2731
|
+
points
|
|
2732
|
+
] })
|
|
2733
|
+
] }),
|
|
2734
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2735
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full border border-muted rounded-md overflow-hidden", style: { maxWidth: "100%", maxHeight: "500px" }, children: [
|
|
2736
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2737
|
+
"img",
|
|
2738
|
+
{
|
|
2739
|
+
src: imageUrl,
|
|
2740
|
+
alt: imageAltText || "Hotspot image",
|
|
2741
|
+
className: "block max-w-full max-h-full object-contain",
|
|
2742
|
+
"data-ai-hint": question.imageAltText ? question.imageAltText.split(" ").slice(0, 2).join(" ") : "diagram illustration"
|
|
2743
|
+
}
|
|
2744
|
+
),
|
|
2745
|
+
hotspots.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2746
|
+
"div",
|
|
2747
|
+
{
|
|
2748
|
+
title: getPlainText(hotspot.description) || `Hotspot ${hotspot.id}`,
|
|
2749
|
+
style: getHotspotStyle(hotspot),
|
|
2750
|
+
onClick: () => handleHotspotClick(hotspot.id),
|
|
2751
|
+
"aria-pressed": selectedIds.includes(hotspot.id),
|
|
2752
|
+
role: "button",
|
|
2753
|
+
tabIndex: showCorrectAnswer ? -1 : 0,
|
|
2754
|
+
onKeyDown: (e) => {
|
|
2755
|
+
if (e.key === "Enter" || e.key === " ") handleHotspotClick(hotspot.id);
|
|
2756
|
+
}
|
|
2757
|
+
},
|
|
2758
|
+
hotspot.id
|
|
2759
|
+
))
|
|
2760
|
+
] }),
|
|
2761
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2762
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
2763
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2764
|
+
] }),
|
|
2765
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2766
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "Correct Hotspots:" }),
|
|
2767
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: (correctAnswerIds || []).map((id) => {
|
|
2768
|
+
const hotspot = hotspots.find((h) => h.id === id);
|
|
2769
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: hotspot?.description || hotspot?.id || "N/A", className: "inline" }) }, id);
|
|
2770
|
+
}) })
|
|
2771
|
+
] })
|
|
2772
|
+
] })
|
|
2773
|
+
] });
|
|
2632
2774
|
};
|
|
2633
2775
|
var loadScript = (src, async = true) => {
|
|
2634
2776
|
return new Promise((resolve, reject) => {
|
|
@@ -2661,8 +2803,7 @@ var loadScript = (src, async = true) => {
|
|
|
2661
2803
|
};
|
|
2662
2804
|
var loadBlocklyScript = () => {
|
|
2663
2805
|
return new Promise((resolve, reject) => {
|
|
2664
|
-
|
|
2665
|
-
if (typeof ((_a = window.Blockly) == null ? void 0 : _a.Blocks) !== "undefined" && typeof ((_b = window.Blockly) == null ? void 0 : _b.JavaScript) !== "undefined") {
|
|
2806
|
+
if (typeof window.Blockly?.Blocks !== "undefined" && typeof window.Blockly?.JavaScript !== "undefined") {
|
|
2666
2807
|
resolve();
|
|
2667
2808
|
return;
|
|
2668
2809
|
}
|
|
@@ -2690,7 +2831,6 @@ var loadBlocklyScript = () => {
|
|
|
2690
2831
|
}
|
|
2691
2832
|
];
|
|
2692
2833
|
const tryLoadFromCDN = async (cdnIndex) => {
|
|
2693
|
-
var _a2, _b2, _c;
|
|
2694
2834
|
if (cdnIndex >= cdnOptions.length) {
|
|
2695
2835
|
throw new Error("All Blockly CDN loading options failed");
|
|
2696
2836
|
}
|
|
@@ -2699,9 +2839,9 @@ var loadBlocklyScript = () => {
|
|
|
2699
2839
|
await loadScript(cdn.mainSrc);
|
|
2700
2840
|
const BlocklyGlobal = window.Blockly;
|
|
2701
2841
|
if (typeof BlocklyGlobal === "undefined") throw new Error(`Blockly global not found from ${cdn.name}.`);
|
|
2702
|
-
if (
|
|
2842
|
+
if (BlocklyGlobal.utils?.global?.setPaths) {
|
|
2703
2843
|
BlocklyGlobal.utils.global.setPaths(cdn.mediaPath);
|
|
2704
|
-
} else if (
|
|
2844
|
+
} else if (BlocklyGlobal.utils?.global) {
|
|
2705
2845
|
BlocklyGlobal.utils.global.blocklyPath = cdn.mediaPath;
|
|
2706
2846
|
BlocklyGlobal.MEDIA = cdn.mediaPath;
|
|
2707
2847
|
} else {
|
|
@@ -2719,10 +2859,10 @@ var loadBlocklyScript = () => {
|
|
|
2719
2859
|
});
|
|
2720
2860
|
};
|
|
2721
2861
|
var useBlocklyLoader = () => {
|
|
2722
|
-
const [isLoading, setIsLoading] =
|
|
2723
|
-
const [loadError, setLoadError] =
|
|
2724
|
-
const [isReady, setIsReady] =
|
|
2725
|
-
const attemptLoad =
|
|
2862
|
+
const [isLoading, setIsLoading] = React9.useState(true);
|
|
2863
|
+
const [loadError, setLoadError] = React9.useState(null);
|
|
2864
|
+
const [isReady, setIsReady] = React9.useState(false);
|
|
2865
|
+
const attemptLoad = React9.useCallback(() => {
|
|
2726
2866
|
setLoadError(null);
|
|
2727
2867
|
setIsReady(false);
|
|
2728
2868
|
loadBlocklyScript().then(() => {
|
|
@@ -2735,32 +2875,31 @@ var useBlocklyLoader = () => {
|
|
|
2735
2875
|
setIsReady(false);
|
|
2736
2876
|
});
|
|
2737
2877
|
}, []);
|
|
2738
|
-
|
|
2878
|
+
React9.useEffect(() => {
|
|
2739
2879
|
if (isLoading && !isReady && !loadError) attemptLoad();
|
|
2740
2880
|
}, [isLoading, isReady, loadError, attemptLoad]);
|
|
2741
|
-
const retry =
|
|
2881
|
+
const retry = React9.useCallback(() => {
|
|
2742
2882
|
setLoadError(null);
|
|
2743
2883
|
setIsReady(false);
|
|
2744
2884
|
setIsLoading(true);
|
|
2745
2885
|
}, []);
|
|
2746
2886
|
return { isLoading, loadError, isReady, retry };
|
|
2747
2887
|
};
|
|
2748
|
-
var BlocklyProgrammingQuestionUI =
|
|
2888
|
+
var BlocklyProgrammingQuestionUI = React9__namespace.default.forwardRef(({
|
|
2749
2889
|
question,
|
|
2750
2890
|
userAnswer,
|
|
2751
2891
|
showCorrectAnswer = false
|
|
2752
2892
|
}, ref) => {
|
|
2753
|
-
const blocklyDivRef =
|
|
2754
|
-
const workspaceRef =
|
|
2755
|
-
const [isInitializingComponent, setIsInitializingComponent] =
|
|
2756
|
-
const [componentError, setComponentError] =
|
|
2893
|
+
const blocklyDivRef = React9.useRef(null);
|
|
2894
|
+
const workspaceRef = React9.useRef(null);
|
|
2895
|
+
const [isInitializingComponent, setIsInitializingComponent] = React9.useState(false);
|
|
2896
|
+
const [componentError, setComponentError] = React9.useState(null);
|
|
2757
2897
|
const { isLoading: blocklyLoading, loadError: blocklyLoadError, isReady: blocklyReady, retry } = useBlocklyLoader();
|
|
2758
|
-
|
|
2898
|
+
React9.useImperativeHandle(ref, () => ({
|
|
2759
2899
|
getWorkspaceXml: () => {
|
|
2760
|
-
var _a, _b;
|
|
2761
2900
|
if (workspaceRef.current && blocklyReady) {
|
|
2762
2901
|
const LocalBlockly = window.Blockly;
|
|
2763
|
-
if (!
|
|
2902
|
+
if (!LocalBlockly?.Xml?.workspaceToDom || !LocalBlockly?.Xml?.domToText) {
|
|
2764
2903
|
console.warn("Blockly.Xml methods not available for XML serialization in getWorkspaceXml.");
|
|
2765
2904
|
return null;
|
|
2766
2905
|
}
|
|
@@ -2775,11 +2914,10 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
|
|
|
2775
2914
|
return null;
|
|
2776
2915
|
}
|
|
2777
2916
|
}));
|
|
2778
|
-
const initializeBlocklyWorkspace =
|
|
2779
|
-
var _a;
|
|
2917
|
+
const initializeBlocklyWorkspace = React9.useCallback(() => {
|
|
2780
2918
|
if (!blocklyReady || !blocklyDivRef.current) return;
|
|
2781
2919
|
const LocalBlockly = window.Blockly;
|
|
2782
|
-
if (!
|
|
2920
|
+
if (!LocalBlockly?.inject || !LocalBlockly?.Xml || !LocalBlockly?.Events || !LocalBlockly?.Themes) {
|
|
2783
2921
|
setComponentError("Blockly library not fully loaded.");
|
|
2784
2922
|
setIsInitializingComponent(false);
|
|
2785
2923
|
return;
|
|
@@ -2811,7 +2949,7 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
|
|
|
2811
2949
|
}
|
|
2812
2950
|
}
|
|
2813
2951
|
setIsInitializingComponent(true);
|
|
2814
|
-
if (
|
|
2952
|
+
if (workspaceRef.current?.dispose) {
|
|
2815
2953
|
try {
|
|
2816
2954
|
workspaceRef.current.dispose();
|
|
2817
2955
|
} catch (e) {
|
|
@@ -2883,13 +3021,12 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
|
|
|
2883
3021
|
showCorrectAnswer,
|
|
2884
3022
|
userAnswer
|
|
2885
3023
|
]);
|
|
2886
|
-
|
|
3024
|
+
React9.useEffect(() => {
|
|
2887
3025
|
if (blocklyReady && blocklyDivRef.current) {
|
|
2888
3026
|
initializeBlocklyWorkspace();
|
|
2889
3027
|
}
|
|
2890
3028
|
return () => {
|
|
2891
|
-
|
|
2892
|
-
if ((_a = workspaceRef.current) == null ? void 0 : _a.dispose) {
|
|
3029
|
+
if (workspaceRef.current?.dispose) {
|
|
2893
3030
|
try {
|
|
2894
3031
|
workspaceRef.current.dispose();
|
|
2895
3032
|
} catch (disposeError) {
|
|
@@ -2899,14 +3036,14 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
|
|
|
2899
3036
|
}
|
|
2900
3037
|
};
|
|
2901
3038
|
}, [blocklyReady, question.id, initializeBlocklyWorkspace]);
|
|
2902
|
-
|
|
3039
|
+
React9.useEffect(() => {
|
|
2903
3040
|
let resizeTimeout;
|
|
2904
3041
|
const handleResize = () => {
|
|
2905
3042
|
clearTimeout(resizeTimeout);
|
|
2906
3043
|
resizeTimeout = setTimeout(() => {
|
|
2907
3044
|
if (workspaceRef.current && blocklyReady) {
|
|
2908
3045
|
const LocalBlockly = window.Blockly;
|
|
2909
|
-
if (LocalBlockly
|
|
3046
|
+
if (LocalBlockly?.svgResize) {
|
|
2910
3047
|
LocalBlockly.svgResize(workspaceRef.current);
|
|
2911
3048
|
}
|
|
2912
3049
|
}
|
|
@@ -2920,64 +3057,91 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
|
|
|
2920
3057
|
}, [blocklyReady]);
|
|
2921
3058
|
const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
|
|
2922
3059
|
const workspaceContainerId = `blockly-workspace-container-${question.id}`;
|
|
2923
|
-
return /* @__PURE__ */
|
|
2924
|
-
"
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3060
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3061
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3062
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.prompt }) }),
|
|
3063
|
+
question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3064
|
+
"Points: ",
|
|
3065
|
+
question.points
|
|
3066
|
+
] })
|
|
3067
|
+
] }),
|
|
3068
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
3069
|
+
blocklyLoading && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3070
|
+
"div",
|
|
3071
|
+
{
|
|
3072
|
+
style: {
|
|
3073
|
+
height: workspaceHeight,
|
|
3074
|
+
width: "100%",
|
|
3075
|
+
borderRadius: "0.375rem",
|
|
3076
|
+
border: "1px solid hsl(var(--border))",
|
|
3077
|
+
backgroundColor: "hsl(var(--background))"
|
|
3078
|
+
},
|
|
3079
|
+
className: "flex items-center justify-center",
|
|
3080
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
3081
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground animate-pulse mb-2", children: "Loading Blockly Environment..." }),
|
|
3082
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto" })
|
|
3083
|
+
] })
|
|
3084
|
+
}
|
|
3085
|
+
),
|
|
3086
|
+
blocklyLoadError && !blocklyLoading && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3087
|
+
"div",
|
|
3088
|
+
{
|
|
3089
|
+
style: {
|
|
3090
|
+
height: workspaceHeight,
|
|
3091
|
+
width: "100%",
|
|
3092
|
+
borderRadius: "0.375rem",
|
|
3093
|
+
border: "1px solid hsl(var(--destructive))",
|
|
3094
|
+
backgroundColor: "hsl(var(--card))"
|
|
3095
|
+
},
|
|
3096
|
+
className: "flex items-center justify-center p-4",
|
|
3097
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-destructive text-center", children: [
|
|
3098
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-lg", children: "Failed to load Blockly" }),
|
|
3099
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-2 mb-3", children: blocklyLoadError }),
|
|
3100
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-x-2", children: [
|
|
3101
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3102
|
+
"button",
|
|
3103
|
+
{
|
|
3104
|
+
onClick: retry,
|
|
3105
|
+
className: "px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors text-sm",
|
|
3106
|
+
children: "Try Again"
|
|
3107
|
+
}
|
|
3108
|
+
),
|
|
3109
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3110
|
+
"button",
|
|
3111
|
+
{
|
|
3112
|
+
onClick: () => window.location.reload(),
|
|
3113
|
+
className: "px-4 py-2 bg-destructive text-destructive-foreground rounded hover:bg-destructive/90 transition-colors text-sm",
|
|
3114
|
+
children: "Refresh Page"
|
|
3115
|
+
}
|
|
3116
|
+
)
|
|
3117
|
+
] })
|
|
3118
|
+
] })
|
|
3119
|
+
}
|
|
3120
|
+
),
|
|
3121
|
+
!blocklyLoading && !blocklyLoadError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3122
|
+
"div",
|
|
3123
|
+
{
|
|
3124
|
+
id: workspaceContainerId,
|
|
3125
|
+
ref: blocklyDivRef,
|
|
3126
|
+
style: {
|
|
3127
|
+
height: workspaceHeight,
|
|
3128
|
+
width: "100%",
|
|
3129
|
+
borderRadius: "0.375rem",
|
|
3130
|
+
border: `1px solid ${componentError ? "hsl(var(--destructive))" : "hsl(var(--border))"}`,
|
|
3131
|
+
backgroundColor: "hsl(var(--card))",
|
|
3132
|
+
position: "relative",
|
|
3133
|
+
userSelect: "none",
|
|
3134
|
+
overflow: "hidden"
|
|
3135
|
+
},
|
|
3136
|
+
"aria-label": `Blockly programming workspace for question: ${question.prompt}`
|
|
3137
|
+
}
|
|
3138
|
+
),
|
|
3139
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3140
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3141
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
|
|
3142
|
+
] })
|
|
3143
|
+
] })
|
|
3144
|
+
] });
|
|
2981
3145
|
});
|
|
2982
3146
|
BlocklyProgrammingQuestionUI.displayName = "BlocklyProgrammingQuestionUI";
|
|
2983
3147
|
var SCRATCH_JS_ENGINE_LAYOUT_PATH = "/static/scratch-blocks/js/blockly_compressed_vertical.js";
|
|
@@ -3047,18 +3211,17 @@ var loadScript2 = (src) => {
|
|
|
3047
3211
|
loadedScriptPromises.set(fullSrc, promise);
|
|
3048
3212
|
return promise;
|
|
3049
3213
|
};
|
|
3050
|
-
var ScratchProgrammingQuestionUI =
|
|
3214
|
+
var ScratchProgrammingQuestionUI = React9.forwardRef(({
|
|
3051
3215
|
question,
|
|
3052
3216
|
userAnswer,
|
|
3053
3217
|
showCorrectAnswer = false
|
|
3054
3218
|
}, ref) => {
|
|
3055
|
-
const blocklyDivRef =
|
|
3056
|
-
const workspaceRef =
|
|
3057
|
-
const [isBlocklyReady, setIsBlocklyReady] =
|
|
3058
|
-
const [componentError, setComponentError] =
|
|
3059
|
-
const [isLoadingScripts, setIsLoadingScripts] =
|
|
3060
|
-
const attemptLoadScripts =
|
|
3061
|
-
var _a, _b, _c, _d;
|
|
3219
|
+
const blocklyDivRef = React9.useRef(null);
|
|
3220
|
+
const workspaceRef = React9.useRef(null);
|
|
3221
|
+
const [isBlocklyReady, setIsBlocklyReady] = React9.useState(false);
|
|
3222
|
+
const [componentError, setComponentError] = React9.useState(null);
|
|
3223
|
+
const [isLoadingScripts, setIsLoadingScripts] = React9.useState(true);
|
|
3224
|
+
const attemptLoadScripts = React9.useCallback(async () => {
|
|
3062
3225
|
setIsLoadingScripts(true);
|
|
3063
3226
|
setComponentError(null);
|
|
3064
3227
|
console.log("ScratchUI: Starting script loading sequence...");
|
|
@@ -3069,19 +3232,19 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3069
3232
|
if (typeof BlocklyGlobalEngine === "undefined") {
|
|
3070
3233
|
throw new Error(`Blockly global object (window.Blockly) not found after loading engine script: ${SCRATCH_JS_ENGINE_LAYOUT_PATH}.`);
|
|
3071
3234
|
}
|
|
3072
|
-
console.log(`ScratchUI: After engine/layout load: Blockly defined: ${!!BlocklyGlobalEngine}, Blockly.Blocks defined: ${!!
|
|
3235
|
+
console.log(`ScratchUI: After engine/layout load: Blockly defined: ${!!BlocklyGlobalEngine}, Blockly.Blocks defined: ${!!BlocklyGlobalEngine?.Blocks}, Blockly.Msg defined: ${!!BlocklyGlobalEngine?.Msg}, Blockly.ScratchMsgs defined: ${!!BlocklyGlobalEngine?.ScratchMsgs}`);
|
|
3073
3236
|
console.log("ScratchUI: Attempting to load Messages:", SCRATCH_JS_MSG_EN_PATH);
|
|
3074
3237
|
await loadScript2(SCRATCH_JS_MSG_EN_PATH);
|
|
3075
3238
|
let BlocklyGlobalAfterMsg = window.Blockly;
|
|
3076
3239
|
if (!BlocklyGlobalAfterMsg) throw new Error("Blockly global disappeared after loading message script.");
|
|
3077
|
-
console.log(`ScratchUI: After en.js load: Blockly defined: ${!!BlocklyGlobalAfterMsg}, Blockly.Msg defined: ${!!
|
|
3240
|
+
console.log(`ScratchUI: After en.js load: Blockly defined: ${!!BlocklyGlobalAfterMsg}, Blockly.Msg defined: ${!!BlocklyGlobalAfterMsg?.Msg}, Keys in Blockly.Msg: ${BlocklyGlobalAfterMsg?.Msg ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${BlocklyGlobalAfterMsg?.Msg?.LOGIC_HUE}`);
|
|
3078
3241
|
if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
|
|
3079
3242
|
console.warn("ScratchUI: Blockly.Msg appears unpopulated or empty. Checking if Blockly.ScratchMsgs.addLocaleData can be used...");
|
|
3080
3243
|
if (BlocklyGlobalAfterMsg.ScratchMsgs && typeof BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData === "function") {
|
|
3081
3244
|
console.log("ScratchUI: Blockly.ScratchMsgs.addLocaleData is available. Attempting to call Blockly.ScratchMsgs.addLocaleData('en').");
|
|
3082
3245
|
BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData("en");
|
|
3083
3246
|
BlocklyGlobalAfterMsg = window.Blockly;
|
|
3084
|
-
console.log(`ScratchUI: After attempting addLocaleData('en'): Keys in Blockly.Msg: ${
|
|
3247
|
+
console.log(`ScratchUI: After attempting addLocaleData('en'): Keys in Blockly.Msg: ${BlocklyGlobalAfterMsg?.Msg ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${BlocklyGlobalAfterMsg?.Msg?.LOGIC_HUE}`);
|
|
3085
3248
|
if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
|
|
3086
3249
|
throw new Error("Blockly.Msg still empty after calling addLocaleData. Message loading failed critically.");
|
|
3087
3250
|
}
|
|
@@ -3095,8 +3258,8 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3095
3258
|
if (!BlocklyGlobalBlocks || !BlocklyGlobalBlocks.Blocks) {
|
|
3096
3259
|
throw new Error(`Blockly.Blocks not defined after loading ${SCRATCH_JS_BLOCK_DEFINITIONS_PATH}.`);
|
|
3097
3260
|
}
|
|
3098
|
-
console.log(`ScratchUI: After block definitions load: Blockly.Blocks defined: ${!!BlocklyGlobalBlocks.Blocks}. Keys: ${BlocklyGlobalBlocks.Blocks ? Object.keys(BlocklyGlobalBlocks.Blocks).slice(0, 10).join(", ") + "..." : "N/A"}. Essential block motion_movesteps defined: ${!!
|
|
3099
|
-
if (typeof
|
|
3261
|
+
console.log(`ScratchUI: After block definitions load: Blockly.Blocks defined: ${!!BlocklyGlobalBlocks.Blocks}. Keys: ${BlocklyGlobalBlocks.Blocks ? Object.keys(BlocklyGlobalBlocks.Blocks).slice(0, 10).join(", ") + "..." : "N/A"}. Essential block motion_movesteps defined: ${!!BlocklyGlobalBlocks.Blocks?.motion_movesteps}`);
|
|
3262
|
+
if (typeof BlocklyGlobalBlocks.Blocks?.motion_movesteps === "undefined") {
|
|
3100
3263
|
throw new Error(`Essential Scratch blocks (e.g., motion_movesteps) not found after loading block definitions. Available blocks: ${Object.keys(BlocklyGlobalBlocks.Blocks || {}).join(", ")}`);
|
|
3101
3264
|
}
|
|
3102
3265
|
setIsBlocklyReady(true);
|
|
@@ -3109,13 +3272,13 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3109
3272
|
setIsLoadingScripts(false);
|
|
3110
3273
|
}
|
|
3111
3274
|
}, []);
|
|
3112
|
-
|
|
3275
|
+
React9.useEffect(() => {
|
|
3113
3276
|
attemptLoadScripts();
|
|
3114
3277
|
}, [attemptLoadScripts]);
|
|
3115
|
-
|
|
3278
|
+
React9.useImperativeHandle(ref, () => ({
|
|
3116
3279
|
getWorkspaceXml: () => {
|
|
3117
3280
|
const LocalBlockly = window.Blockly;
|
|
3118
|
-
if (workspaceRef.current &&
|
|
3281
|
+
if (workspaceRef.current && LocalBlockly?.Xml) {
|
|
3119
3282
|
try {
|
|
3120
3283
|
const xml = LocalBlockly.Xml.workspaceToDom(workspaceRef.current);
|
|
3121
3284
|
return LocalBlockly.Xml.domToText(xml);
|
|
@@ -3127,8 +3290,7 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3127
3290
|
return null;
|
|
3128
3291
|
}
|
|
3129
3292
|
}));
|
|
3130
|
-
const initializeWorkspace =
|
|
3131
|
-
var _a, _b;
|
|
3293
|
+
const initializeWorkspace = React9.useCallback(() => {
|
|
3132
3294
|
const LocalBlockly = window.Blockly;
|
|
3133
3295
|
if (!isBlocklyReady || !blocklyDivRef.current || !LocalBlockly) {
|
|
3134
3296
|
console.warn("ScratchUI: Conditions not met for workspace initialization. isBlocklyReady:", isBlocklyReady, "blocklyDivRef.current:", !!blocklyDivRef.current, "LocalBlockly:", !!LocalBlockly);
|
|
@@ -3143,7 +3305,7 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3143
3305
|
console.error("ScratchUI: Blockly.Msg is empty or missing common keys before injection. Current Msg keys count:", Object.keys(LocalBlockly.Msg).length, "CATEGORY_MOTION:", LocalBlockly.Msg.CATEGORY_MOTION);
|
|
3144
3306
|
return;
|
|
3145
3307
|
}
|
|
3146
|
-
if (typeof
|
|
3308
|
+
if (typeof LocalBlockly.Blocks?.motion_movesteps === "undefined" || typeof LocalBlockly.Blocks?.event_whenflagclicked === "undefined") {
|
|
3147
3309
|
const availableBlocks = Object.keys(LocalBlockly.Blocks || {}).join(", ");
|
|
3148
3310
|
setComponentError(`ScratchUI: Essential Scratch block definitions (e.g., motion_movesteps, event_whenflagclicked) are missing before injection. Available blocks: ${availableBlocks}`);
|
|
3149
3311
|
return;
|
|
@@ -3231,13 +3393,13 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3231
3393
|
setComponentError(`ScratchUI: Workspace initialization failed: ${e.message || String(e)}. Check console for details. Toolbox used: ${question.toolboxDefinition ? "Custom" : "Default Simplified"}.`);
|
|
3232
3394
|
}
|
|
3233
3395
|
}, [question, showCorrectAnswer, userAnswer, isBlocklyReady]);
|
|
3234
|
-
|
|
3396
|
+
React9.useEffect(() => {
|
|
3235
3397
|
if (isBlocklyReady) {
|
|
3236
3398
|
initializeWorkspace();
|
|
3237
3399
|
}
|
|
3238
3400
|
const handleResize = () => {
|
|
3239
3401
|
const LocalBlocklyResize = window.Blockly;
|
|
3240
|
-
if (workspaceRef.current &&
|
|
3402
|
+
if (workspaceRef.current && LocalBlocklyResize?.svgResize) {
|
|
3241
3403
|
LocalBlocklyResize.svgResize(workspaceRef.current);
|
|
3242
3404
|
}
|
|
3243
3405
|
};
|
|
@@ -3258,75 +3420,106 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
|
|
|
3258
3420
|
}, [isBlocklyReady, initializeWorkspace]);
|
|
3259
3421
|
const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
|
|
3260
3422
|
if (isLoadingScripts) {
|
|
3261
|
-
return /* @__PURE__ */
|
|
3423
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
3424
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto mb-2" }),
|
|
3425
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Loading Scratch Assets..." })
|
|
3426
|
+
] }) });
|
|
3262
3427
|
}
|
|
3263
3428
|
if (componentError) {
|
|
3264
|
-
return /* @__PURE__ */
|
|
3429
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { height: workspaceHeight, width: "100%", color: "hsl(var(--destructive))", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--destructive))", borderRadius: "0.375rem", padding: "1rem", backgroundColor: "hsl(var(--card))" }, children: [
|
|
3430
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-lg mb-2", children: "Failed to load Scratch Workspace." }),
|
|
3431
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 text-center", children: componentError }),
|
|
3432
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-4 text-center", children: [
|
|
3433
|
+
"Please ensure all Scratch/Blockly JavaScript files are correctly copied to your",
|
|
3434
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "public/static/scratch-blocks/js" }),
|
|
3435
|
+
" directory. Check browser console for more details.",
|
|
3436
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
3437
|
+
/* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
|
|
3438
|
+
"CRITICAL: Ensure you have copied the CSS files from ",
|
|
3439
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "node_modules/scratch-blocks/css/" }),
|
|
3440
|
+
" (e.g., ",
|
|
3441
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "vertical.css" }),
|
|
3442
|
+
") to ",
|
|
3443
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "public/static/scratch-blocks/css/" }),
|
|
3444
|
+
" and linked it in your main layout. Without CSS, blocks will not render correctly."
|
|
3445
|
+
] })
|
|
3446
|
+
] }),
|
|
3447
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: attemptLoadScripts, variant: "outline", children: "Try Reloading Scripts" })
|
|
3448
|
+
] });
|
|
3265
3449
|
}
|
|
3266
3450
|
if (!isBlocklyReady && !isLoadingScripts) {
|
|
3267
|
-
return /* @__PURE__ */
|
|
3451
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" }, children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Scratch environment did not initialize (Blockly not ready). Check console for script loading errors." }) });
|
|
3268
3452
|
}
|
|
3269
|
-
return /* @__PURE__ */
|
|
3270
|
-
"
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3453
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3454
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3455
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: question.prompt }),
|
|
3456
|
+
question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3457
|
+
"Points: ",
|
|
3458
|
+
question.points
|
|
3459
|
+
] })
|
|
3460
|
+
] }),
|
|
3461
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
3462
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3463
|
+
"div",
|
|
3464
|
+
{
|
|
3465
|
+
ref: blocklyDivRef,
|
|
3466
|
+
style: {
|
|
3467
|
+
height: workspaceHeight,
|
|
3468
|
+
width: "100%",
|
|
3469
|
+
borderRadius: "0.375rem",
|
|
3470
|
+
backgroundColor: "hsl(var(--card))",
|
|
3471
|
+
position: "relative",
|
|
3472
|
+
userSelect: "none",
|
|
3473
|
+
overflow: "hidden",
|
|
3474
|
+
display: isLoadingScripts || componentError || !isBlocklyReady ? "none" : "block"
|
|
3475
|
+
},
|
|
3476
|
+
"aria-label": `Scratch programming workspace for question: ${question.prompt}`
|
|
3477
|
+
}
|
|
3478
|
+
),
|
|
3479
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3480
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3481
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-accent-foreground/80", children: question.explanation })
|
|
3482
|
+
] })
|
|
3483
|
+
] })
|
|
3484
|
+
] });
|
|
3286
3485
|
});
|
|
3287
3486
|
ScratchProgrammingQuestionUI.displayName = "ScratchProgrammingQuestionUI";
|
|
3288
3487
|
var Tabs = TabsPrimitive__namespace.Root;
|
|
3289
|
-
var TabsList =
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
className
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
);
|
|
3301
|
-
});
|
|
3488
|
+
var TabsList = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3489
|
+
TabsPrimitive__namespace.List,
|
|
3490
|
+
{
|
|
3491
|
+
ref,
|
|
3492
|
+
className: cn(
|
|
3493
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
3494
|
+
className
|
|
3495
|
+
),
|
|
3496
|
+
...props
|
|
3497
|
+
}
|
|
3498
|
+
));
|
|
3302
3499
|
TabsList.displayName = TabsPrimitive__namespace.List.displayName;
|
|
3303
|
-
var TabsTrigger =
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
className
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
);
|
|
3315
|
-
});
|
|
3500
|
+
var TabsTrigger = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3501
|
+
TabsPrimitive__namespace.Trigger,
|
|
3502
|
+
{
|
|
3503
|
+
ref,
|
|
3504
|
+
className: cn(
|
|
3505
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
3506
|
+
className
|
|
3507
|
+
),
|
|
3508
|
+
...props
|
|
3509
|
+
}
|
|
3510
|
+
));
|
|
3316
3511
|
TabsTrigger.displayName = TabsPrimitive__namespace.Trigger.displayName;
|
|
3317
|
-
var TabsContent =
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
className
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
);
|
|
3329
|
-
});
|
|
3512
|
+
var TabsContent = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3513
|
+
TabsPrimitive__namespace.Content,
|
|
3514
|
+
{
|
|
3515
|
+
ref,
|
|
3516
|
+
className: cn(
|
|
3517
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
3518
|
+
className
|
|
3519
|
+
),
|
|
3520
|
+
...props
|
|
3521
|
+
}
|
|
3522
|
+
));
|
|
3330
3523
|
TabsContent.displayName = TabsPrimitive__namespace.Content.displayName;
|
|
3331
3524
|
var TOAST_LIMIT = 1;
|
|
3332
3525
|
var TOAST_REMOVE_DELAY = 1e6;
|
|
@@ -3352,15 +3545,17 @@ var addToRemoveQueue = (toastId) => {
|
|
|
3352
3545
|
var reducer = (state, action) => {
|
|
3353
3546
|
switch (action.type) {
|
|
3354
3547
|
case "ADD_TOAST":
|
|
3355
|
-
return
|
|
3548
|
+
return {
|
|
3549
|
+
...state,
|
|
3356
3550
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
|
3357
|
-
}
|
|
3551
|
+
};
|
|
3358
3552
|
case "UPDATE_TOAST":
|
|
3359
|
-
return
|
|
3553
|
+
return {
|
|
3554
|
+
...state,
|
|
3360
3555
|
toasts: state.toasts.map(
|
|
3361
|
-
(t) => t.id === action.toast.id ?
|
|
3556
|
+
(t) => t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
3362
3557
|
)
|
|
3363
|
-
}
|
|
3558
|
+
};
|
|
3364
3559
|
case "DISMISS_TOAST": {
|
|
3365
3560
|
const { toastId } = action;
|
|
3366
3561
|
if (toastId) {
|
|
@@ -3370,23 +3565,27 @@ var reducer = (state, action) => {
|
|
|
3370
3565
|
addToRemoveQueue(toast2.id);
|
|
3371
3566
|
});
|
|
3372
3567
|
}
|
|
3373
|
-
return
|
|
3568
|
+
return {
|
|
3569
|
+
...state,
|
|
3374
3570
|
toasts: state.toasts.map(
|
|
3375
|
-
(t) => t.id === toastId || toastId === void 0 ?
|
|
3571
|
+
(t) => t.id === toastId || toastId === void 0 ? {
|
|
3572
|
+
...t,
|
|
3376
3573
|
open: false
|
|
3377
|
-
}
|
|
3574
|
+
} : t
|
|
3378
3575
|
)
|
|
3379
|
-
}
|
|
3576
|
+
};
|
|
3380
3577
|
}
|
|
3381
3578
|
case "REMOVE_TOAST":
|
|
3382
3579
|
if (action.toastId === void 0) {
|
|
3383
|
-
return
|
|
3580
|
+
return {
|
|
3581
|
+
...state,
|
|
3384
3582
|
toasts: []
|
|
3385
|
-
}
|
|
3583
|
+
};
|
|
3386
3584
|
}
|
|
3387
|
-
return
|
|
3585
|
+
return {
|
|
3586
|
+
...state,
|
|
3388
3587
|
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
|
3389
|
-
}
|
|
3588
|
+
};
|
|
3390
3589
|
}
|
|
3391
3590
|
};
|
|
3392
3591
|
var listeners = [];
|
|
@@ -3397,23 +3596,23 @@ function dispatch(action) {
|
|
|
3397
3596
|
listener(memoryState);
|
|
3398
3597
|
});
|
|
3399
3598
|
}
|
|
3400
|
-
function toast(
|
|
3401
|
-
var props = __objRest(_a, []);
|
|
3599
|
+
function toast({ ...props }) {
|
|
3402
3600
|
const id = genId();
|
|
3403
3601
|
const update = (props2) => dispatch({
|
|
3404
3602
|
type: "UPDATE_TOAST",
|
|
3405
|
-
toast:
|
|
3603
|
+
toast: { ...props2, id }
|
|
3406
3604
|
});
|
|
3407
3605
|
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
3408
3606
|
dispatch({
|
|
3409
3607
|
type: "ADD_TOAST",
|
|
3410
|
-
toast:
|
|
3608
|
+
toast: {
|
|
3609
|
+
...props,
|
|
3411
3610
|
id,
|
|
3412
3611
|
open: true,
|
|
3413
3612
|
onOpenChange: (open) => {
|
|
3414
3613
|
if (!open) dismiss();
|
|
3415
3614
|
}
|
|
3416
|
-
}
|
|
3615
|
+
}
|
|
3417
3616
|
});
|
|
3418
3617
|
return {
|
|
3419
3618
|
id,
|
|
@@ -3422,8 +3621,8 @@ function toast(_a) {
|
|
|
3422
3621
|
};
|
|
3423
3622
|
}
|
|
3424
3623
|
function useToast() {
|
|
3425
|
-
const [state, setState] =
|
|
3426
|
-
|
|
3624
|
+
const [state, setState] = React9__namespace.useState(memoryState);
|
|
3625
|
+
React9__namespace.useEffect(() => {
|
|
3427
3626
|
listeners.push(setState);
|
|
3428
3627
|
return () => {
|
|
3429
3628
|
const index = listeners.indexOf(setState);
|
|
@@ -3432,13 +3631,12 @@ function useToast() {
|
|
|
3432
3631
|
}
|
|
3433
3632
|
};
|
|
3434
3633
|
}, [state]);
|
|
3435
|
-
return
|
|
3634
|
+
return {
|
|
3635
|
+
...state,
|
|
3436
3636
|
toast,
|
|
3437
3637
|
dismiss: (toastId) => dispatch({ type: "DISMISS_TOAST", toastId })
|
|
3438
|
-
}
|
|
3638
|
+
};
|
|
3439
3639
|
}
|
|
3440
|
-
|
|
3441
|
-
// src/react-ui/components/ui/CodingQuestionUI.tsx
|
|
3442
3640
|
var languageMap = {
|
|
3443
3641
|
cpp: langCpp.cpp(),
|
|
3444
3642
|
javascript: langJavascript.javascript({ typescript: true }),
|
|
@@ -3455,15 +3653,15 @@ var CodingQuestionUI = ({
|
|
|
3455
3653
|
userAnswer,
|
|
3456
3654
|
showCorrectAnswer = false
|
|
3457
3655
|
}) => {
|
|
3458
|
-
const [code, setCode] =
|
|
3459
|
-
const [isRunningTests, setIsRunningTests] =
|
|
3460
|
-
const [testResults, setTestResults] =
|
|
3656
|
+
const [code, setCode] = React9.useState("");
|
|
3657
|
+
const [isRunningTests, setIsRunningTests] = React9.useState(false);
|
|
3658
|
+
const [testResults, setTestResults] = React9.useState([]);
|
|
3461
3659
|
const { toast: toast2 } = useToast();
|
|
3462
|
-
|
|
3660
|
+
React9.useEffect(() => {
|
|
3463
3661
|
const initialCode = typeof userAnswer === "string" ? userAnswer : question.functionSignature || "";
|
|
3464
3662
|
setCode(initialCode);
|
|
3465
3663
|
}, [question.id, userAnswer, question.functionSignature]);
|
|
3466
|
-
const handleCodeChange =
|
|
3664
|
+
const handleCodeChange = React9.useCallback((value) => {
|
|
3467
3665
|
setCode(value);
|
|
3468
3666
|
onAnswerChange(value);
|
|
3469
3667
|
}, [onAnswerChange]);
|
|
@@ -3485,28 +3683,67 @@ var CodingQuestionUI = ({
|
|
|
3485
3683
|
}
|
|
3486
3684
|
};
|
|
3487
3685
|
const langExtension = languageMap[question.codingLanguage];
|
|
3488
|
-
return /* @__PURE__ */
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3686
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3687
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3688
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.prompt }) }),
|
|
3689
|
+
question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3690
|
+
"Points: ",
|
|
3691
|
+
question.points
|
|
3692
|
+
] })
|
|
3693
|
+
] }),
|
|
3694
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
3695
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3696
|
+
CodeMirror__default.default,
|
|
3697
|
+
{
|
|
3698
|
+
value: code,
|
|
3699
|
+
height: "300px",
|
|
3700
|
+
extensions: [langExtension],
|
|
3701
|
+
onChange: handleCodeChange,
|
|
3702
|
+
readOnly: showCorrectAnswer,
|
|
3703
|
+
theme: "dark"
|
|
3704
|
+
}
|
|
3705
|
+
) }),
|
|
3706
|
+
!showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: handleRunPublicTests, disabled: isRunningTests, children: [
|
|
3707
|
+
isRunningTests ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "mr-2 h-4 w-4" }),
|
|
3708
|
+
isRunningTests ? "Running..." : "Run Public Tests"
|
|
3709
|
+
] }),
|
|
3710
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tabs, { defaultValue: "tests", className: "w-full", children: [
|
|
3711
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabsList, { children: [
|
|
3712
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: "tests", children: "Test Cases" }),
|
|
3713
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: "solution", children: "Solution" })
|
|
3714
|
+
] }),
|
|
3715
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "tests", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 p-2 border rounded-md min-h-[100px]", children: testResults.length > 0 ? testResults.map((result, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm", children: [
|
|
3716
|
+
result.passed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 mr-2" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-destructive mr-2" }),
|
|
3717
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3718
|
+
"Test Case #",
|
|
3719
|
+
index + 1,
|
|
3720
|
+
": ",
|
|
3721
|
+
result.passed ? "Passed" : "Failed"
|
|
3722
|
+
] }),
|
|
3723
|
+
!result.passed && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
|
|
3724
|
+
"- ",
|
|
3725
|
+
result.reasoning
|
|
3726
|
+
] })
|
|
3727
|
+
] }, result.testCaseId)) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: showCorrectAnswer ? "Final results are shown in the quiz summary." : "Click 'Run Public Tests' to see results." }) }) }),
|
|
3728
|
+
showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "solution", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3729
|
+
CodeMirror__default.default,
|
|
3730
|
+
{
|
|
3731
|
+
value: question.solutionCode,
|
|
3732
|
+
height: "300px",
|
|
3733
|
+
extensions: [langExtension],
|
|
3734
|
+
readOnly: true,
|
|
3735
|
+
theme: "dark"
|
|
3736
|
+
}
|
|
3737
|
+
) }) })
|
|
3738
|
+
] }),
|
|
3739
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3740
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3741
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
|
|
3742
|
+
] })
|
|
3743
|
+
] })
|
|
3744
|
+
] });
|
|
3508
3745
|
};
|
|
3509
|
-
var QuestionRenderer =
|
|
3746
|
+
var QuestionRenderer = React9__namespace.default.forwardRef(({
|
|
3510
3747
|
question,
|
|
3511
3748
|
onAnswerChange,
|
|
3512
3749
|
userAnswer,
|
|
@@ -3521,131 +3758,125 @@ var QuestionRenderer = React28__namespace.default.forwardRef(({
|
|
|
3521
3758
|
};
|
|
3522
3759
|
switch (question.questionType) {
|
|
3523
3760
|
case "multiple_choice":
|
|
3524
|
-
return /* @__PURE__ */
|
|
3761
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MultipleChoiceQuestionUI, { ...commonProps, question });
|
|
3525
3762
|
case "true_false":
|
|
3526
|
-
return /* @__PURE__ */
|
|
3763
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TrueFalseQuestionUI, { ...commonProps, question });
|
|
3527
3764
|
case "multiple_response":
|
|
3528
|
-
return /* @__PURE__ */
|
|
3765
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MultipleResponseQuestionUI, { ...commonProps, question });
|
|
3529
3766
|
case "short_answer":
|
|
3530
|
-
return /* @__PURE__ */
|
|
3767
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ShortAnswerQuestionUI, { ...commonProps, question });
|
|
3531
3768
|
case "numeric":
|
|
3532
|
-
return /* @__PURE__ */
|
|
3769
|
+
return /* @__PURE__ */ jsxRuntime.jsx(NumericQuestionUI, { ...commonProps, question });
|
|
3533
3770
|
case "fill_in_the_blanks":
|
|
3534
|
-
return /* @__PURE__ */
|
|
3771
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FillInTheBlanksQuestionUI, { ...commonProps, question });
|
|
3535
3772
|
case "sequence":
|
|
3536
|
-
return /* @__PURE__ */
|
|
3773
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SequenceQuestionUI, { ...commonProps, question });
|
|
3537
3774
|
case "matching":
|
|
3538
|
-
return /* @__PURE__ */
|
|
3775
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MatchingQuestionUI, { ...commonProps, question });
|
|
3539
3776
|
case "drag_and_drop":
|
|
3540
|
-
return /* @__PURE__ */
|
|
3777
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DragAndDropQuestionUI, { ...commonProps, question });
|
|
3541
3778
|
case "hotspot":
|
|
3542
|
-
return /* @__PURE__ */
|
|
3779
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HotspotQuestionUI, { ...commonProps, question });
|
|
3543
3780
|
case "blockly_programming":
|
|
3544
|
-
return /* @__PURE__ */
|
|
3781
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BlocklyProgrammingQuestionUI, { ...commonProps, question, ref });
|
|
3545
3782
|
case "scratch_programming":
|
|
3546
|
-
return /* @__PURE__ */
|
|
3783
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ScratchProgrammingQuestionUI, { ...commonProps, question, ref });
|
|
3547
3784
|
case "coding":
|
|
3548
|
-
return /* @__PURE__ */
|
|
3785
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CodingQuestionUI, { ...commonProps, question });
|
|
3549
3786
|
default:
|
|
3550
|
-
return /* @__PURE__ */
|
|
3787
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border border-destructive bg-destructive/10 rounded-md", children: [
|
|
3788
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-destructive", children: t("unsupportedQuestionType") }),
|
|
3789
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("unsupportedQuestionTypeDescription") }),
|
|
3790
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 p-2 bg-muted rounded text-xs font-code overflow-x-auto", children: JSON.stringify(question, null, 2) })
|
|
3791
|
+
] });
|
|
3551
3792
|
}
|
|
3552
3793
|
});
|
|
3553
3794
|
QuestionRenderer.displayName = "QuestionRenderer";
|
|
3554
|
-
var Progress =
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
className
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
}, props),
|
|
3565
|
-
/* @__PURE__ */ React28__namespace.createElement(
|
|
3795
|
+
var Progress = React9__namespace.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3796
|
+
ProgressPrimitive__namespace.Root,
|
|
3797
|
+
{
|
|
3798
|
+
ref,
|
|
3799
|
+
className: cn(
|
|
3800
|
+
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
|
3801
|
+
className
|
|
3802
|
+
),
|
|
3803
|
+
...props,
|
|
3804
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3566
3805
|
ProgressPrimitive__namespace.Indicator,
|
|
3567
3806
|
{
|
|
3568
3807
|
className: "h-full w-full flex-1 bg-primary transition-all",
|
|
3569
3808
|
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
3570
3809
|
}
|
|
3571
3810
|
)
|
|
3572
|
-
|
|
3573
|
-
|
|
3811
|
+
}
|
|
3812
|
+
));
|
|
3574
3813
|
Progress.displayName = ProgressPrimitive__namespace.Root.displayName;
|
|
3575
3814
|
var Accordion = AccordionPrimitive__namespace.Root;
|
|
3576
|
-
var AccordionItem =
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
);
|
|
3585
|
-
});
|
|
3815
|
+
var AccordionItem = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3816
|
+
AccordionPrimitive__namespace.Item,
|
|
3817
|
+
{
|
|
3818
|
+
ref,
|
|
3819
|
+
className: cn("border-b", className),
|
|
3820
|
+
...props
|
|
3821
|
+
}
|
|
3822
|
+
));
|
|
3586
3823
|
AccordionItem.displayName = "AccordionItem";
|
|
3587
|
-
var AccordionTrigger =
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
className
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
});
|
|
3824
|
+
var AccordionTrigger = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { className: "flex", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3825
|
+
AccordionPrimitive__namespace.Trigger,
|
|
3826
|
+
{
|
|
3827
|
+
ref,
|
|
3828
|
+
className: cn(
|
|
3829
|
+
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
3830
|
+
className
|
|
3831
|
+
),
|
|
3832
|
+
...props,
|
|
3833
|
+
children: [
|
|
3834
|
+
children,
|
|
3835
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200" })
|
|
3836
|
+
]
|
|
3837
|
+
}
|
|
3838
|
+
) }));
|
|
3602
3839
|
AccordionTrigger.displayName = AccordionPrimitive__namespace.Trigger.displayName;
|
|
3603
|
-
var AccordionContent =
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
);
|
|
3613
|
-
});
|
|
3840
|
+
var AccordionContent = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3841
|
+
AccordionPrimitive__namespace.Content,
|
|
3842
|
+
{
|
|
3843
|
+
ref,
|
|
3844
|
+
className: "overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
|
3845
|
+
...props,
|
|
3846
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pb-4 pt-0", className), children })
|
|
3847
|
+
}
|
|
3848
|
+
));
|
|
3614
3849
|
AccordionContent.displayName = AccordionPrimitive__namespace.Content.displayName;
|
|
3615
|
-
var ScrollArea =
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3850
|
+
var ScrollArea = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3851
|
+
ScrollAreaPrimitive__namespace.Root,
|
|
3852
|
+
{
|
|
3853
|
+
ref,
|
|
3854
|
+
className: cn("relative overflow-hidden", className),
|
|
3855
|
+
...props,
|
|
3856
|
+
children: [
|
|
3857
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
|
|
3858
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollBar, {}),
|
|
3859
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Corner, {})
|
|
3860
|
+
]
|
|
3861
|
+
}
|
|
3862
|
+
));
|
|
3628
3863
|
ScrollArea.displayName = ScrollAreaPrimitive__namespace.Root.displayName;
|
|
3629
|
-
var ScrollBar =
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
);
|
|
3645
|
-
});
|
|
3864
|
+
var ScrollBar = React9__namespace.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3865
|
+
ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
|
|
3866
|
+
{
|
|
3867
|
+
ref,
|
|
3868
|
+
orientation,
|
|
3869
|
+
className: cn(
|
|
3870
|
+
"flex touch-none select-none transition-colors",
|
|
3871
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
3872
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
3873
|
+
className
|
|
3874
|
+
),
|
|
3875
|
+
...props,
|
|
3876
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
|
|
3877
|
+
}
|
|
3878
|
+
));
|
|
3646
3879
|
ScrollBar.displayName = ScrollAreaPrimitive__namespace.ScrollAreaScrollbar.displayName;
|
|
3647
|
-
|
|
3648
|
-
// src/react-ui/components/ui/QuizResult.tsx
|
|
3649
3880
|
var QuizResult = ({
|
|
3650
3881
|
result,
|
|
3651
3882
|
quizTitle,
|
|
@@ -3654,7 +3885,6 @@ var QuizResult = ({
|
|
|
3654
3885
|
showReviewButton = false,
|
|
3655
3886
|
isReviewLoading = false
|
|
3656
3887
|
}) => {
|
|
3657
|
-
var _a, _b, _c, _d;
|
|
3658
3888
|
const { t } = reactI18next.useTranslation();
|
|
3659
3889
|
const getAnswerDisplay = (answer) => {
|
|
3660
3890
|
if (answer === null || answer === void 0) return t("practiceFlow.results.notAnswered");
|
|
@@ -3673,39 +3903,165 @@ var QuizResult = ({
|
|
|
3673
3903
|
}
|
|
3674
3904
|
return String(answer);
|
|
3675
3905
|
};
|
|
3676
|
-
return /* @__PURE__ */
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
{
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3906
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
|
|
3907
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
|
|
3908
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-3xl font-headline text-center", children: t("practiceFlow.results.title", { quizTitle }) }),
|
|
3909
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardDescription, { className: "text-center text-lg", children: t("practiceFlow.results.description") })
|
|
3910
|
+
] }),
|
|
3911
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-6", children: [
|
|
3912
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "bg-secondary/50", children: [
|
|
3913
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "text-xl flex items-center", children: [
|
|
3914
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.BarChart2, { className: "mr-2 h-5 w-5 text-primary" }),
|
|
3915
|
+
t("practiceFlow.results.overallScore")
|
|
3916
|
+
] }) }),
|
|
3917
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center", children: [
|
|
3918
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3919
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-3xl font-bold text-primary", children: [
|
|
3920
|
+
result.score,
|
|
3921
|
+
" / ",
|
|
3922
|
+
result.maxScore
|
|
3923
|
+
] }),
|
|
3924
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.points") })
|
|
3925
|
+
] }),
|
|
3926
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3927
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-3xl font-bold text-primary", children: [
|
|
3928
|
+
result.percentage.toFixed(2),
|
|
3929
|
+
"%"
|
|
3930
|
+
] }),
|
|
3931
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.percentage") })
|
|
3932
|
+
] }),
|
|
3933
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: result.passed !== void 0 && (result.passed ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-green-600", children: [
|
|
3934
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-10 w-10" }),
|
|
3935
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.passed") })
|
|
3936
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-destructive", children: [
|
|
3937
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-10 w-10" }),
|
|
3938
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.failed") })
|
|
3939
|
+
] })) })
|
|
3940
|
+
] })
|
|
3941
|
+
] }),
|
|
3942
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm", children: [
|
|
3943
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
|
|
3944
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-5 w-5 text-primary" }),
|
|
3945
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("practiceFlow.results.timeSpent") }),
|
|
3946
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
|
|
3947
|
+
result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A",
|
|
3948
|
+
" ",
|
|
3949
|
+
t("practiceFlow.results.timeUnit")
|
|
3950
|
+
] })
|
|
3951
|
+
] }),
|
|
3952
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
|
|
3953
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Percent, { className: "h-5 w-5 text-primary" }),
|
|
3954
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("practiceFlow.results.avgTimePerQuestion") }),
|
|
3955
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
|
|
3956
|
+
result.averageTimePerQuestionSeconds?.toFixed(1) ?? "N/A",
|
|
3957
|
+
" ",
|
|
3958
|
+
t("practiceFlow.results.timeUnit")
|
|
3959
|
+
] })
|
|
3960
|
+
] })
|
|
3961
|
+
] }),
|
|
3962
|
+
result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
|
|
3963
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-lg", children: "SCORM Sync Status" }) }),
|
|
3964
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { children: [
|
|
3965
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
3966
|
+
result.scormStatus === "error" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }),
|
|
3967
|
+
"Status: ",
|
|
3968
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold ml-1", children: result.scormStatus })
|
|
3969
|
+
] }),
|
|
3970
|
+
result.scormError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive mt-1", children: [
|
|
3971
|
+
"Details: ",
|
|
3972
|
+
result.scormError
|
|
3973
|
+
] })
|
|
3974
|
+
] })
|
|
3975
|
+
] }),
|
|
3976
|
+
result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
|
|
3977
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-lg", children: "Webhook Sync Status" }) }),
|
|
3978
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { children: [
|
|
3979
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
3980
|
+
result.webhookStatus === "error" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }),
|
|
3981
|
+
"Status: ",
|
|
3982
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold ml-1", children: result.webhookStatus })
|
|
3983
|
+
] }),
|
|
3984
|
+
result.webhookError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive mt-1", children: [
|
|
3985
|
+
"Details: ",
|
|
3986
|
+
result.webhookError
|
|
3987
|
+
] })
|
|
3988
|
+
] })
|
|
3989
|
+
] }),
|
|
3990
|
+
/* @__PURE__ */ jsxRuntime.jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(AccordionItem, { value: "question-breakdown", children: [
|
|
3991
|
+
/* @__PURE__ */ jsxRuntime.jsx(AccordionTrigger, { className: "text-lg font-semibold", children: t("practiceFlow.results.questionBreakdown") }),
|
|
3992
|
+
/* @__PURE__ */ jsxRuntime.jsx(AccordionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "h-[300px] pr-4", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-4", children: result.questionResults.map((qResult, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "p-4 border rounded-md bg-background", children: [
|
|
3993
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-2", children: [
|
|
3994
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold", children: t("common.questions", { count: index + 1 }) }),
|
|
3995
|
+
qResult.isCorrect ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-green-600 font-medium flex items-center", children: [
|
|
3996
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "mr-1 h-4 w-4" }),
|
|
3997
|
+
" ",
|
|
3998
|
+
t("practiceFlow.results.passed")
|
|
3999
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive font-medium flex items-center", children: [
|
|
4000
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "mr-1 h-4 w-4" }),
|
|
4001
|
+
" ",
|
|
4002
|
+
t("practiceFlow.results.failed")
|
|
4003
|
+
] })
|
|
4004
|
+
] }),
|
|
4005
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
|
|
4006
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.yourAnswer") }),
|
|
4007
|
+
" ",
|
|
4008
|
+
getAnswerDisplay(qResult.userAnswer)
|
|
4009
|
+
] }),
|
|
4010
|
+
!qResult.isCorrect && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
|
|
4011
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.correctAnswer") }),
|
|
4012
|
+
" ",
|
|
4013
|
+
getAnswerDisplay(qResult.correctAnswer)
|
|
4014
|
+
] }),
|
|
4015
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
4016
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.pointsEarned") }),
|
|
4017
|
+
" ",
|
|
4018
|
+
qResult.pointsEarned
|
|
4019
|
+
] }),
|
|
4020
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
4021
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.timeSpent") }),
|
|
4022
|
+
" ",
|
|
4023
|
+
qResult.timeSpentSeconds?.toFixed(0) ?? "N/A",
|
|
4024
|
+
t("practiceFlow.results.timeUnit")
|
|
4025
|
+
] })
|
|
4026
|
+
] }, qResult.questionId)) }) }) })
|
|
4027
|
+
] }) })
|
|
4028
|
+
] }),
|
|
4029
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2", children: [
|
|
4030
|
+
onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto", children: [
|
|
4031
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
|
|
4032
|
+
t("common.exit")
|
|
4033
|
+
] }),
|
|
4034
|
+
showReviewButton && onGenerateReview && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4035
|
+
Button,
|
|
4036
|
+
{
|
|
4037
|
+
onClick: onGenerateReview,
|
|
4038
|
+
disabled: isReviewLoading,
|
|
4039
|
+
className: "w-full sm:w-auto",
|
|
4040
|
+
children: [
|
|
4041
|
+
isReviewLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wand2, { className: "mr-2 h-4 w-4" }),
|
|
4042
|
+
isReviewLoading ? t("practiceFlow.results.generatingReview") : t("practiceFlow.results.generateReview")
|
|
4043
|
+
]
|
|
4044
|
+
}
|
|
4045
|
+
)
|
|
4046
|
+
] })
|
|
4047
|
+
] });
|
|
3689
4048
|
};
|
|
3690
|
-
|
|
3691
|
-
// src/react-ui/components/ui/QuizPlayer.tsx
|
|
3692
4049
|
var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
3693
|
-
|
|
3694
|
-
const [
|
|
3695
|
-
const [
|
|
3696
|
-
const [
|
|
3697
|
-
const [
|
|
3698
|
-
const [
|
|
3699
|
-
const [
|
|
3700
|
-
const [
|
|
3701
|
-
const [
|
|
3702
|
-
const [
|
|
3703
|
-
const [error, setError] = React28.useState(null);
|
|
4050
|
+
const [engine, setEngine] = React9.useState(null);
|
|
4051
|
+
const [currentQuestion, setCurrentQuestion] = React9.useState(null);
|
|
4052
|
+
const [currentQuestionNumber, setCurrentQuestionNumber] = React9.useState(0);
|
|
4053
|
+
const [totalQuestions, setTotalQuestions] = React9.useState(0);
|
|
4054
|
+
const [userAnswer, setUserAnswer] = React9.useState(null);
|
|
4055
|
+
const [quizFinished, setQuizFinished] = React9.useState(false);
|
|
4056
|
+
const [finalResult, setFinalResult] = React9.useState(null);
|
|
4057
|
+
const [timeLeft, setTimeLeft] = React9.useState(null);
|
|
4058
|
+
const [isLoading, setIsLoading] = React9.useState(true);
|
|
4059
|
+
const [error, setError] = React9.useState(null);
|
|
3704
4060
|
const { t } = reactI18next.useTranslation();
|
|
3705
|
-
const programmingQuestionRef =
|
|
3706
|
-
const engineRef =
|
|
3707
|
-
const isInitializedRef =
|
|
3708
|
-
const callbacks =
|
|
4061
|
+
const programmingQuestionRef = React9.useRef(null);
|
|
4062
|
+
const engineRef = React9.useRef(null);
|
|
4063
|
+
const isInitializedRef = React9.useRef(false);
|
|
4064
|
+
const callbacks = React9.useMemo(() => ({
|
|
3709
4065
|
onQuizStart: (initialData) => {
|
|
3710
4066
|
setCurrentQuestion(initialData.initialQuestion);
|
|
3711
4067
|
setCurrentQuestionNumber(initialData.currentQuestionNumber);
|
|
@@ -3714,11 +4070,10 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3714
4070
|
setIsLoading(false);
|
|
3715
4071
|
},
|
|
3716
4072
|
onQuestionChange: (question, qNum, total) => {
|
|
3717
|
-
var _a2;
|
|
3718
4073
|
setCurrentQuestion(question);
|
|
3719
4074
|
setCurrentQuestionNumber(qNum);
|
|
3720
4075
|
setTotalQuestions(total);
|
|
3721
|
-
const existingAnswer =
|
|
4076
|
+
const existingAnswer = engineRef.current?.getUserAnswer(question?.id || "");
|
|
3722
4077
|
setUserAnswer(existingAnswer !== void 0 ? existingAnswer : null);
|
|
3723
4078
|
},
|
|
3724
4079
|
onQuizFinish: (results) => {
|
|
@@ -3734,19 +4089,19 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3734
4089
|
setError("Time's up! Your quiz has been submitted automatically.");
|
|
3735
4090
|
}
|
|
3736
4091
|
}), [onQuizComplete]);
|
|
3737
|
-
const handleAnswerChange =
|
|
3738
|
-
if (
|
|
4092
|
+
const handleAnswerChange = React9.useCallback((answer) => {
|
|
4093
|
+
if (currentQuestion?.questionType !== "blockly_programming" && currentQuestion?.questionType !== "scratch_programming") {
|
|
3739
4094
|
setUserAnswer(answer);
|
|
3740
4095
|
}
|
|
3741
|
-
}, [currentQuestion
|
|
3742
|
-
const quizConfigKey =
|
|
4096
|
+
}, [currentQuestion?.questionType]);
|
|
4097
|
+
const quizConfigKey = React9.useMemo(() => {
|
|
3743
4098
|
return JSON.stringify({
|
|
3744
4099
|
id: quizConfig.id,
|
|
3745
4100
|
version: quizConfig.version,
|
|
3746
4101
|
title: quizConfig.title
|
|
3747
4102
|
});
|
|
3748
4103
|
}, [quizConfig.id, quizConfig.version, quizConfig.title]);
|
|
3749
|
-
|
|
4104
|
+
React9.useEffect(() => {
|
|
3750
4105
|
if (isInitializedRef.current) {
|
|
3751
4106
|
return;
|
|
3752
4107
|
}
|
|
@@ -3782,8 +4137,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3782
4137
|
isInitializedRef.current = false;
|
|
3783
4138
|
};
|
|
3784
4139
|
}, [quizConfigKey, callbacks, quizConfig]);
|
|
3785
|
-
const handleSubmitAnswer =
|
|
3786
|
-
var _a2;
|
|
4140
|
+
const handleSubmitAnswer = React9.useCallback(() => {
|
|
3787
4141
|
const currentEngine = engineRef.current;
|
|
3788
4142
|
if (!currentEngine || !currentQuestion) return;
|
|
3789
4143
|
let answerToSubmit = null;
|
|
@@ -3791,7 +4145,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3791
4145
|
if (programmingQuestionRef.current && typeof programmingQuestionRef.current.getWorkspaceXml === "function") {
|
|
3792
4146
|
answerToSubmit = programmingQuestionRef.current.getWorkspaceXml();
|
|
3793
4147
|
} else {
|
|
3794
|
-
answerToSubmit =
|
|
4148
|
+
answerToSubmit = currentEngine.getUserAnswer(currentQuestion.id) ?? null;
|
|
3795
4149
|
}
|
|
3796
4150
|
} else {
|
|
3797
4151
|
answerToSubmit = userAnswer;
|
|
@@ -3800,7 +4154,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3800
4154
|
currentEngine.submitAnswer(currentQuestion.id, answerToSubmit);
|
|
3801
4155
|
}
|
|
3802
4156
|
}, [currentQuestion, userAnswer]);
|
|
3803
|
-
const handleNext =
|
|
4157
|
+
const handleNext = React9.useCallback(() => {
|
|
3804
4158
|
const currentEngine = engineRef.current;
|
|
3805
4159
|
if (!currentEngine) return;
|
|
3806
4160
|
handleSubmitAnswer();
|
|
@@ -3810,77 +4164,128 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3810
4164
|
handleFinishQuiz();
|
|
3811
4165
|
}
|
|
3812
4166
|
}, [handleSubmitAnswer]);
|
|
3813
|
-
const handlePrevious =
|
|
4167
|
+
const handlePrevious = React9.useCallback(() => {
|
|
3814
4168
|
const currentEngine = engineRef.current;
|
|
3815
4169
|
if (!currentEngine || currentEngine.getCurrentQuestionNumber() <= 1) return;
|
|
3816
4170
|
handleSubmitAnswer();
|
|
3817
4171
|
currentEngine.previousQuestion();
|
|
3818
4172
|
}, [handleSubmitAnswer]);
|
|
3819
|
-
const handleFinishQuiz =
|
|
4173
|
+
const handleFinishQuiz = React9.useCallback(async () => {
|
|
3820
4174
|
const currentEngine = engineRef.current;
|
|
3821
4175
|
if (!currentEngine) return;
|
|
3822
4176
|
setIsLoading(true);
|
|
3823
4177
|
handleSubmitAnswer();
|
|
3824
4178
|
await currentEngine.calculateResults();
|
|
3825
4179
|
}, [handleSubmitAnswer]);
|
|
3826
|
-
const progressPercent =
|
|
4180
|
+
const progressPercent = React9.useMemo(() => {
|
|
3827
4181
|
if (totalQuestions === 0) return 0;
|
|
3828
4182
|
return quizFinished ? 100 : Math.max(0, Math.min(100, (currentQuestionNumber - 1) / totalQuestions * 100));
|
|
3829
4183
|
}, [currentQuestionNumber, totalQuestions, quizFinished]);
|
|
3830
|
-
const formatTime =
|
|
4184
|
+
const formatTime = React9.useCallback((seconds) => {
|
|
3831
4185
|
if (seconds === null) return "-:--";
|
|
3832
4186
|
const mins = Math.floor(seconds / 60);
|
|
3833
4187
|
const secs = seconds % 60;
|
|
3834
4188
|
return `${mins}:${secs < 10 ? "0" : ""}${secs}`;
|
|
3835
4189
|
}, []);
|
|
3836
4190
|
if (isLoading) {
|
|
3837
|
-
return /* @__PURE__ */
|
|
4191
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-64", children: [
|
|
4192
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-12 w-12 animate-spin text-primary" }),
|
|
4193
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-muted-foreground", children: t("common.loading") })
|
|
4194
|
+
] });
|
|
3838
4195
|
}
|
|
3839
4196
|
if (error) {
|
|
3840
|
-
return /* @__PURE__ */
|
|
4197
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
|
|
4198
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "text-destructive flex items-center", children: [
|
|
4199
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "mr-2 h-6 w-6" }),
|
|
4200
|
+
"Quiz Error"
|
|
4201
|
+
] }) }),
|
|
4202
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }) }),
|
|
4203
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
|
|
4204
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
|
|
4205
|
+
" ",
|
|
4206
|
+
t("common.exit")
|
|
4207
|
+
] }) })
|
|
4208
|
+
] });
|
|
3841
4209
|
}
|
|
3842
4210
|
if (quizFinished && finalResult) {
|
|
3843
|
-
return /* @__PURE__ */
|
|
4211
|
+
return /* @__PURE__ */ jsxRuntime.jsx(QuizResult, { result: finalResult, onExitQuiz, quizTitle: quizConfig.title });
|
|
3844
4212
|
}
|
|
3845
4213
|
if (!currentQuestion) {
|
|
3846
|
-
return /* @__PURE__ */
|
|
4214
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
|
|
4215
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
|
|
4216
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { children: "Quiz Ended" }),
|
|
4217
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: "No more questions, or quiz not loaded correctly." })
|
|
4218
|
+
] }),
|
|
4219
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
|
|
4220
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
|
|
4221
|
+
" ",
|
|
4222
|
+
t("common.exit")
|
|
4223
|
+
] }) })
|
|
4224
|
+
] });
|
|
3847
4225
|
}
|
|
3848
|
-
return /* @__PURE__ */
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
4226
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
|
|
4227
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
|
|
4228
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
4229
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-2xl font-headline", children: quizConfig.title }),
|
|
4230
|
+
timeLeft !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-muted-foreground", children: [
|
|
4231
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "mr-1 h-4 w-4" }),
|
|
4232
|
+
t("practiceFlow.player.timeLeft", { time: formatTime(timeLeft) })
|
|
4233
|
+
] })
|
|
4234
|
+
] }),
|
|
4235
|
+
quizConfig.description && /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: quizConfig.description }),
|
|
4236
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2", children: [
|
|
4237
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4238
|
+
Progress,
|
|
4239
|
+
{
|
|
4240
|
+
value: progressPercent,
|
|
4241
|
+
"aria-label": `Quiz progress: ${currentQuestionNumber} of ${totalQuestions} questions`,
|
|
4242
|
+
className: "w-full"
|
|
4243
|
+
}
|
|
4244
|
+
),
|
|
4245
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 text-right", children: t("practiceFlow.player.questionProgress", { current: currentQuestionNumber, total: totalQuestions }) })
|
|
4246
|
+
] })
|
|
4247
|
+
] }),
|
|
4248
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "min-h-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4249
|
+
QuestionRenderer,
|
|
4250
|
+
{
|
|
4251
|
+
question: currentQuestion,
|
|
4252
|
+
onAnswerChange: handleAnswerChange,
|
|
4253
|
+
userAnswer,
|
|
4254
|
+
showCorrectAnswer: quizConfig.settings?.showCorrectAnswers === "immediately",
|
|
4255
|
+
ref: currentQuestion.questionType === "blockly_programming" || currentQuestion.questionType === "scratch_programming" ? programmingQuestionRef : null
|
|
4256
|
+
},
|
|
4257
|
+
currentQuestion.id
|
|
4258
|
+
) }),
|
|
4259
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardFooter, { className: "flex justify-between items-center", children: [
|
|
4260
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4261
|
+
Button,
|
|
4262
|
+
{
|
|
4263
|
+
variant: "outline",
|
|
4264
|
+
onClick: handlePrevious,
|
|
4265
|
+
disabled: currentQuestionNumber <= 1,
|
|
4266
|
+
children: [
|
|
4267
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "mr-2 h-4 w-4" }),
|
|
4268
|
+
" ",
|
|
4269
|
+
t("common.previous")
|
|
4270
|
+
]
|
|
4271
|
+
}
|
|
4272
|
+
),
|
|
4273
|
+
onExitQuiz && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4274
|
+
Button,
|
|
4275
|
+
{
|
|
4276
|
+
variant: "ghost",
|
|
4277
|
+
onClick: onExitQuiz,
|
|
4278
|
+
className: "text-muted-foreground hover:text-destructive",
|
|
4279
|
+
children: t("common.exit")
|
|
4280
|
+
}
|
|
4281
|
+
),
|
|
4282
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: handleNext, children: [
|
|
4283
|
+
currentQuestionNumber === totalQuestions ? t("practiceFlow.player.finishQuiz") : t("common.next"),
|
|
4284
|
+
currentQuestionNumber !== totalQuestions && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "ml-2 h-4 w-4" }),
|
|
4285
|
+
currentQuestionNumber === totalQuestions && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "ml-2 h-4 w-4" })
|
|
4286
|
+
] })
|
|
4287
|
+
] })
|
|
4288
|
+
] });
|
|
3884
4289
|
};
|
|
3885
4290
|
|
|
3886
4291
|
// src/player.ts
|
|
@@ -3892,7 +4297,7 @@ function mountQuizPlayer(targetElementId, quizConfig) {
|
|
|
3892
4297
|
return;
|
|
3893
4298
|
}
|
|
3894
4299
|
const AppContainer = () => {
|
|
3895
|
-
const [quizResult, setQuizResult] =
|
|
4300
|
+
const [quizResult, setQuizResult] = React9.useState(null);
|
|
3896
4301
|
const handleQuizComplete = (result) => {
|
|
3897
4302
|
console.log("Quiz Complete (captured inside React AppContainer):", result);
|
|
3898
4303
|
setQuizResult(result);
|
|
@@ -3909,20 +4314,20 @@ function mountQuizPlayer(targetElementId, quizConfig) {
|
|
|
3909
4314
|
}
|
|
3910
4315
|
};
|
|
3911
4316
|
if (quizResult) {
|
|
3912
|
-
return
|
|
4317
|
+
return React9__namespace.default.createElement(QuizResult, {
|
|
3913
4318
|
result: quizResult,
|
|
3914
4319
|
quizTitle: quizConfig.title,
|
|
3915
4320
|
onExitQuiz: handleExit
|
|
3916
4321
|
});
|
|
3917
4322
|
}
|
|
3918
|
-
return
|
|
4323
|
+
return React9__namespace.default.createElement(QuizPlayer, {
|
|
3919
4324
|
quizConfig,
|
|
3920
4325
|
onQuizComplete: handleQuizComplete,
|
|
3921
4326
|
onExitQuiz: handleExit
|
|
3922
4327
|
});
|
|
3923
4328
|
};
|
|
3924
4329
|
const root = ReactDOM__default.default.createRoot(targetElement);
|
|
3925
|
-
root.render(
|
|
4330
|
+
root.render(React9__namespace.default.createElement(React9__namespace.default.StrictMode, null, React9__namespace.default.createElement(AppContainer)));
|
|
3926
4331
|
}
|
|
3927
4332
|
|
|
3928
4333
|
exports.mountQuizPlayer = mountQuizPlayer;
|