@thanh01.pmt/interactive-quiz-kit 1.0.24 → 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 +5213 -3105
- package/dist/authoring.d.cts +12 -0
- package/dist/authoring.d.ts +12 -0
- package/dist/authoring.js +4978 -2870
- package/dist/index.cjs +126 -182
- package/dist/index.d.cts +444 -0
- package/dist/index.d.ts +444 -0
- package/dist/index.js +126 -182
- 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 +7473 -4083
- package/dist/react-ui.d.cts +207 -0
- package/dist/react-ui.d.ts +207 -0
- package/dist/react-ui.js +7054 -3664
- package/dist/toaster-CtnxWhfE.d.ts +133 -0
- package/dist/toaster-DUq851l_.d.cts +133 -0
- package/package.json +13 -11
package/dist/player.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
1
|
+
import * as React9 from 'react';
|
|
2
|
+
import React9__default, { useRef, useState, useImperativeHandle, useCallback, useEffect, forwardRef, useMemo } from 'react';
|
|
3
3
|
import ReactDOM from 'react-dom/client';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { z } from 'zod';
|
|
@@ -9,6 +9,7 @@ import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
|
9
9
|
import { Circle, Check, ChevronDown, ChevronUp, Loader2, Play, CheckCircle, XCircle, RotateCcw, BarChart2, Clock, Percent, AlertTriangle, LogOut, Wand2, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { twMerge } from 'tailwind-merge';
|
|
12
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
12
13
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
13
14
|
import { cva } from 'class-variance-authority';
|
|
14
15
|
import ReactMarkdown from 'react-markdown';
|
|
@@ -28,37 +29,7 @@ import * as ProgressPrimitive from '@radix-ui/react-progress';
|
|
|
28
29
|
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
29
30
|
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
var __defProps = Object.defineProperties;
|
|
33
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
34
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
35
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
36
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
37
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
38
|
-
var __spreadValues = (a, b) => {
|
|
39
|
-
for (var prop in b || (b = {}))
|
|
40
|
-
if (__hasOwnProp.call(b, prop))
|
|
41
|
-
__defNormalProp(a, prop, b[prop]);
|
|
42
|
-
if (__getOwnPropSymbols)
|
|
43
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
44
|
-
if (__propIsEnum.call(b, prop))
|
|
45
|
-
__defNormalProp(a, prop, b[prop]);
|
|
46
|
-
}
|
|
47
|
-
return a;
|
|
48
|
-
};
|
|
49
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
50
|
-
var __objRest = (source, exclude) => {
|
|
51
|
-
var target = {};
|
|
52
|
-
for (var prop in source)
|
|
53
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
54
|
-
target[prop] = source[prop];
|
|
55
|
-
if (source != null && __getOwnPropSymbols)
|
|
56
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
57
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
58
|
-
target[prop] = source[prop];
|
|
59
|
-
}
|
|
60
|
-
return target;
|
|
61
|
-
};
|
|
32
|
+
// src/player.ts
|
|
62
33
|
|
|
63
34
|
// src/services/SCORMService.ts
|
|
64
35
|
var SCORM_TRUE = "true";
|
|
@@ -79,11 +50,12 @@ var SCORMService = class {
|
|
|
79
50
|
this.isInitialized = false;
|
|
80
51
|
this.isTerminated = false;
|
|
81
52
|
this.studentName = null;
|
|
82
|
-
this.settings =
|
|
53
|
+
this.settings = {
|
|
83
54
|
setCompletionOnFinish: true,
|
|
84
55
|
setSuccessOnPass: true,
|
|
85
|
-
autoCommit: true
|
|
86
|
-
|
|
56
|
+
autoCommit: true,
|
|
57
|
+
...settings
|
|
58
|
+
};
|
|
87
59
|
if (typeof window !== "undefined") {
|
|
88
60
|
this._findAPI();
|
|
89
61
|
}
|
|
@@ -189,14 +161,13 @@ var SCORMService = class {
|
|
|
189
161
|
}
|
|
190
162
|
}
|
|
191
163
|
getValue(element) {
|
|
192
|
-
var _a;
|
|
193
164
|
if (!this.hasAPI() || !this.isInitialized) return null;
|
|
194
165
|
const value = this.scormVersionFound === "2004" ? this.scormAPI.GetValue(element) : this.scormAPI.LMSGetValue(element);
|
|
195
166
|
const error = this.getLastError();
|
|
196
167
|
if (error.code !== SCORM_NO_ERROR && error.code !== "403" && error.code !== "0") {
|
|
197
168
|
console.warn(`SCORMService: GetValue for ${element} produced an error ${error.code}: ${error.message}. Returning raw value:`, value);
|
|
198
169
|
}
|
|
199
|
-
return
|
|
170
|
+
return value?.toString() ?? null;
|
|
200
171
|
}
|
|
201
172
|
commit() {
|
|
202
173
|
if (!this.hasAPI() || !this.isInitialized) {
|
|
@@ -268,7 +239,6 @@ var SCORMService = class {
|
|
|
268
239
|
}
|
|
269
240
|
}
|
|
270
241
|
getLastError() {
|
|
271
|
-
var _a, _b;
|
|
272
242
|
if (!this.hasAPI()) return { code: "-1", message: "SCORM API not found." };
|
|
273
243
|
const errorCode = this.scormVersionFound === "2004" ? this.scormAPI.GetLastError() : this.scormAPI.LMSGetLastError();
|
|
274
244
|
if (errorCode === SCORM_NO_ERROR || errorCode === 0 || errorCode === "0") {
|
|
@@ -278,8 +248,8 @@ var SCORMService = class {
|
|
|
278
248
|
const diagnostic = this.scormVersionFound === "2004" ? this.scormAPI.GetDiagnostic(errorCode.toString()) : this.scormAPI.LMSGetDiagnostic(errorCode.toString());
|
|
279
249
|
return {
|
|
280
250
|
code: errorCode.toString(),
|
|
281
|
-
message:
|
|
282
|
-
diagnostic:
|
|
251
|
+
message: errorMessage?.toString() ?? "Unknown error.",
|
|
252
|
+
diagnostic: diagnostic?.toString() ?? void 0
|
|
283
253
|
};
|
|
284
254
|
}
|
|
285
255
|
formatCMITime(totalSeconds) {
|
|
@@ -310,14 +280,13 @@ var SCORMService = class {
|
|
|
310
280
|
// src/services/evaluators/multiple-choice-evaluator.ts
|
|
311
281
|
var MultipleChoiceEvaluator = class {
|
|
312
282
|
async evaluate(question, answer) {
|
|
313
|
-
|
|
314
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
283
|
+
const points = question.points ?? 0;
|
|
315
284
|
const correctAnswerId = question.correctAnswerId;
|
|
316
285
|
const isCorrect = answer === correctAnswerId;
|
|
317
286
|
const correctOption = question.options.find((opt) => opt.id === correctAnswerId);
|
|
318
287
|
const correctAnswerDetail = {
|
|
319
288
|
id: correctAnswerId,
|
|
320
|
-
value:
|
|
289
|
+
value: correctOption?.text || ""
|
|
321
290
|
};
|
|
322
291
|
return Promise.resolve({
|
|
323
292
|
isCorrect,
|
|
@@ -330,8 +299,7 @@ var MultipleChoiceEvaluator = class {
|
|
|
330
299
|
// src/services/evaluators/multiple-response-evaluator.ts
|
|
331
300
|
var MultipleResponseEvaluator = class {
|
|
332
301
|
async evaluate(question, answer) {
|
|
333
|
-
|
|
334
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
302
|
+
const points = question.points ?? 0;
|
|
335
303
|
const correctAnswerIds = question.correctAnswerIds;
|
|
336
304
|
let isCorrect = false;
|
|
337
305
|
if (Array.isArray(answer)) {
|
|
@@ -340,10 +308,7 @@ var MultipleResponseEvaluator = class {
|
|
|
340
308
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
341
309
|
}
|
|
342
310
|
const correctValues = correctAnswerIds.map(
|
|
343
|
-
(id) =>
|
|
344
|
-
var _a2;
|
|
345
|
-
return ((_a2 = question.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
346
|
-
}
|
|
311
|
+
(id) => question.options.find((opt) => opt.id === id)?.text || ""
|
|
347
312
|
);
|
|
348
313
|
const correctAnswerDetail = {
|
|
349
314
|
id: correctAnswerIds,
|
|
@@ -360,8 +325,7 @@ var MultipleResponseEvaluator = class {
|
|
|
360
325
|
// src/services/evaluators/true-false-evaluator.ts
|
|
361
326
|
var TrueFalseEvaluator = class {
|
|
362
327
|
async evaluate(question, answer) {
|
|
363
|
-
|
|
364
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
328
|
+
const points = question.points ?? 0;
|
|
365
329
|
const correctAnswer = question.correctAnswer;
|
|
366
330
|
let userAnswer = answer;
|
|
367
331
|
if (typeof answer === "string") {
|
|
@@ -383,12 +347,11 @@ var TrueFalseEvaluator = class {
|
|
|
383
347
|
// src/services/evaluators/short-answer-evaluator.ts
|
|
384
348
|
var ShortAnswerEvaluator = class {
|
|
385
349
|
async evaluate(question, answer) {
|
|
386
|
-
|
|
387
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
350
|
+
const points = question.points ?? 0;
|
|
388
351
|
let isCorrect = false;
|
|
389
352
|
if (typeof answer === "string") {
|
|
390
353
|
const userAnswerTrimmed = answer.trim();
|
|
391
|
-
const caseSensitive =
|
|
354
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
392
355
|
isCorrect = question.acceptedAnswers.some(
|
|
393
356
|
(accAns) => caseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
|
|
394
357
|
);
|
|
@@ -408,8 +371,7 @@ var ShortAnswerEvaluator = class {
|
|
|
408
371
|
// src/services/evaluators/numeric-evaluator.ts
|
|
409
372
|
var NumericEvaluator = class {
|
|
410
373
|
async evaluate(question, answer) {
|
|
411
|
-
|
|
412
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
374
|
+
const points = question.points ?? 0;
|
|
413
375
|
let isCorrect = false;
|
|
414
376
|
if (typeof answer === "string" || typeof answer === "number") {
|
|
415
377
|
const userAnswerNum = parseFloat(String(answer));
|
|
@@ -432,17 +394,13 @@ var NumericEvaluator = class {
|
|
|
432
394
|
// src/services/evaluators/sequence-evaluator.ts
|
|
433
395
|
var SequenceEvaluator = class {
|
|
434
396
|
async evaluate(question, answer) {
|
|
435
|
-
|
|
436
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
397
|
+
const points = question.points ?? 0;
|
|
437
398
|
let isCorrect = false;
|
|
438
399
|
if (Array.isArray(answer) && answer.length === question.correctOrder.length) {
|
|
439
400
|
isCorrect = answer.every((itemId, index) => itemId === question.correctOrder[index]);
|
|
440
401
|
}
|
|
441
402
|
const correctValues = question.correctOrder.map(
|
|
442
|
-
(id) =>
|
|
443
|
-
var _a2;
|
|
444
|
-
return ((_a2 = question.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
445
|
-
}
|
|
403
|
+
(id) => question.items.find((item) => item.id === id)?.content || ""
|
|
446
404
|
);
|
|
447
405
|
const correctAnswerDetail = {
|
|
448
406
|
id: question.correctOrder,
|
|
@@ -459,17 +417,15 @@ var SequenceEvaluator = class {
|
|
|
459
417
|
// src/services/evaluators/matching-evaluator.ts
|
|
460
418
|
var MatchingEvaluator = class {
|
|
461
419
|
async evaluate(question, answer) {
|
|
462
|
-
|
|
463
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
420
|
+
const points = question.points ?? 0;
|
|
464
421
|
let isCorrect = false;
|
|
465
422
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
466
423
|
const userAnswerMap = answer;
|
|
467
424
|
isCorrect = question.correctAnswerMap.length === Object.keys(userAnswerMap).length && question.correctAnswerMap.every((map) => userAnswerMap[map.promptId] === map.optionId);
|
|
468
425
|
}
|
|
469
426
|
const correctMap = question.correctAnswerMap.reduce((acc, curr) => {
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
const optionText = ((_b = question.options.find((o) => o.id === curr.optionId)) == null ? void 0 : _b.content) || "";
|
|
427
|
+
const promptText = question.prompts.find((p) => p.id === curr.promptId)?.content || "";
|
|
428
|
+
const optionText = question.options.find((o) => o.id === curr.optionId)?.content || "";
|
|
473
429
|
acc[promptText] = optionText;
|
|
474
430
|
return acc;
|
|
475
431
|
}, {});
|
|
@@ -488,16 +444,14 @@ var MatchingEvaluator = class {
|
|
|
488
444
|
// src/services/evaluators/fill-in-the-blanks-evaluator.ts
|
|
489
445
|
var FillInTheBlanksEvaluator = class {
|
|
490
446
|
async evaluate(question, answer) {
|
|
491
|
-
|
|
492
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
447
|
+
const points = question.points ?? 0;
|
|
493
448
|
let isCorrect = false;
|
|
494
449
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
495
450
|
const userAnswerMap = answer;
|
|
496
451
|
isCorrect = question.answers.length > 0 && question.answers.every((correctAnsDef) => {
|
|
497
|
-
|
|
498
|
-
const userValForBlank = (_a2 = userAnswerMap[correctAnsDef.blankId]) == null ? void 0 : _a2.trim();
|
|
452
|
+
const userValForBlank = userAnswerMap[correctAnsDef.blankId]?.trim();
|
|
499
453
|
if (userValForBlank === void 0) return false;
|
|
500
|
-
const caseSensitive =
|
|
454
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
501
455
|
return correctAnsDef.acceptedValues.some(
|
|
502
456
|
(accVal) => caseSensitive ? accVal.trim() === userValForBlank : accVal.trim().toLowerCase() === userValForBlank.toLowerCase()
|
|
503
457
|
);
|
|
@@ -522,17 +476,15 @@ var FillInTheBlanksEvaluator = class {
|
|
|
522
476
|
// src/services/evaluators/drag-and-drop-evaluator.ts
|
|
523
477
|
var DragAndDropEvaluator = class {
|
|
524
478
|
async evaluate(question, answer) {
|
|
525
|
-
|
|
526
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
479
|
+
const points = question.points ?? 0;
|
|
527
480
|
let isCorrect = false;
|
|
528
481
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
529
482
|
const userAnswerMap = answer;
|
|
530
483
|
isCorrect = question.answerMap.length === Object.keys(userAnswerMap).length && question.answerMap.every((map) => userAnswerMap[map.draggableId] === map.dropZoneId);
|
|
531
484
|
}
|
|
532
485
|
const correctMap = question.answerMap.reduce((acc, curr) => {
|
|
533
|
-
|
|
534
|
-
const
|
|
535
|
-
const dropZoneText = ((_b = question.dropZones.find((z3) => z3.id === curr.dropZoneId)) == null ? void 0 : _b.label) || "";
|
|
486
|
+
const draggableText = question.draggableItems.find((d) => d.id === curr.draggableId)?.content || "";
|
|
487
|
+
const dropZoneText = question.dropZones.find((z3) => z3.id === curr.dropZoneId)?.label || "";
|
|
536
488
|
acc[draggableText] = dropZoneText;
|
|
537
489
|
return acc;
|
|
538
490
|
}, {});
|
|
@@ -551,8 +503,7 @@ var DragAndDropEvaluator = class {
|
|
|
551
503
|
// src/services/evaluators/hotspot-evaluator.ts
|
|
552
504
|
var HotspotEvaluator = class {
|
|
553
505
|
async evaluate(question, answer) {
|
|
554
|
-
|
|
555
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
506
|
+
const points = question.points ?? 0;
|
|
556
507
|
let isCorrect = false;
|
|
557
508
|
if (Array.isArray(answer)) {
|
|
558
509
|
const userAnswerSet = new Set(answer);
|
|
@@ -560,10 +511,7 @@ var HotspotEvaluator = class {
|
|
|
560
511
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
561
512
|
}
|
|
562
513
|
const correctValues = question.correctHotspotIds.map(
|
|
563
|
-
(id) =>
|
|
564
|
-
var _a2;
|
|
565
|
-
return ((_a2 = question.hotspots.find((h) => h.id === id)) == null ? void 0 : _a2.description) || id;
|
|
566
|
-
}
|
|
514
|
+
(id) => question.hotspots.find((h) => h.id === id)?.description || id
|
|
567
515
|
);
|
|
568
516
|
const correctAnswerDetail = {
|
|
569
517
|
id: question.correctHotspotIds,
|
|
@@ -580,11 +528,10 @@ var HotspotEvaluator = class {
|
|
|
580
528
|
// src/services/evaluators/programming-evaluator.ts
|
|
581
529
|
var ProgrammingEvaluator = class {
|
|
582
530
|
async evaluate(question, answer) {
|
|
583
|
-
|
|
584
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
531
|
+
const points = question.points ?? 0;
|
|
585
532
|
let isCorrect = false;
|
|
586
533
|
if (typeof answer === "string" && typeof question.solutionGeneratedCode === "string") {
|
|
587
|
-
if (typeof window !== "undefined" &&
|
|
534
|
+
if (typeof window !== "undefined" && window.Blockly?.JavaScript) {
|
|
588
535
|
const LocalBlockly = window.Blockly;
|
|
589
536
|
let generatedUserCode = "";
|
|
590
537
|
try {
|
|
@@ -658,10 +605,10 @@ var JsonRepairEngine = class {
|
|
|
658
605
|
if (breakIndex !== -1) {
|
|
659
606
|
const stringContent = afterUnterminated.substring(0, breakIndex);
|
|
660
607
|
const remainder = afterUnterminated.substring(breakIndex);
|
|
661
|
-
const escapedContent = stringContent.replace(
|
|
608
|
+
const escapedContent = stringContent.replace(/(?<!\\)"/g, '\\"');
|
|
662
609
|
repaired = beforeUnterminated + escapedContent + '"' + remainder;
|
|
663
610
|
} else {
|
|
664
|
-
const escapedContent = afterUnterminated.replace(
|
|
611
|
+
const escapedContent = afterUnterminated.replace(/(?<!\\)"/g, '\\"');
|
|
665
612
|
repaired = beforeUnterminated + escapedContent + '"';
|
|
666
613
|
}
|
|
667
614
|
}
|
|
@@ -737,7 +684,6 @@ var JsonRepairEngine = class {
|
|
|
737
684
|
* Main repair function that attempts multiple strategies.
|
|
738
685
|
*/
|
|
739
686
|
static repairJson(jsonStr) {
|
|
740
|
-
var _a;
|
|
741
687
|
let current = jsonStr.trim();
|
|
742
688
|
const maxAttempts = 5;
|
|
743
689
|
let lastError = "";
|
|
@@ -767,7 +713,7 @@ var JsonRepairEngine = class {
|
|
|
767
713
|
}
|
|
768
714
|
lastError = validation.error || "";
|
|
769
715
|
lastPosition = validation.position;
|
|
770
|
-
if (
|
|
716
|
+
if (validation.error?.includes("Unterminated string")) {
|
|
771
717
|
current = this.repairUnterminatedStrings(current);
|
|
772
718
|
} else {
|
|
773
719
|
current = this.applyCommonFixes(current);
|
|
@@ -1062,9 +1008,10 @@ var CodeEvaluationService = class {
|
|
|
1062
1008
|
userCode,
|
|
1063
1009
|
testCase
|
|
1064
1010
|
}, this.apiKey);
|
|
1065
|
-
return
|
|
1066
|
-
testCaseId: testCase.id
|
|
1067
|
-
|
|
1011
|
+
return {
|
|
1012
|
+
testCaseId: testCase.id,
|
|
1013
|
+
...aiResult
|
|
1014
|
+
};
|
|
1068
1015
|
}
|
|
1069
1016
|
/**
|
|
1070
1017
|
* Evaluates user's code against all test cases for a given question.
|
|
@@ -1101,8 +1048,7 @@ var CodeEvaluationService = class {
|
|
|
1101
1048
|
// src/services/evaluators/coding-evaluator.ts
|
|
1102
1049
|
var CodingEvaluator = class {
|
|
1103
1050
|
async evaluate(question, answer) {
|
|
1104
|
-
|
|
1105
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
1051
|
+
const points = question.points ?? 0;
|
|
1106
1052
|
if (typeof answer !== "string" || !answer.trim()) {
|
|
1107
1053
|
return {
|
|
1108
1054
|
isCorrect: false,
|
|
@@ -1159,17 +1105,16 @@ var QuizEngine = class {
|
|
|
1159
1105
|
this.quizResultState = { scormStatus: "idle" };
|
|
1160
1106
|
this.questionStartTime = null;
|
|
1161
1107
|
this.questionTimings = /* @__PURE__ */ new Map();
|
|
1162
|
-
var _a, _b, _c, _d, _e;
|
|
1163
1108
|
this.config = options.config;
|
|
1164
1109
|
this.callbacks = options.callbacks || {};
|
|
1165
|
-
this.questions =
|
|
1110
|
+
this.questions = this.config.settings?.shuffleQuestions ? [...this.config.questions].sort(() => Math.random() - 0.5) : this.config.questions;
|
|
1166
1111
|
this.overallStartTime = Date.now();
|
|
1167
1112
|
this.evaluators = /* @__PURE__ */ new Map();
|
|
1168
1113
|
this.registerEvaluators();
|
|
1169
|
-
if (
|
|
1114
|
+
if (this.config.settings?.timeLimitMinutes && this.config.settings.timeLimitMinutes > 0) {
|
|
1170
1115
|
this.timeLeftInSeconds = this.config.settings.timeLimitMinutes * 60;
|
|
1171
1116
|
}
|
|
1172
|
-
if (
|
|
1117
|
+
if (this.config.settings?.scorm) {
|
|
1173
1118
|
this.quizResultState.scormStatus = "initializing";
|
|
1174
1119
|
this.scormService = new SCORMService(this.config.settings.scorm);
|
|
1175
1120
|
if (this.scormService.hasAPI()) {
|
|
@@ -1202,7 +1147,7 @@ var QuizEngine = class {
|
|
|
1202
1147
|
if (this.timeLeftInSeconds !== null) {
|
|
1203
1148
|
this.startTimer();
|
|
1204
1149
|
}
|
|
1205
|
-
|
|
1150
|
+
this.callbacks.onQuestionChange?.(initialQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1206
1151
|
}
|
|
1207
1152
|
registerEvaluators() {
|
|
1208
1153
|
this.evaluators.set("multiple_choice", new MultipleChoiceEvaluator());
|
|
@@ -1240,15 +1185,14 @@ var QuizEngine = class {
|
|
|
1240
1185
|
}
|
|
1241
1186
|
}
|
|
1242
1187
|
handleTick() {
|
|
1243
|
-
var _a, _b, _c, _d;
|
|
1244
1188
|
if (this.timeLeftInSeconds === null) return;
|
|
1245
1189
|
if (this.timeLeftInSeconds > 0) {
|
|
1246
1190
|
this.timeLeftInSeconds--;
|
|
1247
|
-
|
|
1191
|
+
this.callbacks.onTimeTick?.(this.timeLeftInSeconds);
|
|
1248
1192
|
}
|
|
1249
1193
|
if (this.timeLeftInSeconds <= 0) {
|
|
1250
1194
|
this.stopTimer();
|
|
1251
|
-
|
|
1195
|
+
this.callbacks.onQuizTimeUp?.();
|
|
1252
1196
|
this.calculateResults();
|
|
1253
1197
|
}
|
|
1254
1198
|
}
|
|
@@ -1271,43 +1215,39 @@ var QuizEngine = class {
|
|
|
1271
1215
|
return this.quizResultState.score !== void 0;
|
|
1272
1216
|
}
|
|
1273
1217
|
submitAnswer(questionId, answer) {
|
|
1274
|
-
var _a, _b;
|
|
1275
1218
|
this.userAnswers.set(questionId, answer);
|
|
1276
1219
|
const question = this.questions.find((q) => q.id === questionId);
|
|
1277
|
-
if (question)
|
|
1220
|
+
if (question) this.callbacks.onAnswerSubmit?.(question, answer);
|
|
1278
1221
|
}
|
|
1279
1222
|
nextQuestion() {
|
|
1280
|
-
var _a, _b;
|
|
1281
1223
|
this._recordCurrentQuestionTime();
|
|
1282
1224
|
if (this.currentQuestionIndex < this.questions.length - 1) {
|
|
1283
1225
|
this.currentQuestionIndex++;
|
|
1284
1226
|
const currentQ = this.getCurrentQuestion();
|
|
1285
1227
|
this.questionStartTime = Date.now();
|
|
1286
|
-
|
|
1228
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1287
1229
|
return currentQ;
|
|
1288
1230
|
}
|
|
1289
1231
|
return null;
|
|
1290
1232
|
}
|
|
1291
1233
|
previousQuestion() {
|
|
1292
|
-
var _a, _b;
|
|
1293
1234
|
this._recordCurrentQuestionTime();
|
|
1294
1235
|
if (this.currentQuestionIndex > 0) {
|
|
1295
1236
|
this.currentQuestionIndex--;
|
|
1296
1237
|
const currentQ = this.getCurrentQuestion();
|
|
1297
1238
|
this.questionStartTime = Date.now();
|
|
1298
|
-
|
|
1239
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1299
1240
|
return currentQ;
|
|
1300
1241
|
}
|
|
1301
1242
|
return null;
|
|
1302
1243
|
}
|
|
1303
1244
|
goToQuestion(index) {
|
|
1304
|
-
var _a, _b;
|
|
1305
1245
|
if (index >= 0 && index < this.questions.length && index !== this.currentQuestionIndex) {
|
|
1306
1246
|
this._recordCurrentQuestionTime();
|
|
1307
1247
|
this.currentQuestionIndex = index;
|
|
1308
1248
|
const currentQ = this.getCurrentQuestion();
|
|
1309
1249
|
this.questionStartTime = Date.now();
|
|
1310
|
-
|
|
1250
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1311
1251
|
return currentQ;
|
|
1312
1252
|
}
|
|
1313
1253
|
return this.getCurrentQuestion();
|
|
@@ -1333,7 +1273,6 @@ var QuizEngine = class {
|
|
|
1333
1273
|
}
|
|
1334
1274
|
// (Tiếp theo từ Phần 1)
|
|
1335
1275
|
async calculateResults() {
|
|
1336
|
-
var _a, _b, _c, _d, _e;
|
|
1337
1276
|
this.stopTimer();
|
|
1338
1277
|
this._recordCurrentQuestionTime();
|
|
1339
1278
|
let totalScore = 0;
|
|
@@ -1342,7 +1281,7 @@ var QuizEngine = class {
|
|
|
1342
1281
|
let accumulatedTotalTimeSpent = 0;
|
|
1343
1282
|
for (const question of this.questions) {
|
|
1344
1283
|
const userAnswerRaw = this.userAnswers.get(question.id) || null;
|
|
1345
|
-
maxScore +=
|
|
1284
|
+
maxScore += question.points ?? 0;
|
|
1346
1285
|
const evaluator = this.evaluators.get(question.questionType);
|
|
1347
1286
|
if (!evaluator) {
|
|
1348
1287
|
console.warn(`No evaluator found for question type: ${question.questionType}`);
|
|
@@ -1382,13 +1321,13 @@ var QuizEngine = class {
|
|
|
1382
1321
|
}
|
|
1383
1322
|
const percentage = maxScore > 0 ? parseFloat((totalScore / maxScore * 100).toFixed(2)) : 0;
|
|
1384
1323
|
let passed = void 0;
|
|
1385
|
-
if (
|
|
1324
|
+
if (this.config.settings?.passingScorePercent != null) {
|
|
1386
1325
|
passed = percentage >= this.config.settings.passingScorePercent;
|
|
1387
1326
|
}
|
|
1388
1327
|
const totalQuizTimeSpentSeconds = parseFloat(accumulatedTotalTimeSpent.toFixed(2));
|
|
1389
1328
|
const averageTimePerQuestionSeconds = this.questions.length > 0 ? parseFloat((totalQuizTimeSpentSeconds / this.questions.length).toFixed(2)) : 0;
|
|
1390
1329
|
const metadataPerformance = await this._calculateMetadataPerformance();
|
|
1391
|
-
const finalResults =
|
|
1330
|
+
const finalResults = {
|
|
1392
1331
|
score: totalScore,
|
|
1393
1332
|
maxScore,
|
|
1394
1333
|
percentage,
|
|
@@ -1400,39 +1339,33 @@ var QuizEngine = class {
|
|
|
1400
1339
|
scormError: this.quizResultState.scormError,
|
|
1401
1340
|
studentName: this.quizResultState.studentName,
|
|
1402
1341
|
totalTimeSpentSeconds: totalQuizTimeSpentSeconds,
|
|
1403
|
-
averageTimePerQuestionSeconds
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1342
|
+
averageTimePerQuestionSeconds,
|
|
1343
|
+
...metadataPerformance
|
|
1344
|
+
};
|
|
1345
|
+
this.quizResultState = { ...this.quizResultState, ...finalResults };
|
|
1346
|
+
if (this.config.settings?.scorm) this._sendResultsToSCORM(finalResults);
|
|
1407
1347
|
await this._sendResultsToWebhook(finalResults);
|
|
1408
|
-
|
|
1348
|
+
this.callbacks.onQuizFinish?.(finalResults);
|
|
1409
1349
|
return finalResults;
|
|
1410
1350
|
}
|
|
1411
1351
|
formatUserAnswerDetail(question, userAnswerRaw) {
|
|
1412
|
-
var _a, _b, _c, _d, _e;
|
|
1413
1352
|
if (userAnswerRaw === null) return null;
|
|
1414
1353
|
switch (question.questionType) {
|
|
1415
1354
|
case "multiple_choice": {
|
|
1416
1355
|
const q = question;
|
|
1417
1356
|
const id = userAnswerRaw;
|
|
1418
|
-
return { id, value:
|
|
1357
|
+
return { id, value: q.options.find((opt) => opt.id === id)?.text || "" };
|
|
1419
1358
|
}
|
|
1420
1359
|
case "multiple_response": {
|
|
1421
1360
|
const q = question;
|
|
1422
1361
|
const ids = userAnswerRaw;
|
|
1423
|
-
const values = ids.map((id) =>
|
|
1424
|
-
var _a2;
|
|
1425
|
-
return ((_a2 = q.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
1426
|
-
});
|
|
1362
|
+
const values = ids.map((id) => q.options.find((opt) => opt.id === id)?.text || "");
|
|
1427
1363
|
return { id: ids, value: values };
|
|
1428
1364
|
}
|
|
1429
1365
|
case "sequence": {
|
|
1430
1366
|
const q = question;
|
|
1431
1367
|
const ids = userAnswerRaw;
|
|
1432
|
-
const values = ids.map((id) =>
|
|
1433
|
-
var _a2;
|
|
1434
|
-
return ((_a2 = q.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
1435
|
-
});
|
|
1368
|
+
const values = ids.map((id) => q.items.find((item) => item.id === id)?.content || "");
|
|
1436
1369
|
return { id: ids, value: values };
|
|
1437
1370
|
}
|
|
1438
1371
|
case "matching": {
|
|
@@ -1441,8 +1374,8 @@ var QuizEngine = class {
|
|
|
1441
1374
|
const valueMap = {};
|
|
1442
1375
|
for (const promptId in userAnswerMap) {
|
|
1443
1376
|
const optionId = userAnswerMap[promptId];
|
|
1444
|
-
const promptText =
|
|
1445
|
-
const optionText =
|
|
1377
|
+
const promptText = q.prompts.find((p) => p.id === promptId)?.content || "";
|
|
1378
|
+
const optionText = q.options.find((o) => o.id === optionId)?.content || "";
|
|
1446
1379
|
valueMap[promptText] = optionText;
|
|
1447
1380
|
}
|
|
1448
1381
|
return { id: null, value: valueMap };
|
|
@@ -1454,8 +1387,8 @@ var QuizEngine = class {
|
|
|
1454
1387
|
const enrichedUserAnswerMap = {};
|
|
1455
1388
|
for (const draggableId in userAnswerMapByIds) {
|
|
1456
1389
|
const dropZoneId = userAnswerMapByIds[draggableId];
|
|
1457
|
-
const draggableText =
|
|
1458
|
-
const dropZoneText =
|
|
1390
|
+
const draggableText = q.draggableItems.find((d) => d.id === draggableId)?.content || `(ID: ${draggableId})`;
|
|
1391
|
+
const dropZoneText = q.dropZones.find((z3) => z3.id === dropZoneId)?.label || `(ID: ${dropZoneId})`;
|
|
1459
1392
|
enrichedUserAnswerMap[draggableText] = dropZoneText;
|
|
1460
1393
|
}
|
|
1461
1394
|
return { id: null, value: enrichedUserAnswerMap };
|
|
@@ -1467,7 +1400,6 @@ var QuizEngine = class {
|
|
|
1467
1400
|
}
|
|
1468
1401
|
}
|
|
1469
1402
|
async _calculateMetadataPerformance() {
|
|
1470
|
-
var _a;
|
|
1471
1403
|
const loPerformanceMap = /* @__PURE__ */ new Map();
|
|
1472
1404
|
const categoryPerformanceMap = /* @__PURE__ */ new Map();
|
|
1473
1405
|
const topicPerformanceMap = /* @__PURE__ */ new Map();
|
|
@@ -1489,7 +1421,7 @@ var QuizEngine = class {
|
|
|
1489
1421
|
const evaluator = this.evaluators.get(q.questionType);
|
|
1490
1422
|
if (evaluator) {
|
|
1491
1423
|
const { isCorrect } = await evaluator.evaluate(q, userAnswer);
|
|
1492
|
-
const pointsForThisQuestion =
|
|
1424
|
+
const pointsForThisQuestion = q.points ?? 0;
|
|
1493
1425
|
updateMap(loPerformanceMap, q.learningObjective, pointsForThisQuestion, isCorrect);
|
|
1494
1426
|
updateMap(categoryPerformanceMap, q.category, pointsForThisQuestion, isCorrect);
|
|
1495
1427
|
updateMap(topicPerformanceMap, q.topic, pointsForThisQuestion, isCorrect);
|
|
@@ -1516,8 +1448,7 @@ var QuizEngine = class {
|
|
|
1516
1448
|
};
|
|
1517
1449
|
}
|
|
1518
1450
|
async _sendResultsToWebhook(results) {
|
|
1519
|
-
|
|
1520
|
-
if (!((_a = this.config.settings) == null ? void 0 : _a.webhookUrl)) {
|
|
1451
|
+
if (!this.config.settings?.webhookUrl) {
|
|
1521
1452
|
results.webhookStatus = "idle";
|
|
1522
1453
|
return;
|
|
1523
1454
|
}
|
|
@@ -1545,12 +1476,11 @@ var QuizEngine = class {
|
|
|
1545
1476
|
}
|
|
1546
1477
|
}
|
|
1547
1478
|
_sendResultsToSCORM(results) {
|
|
1548
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1549
1479
|
if (!this.scormService || !this.scormService.hasAPI() || this.quizResultState.scormStatus === "no_api") {
|
|
1550
1480
|
results.scormStatus = this.quizResultState.scormStatus || "idle";
|
|
1551
1481
|
return;
|
|
1552
1482
|
}
|
|
1553
|
-
if (this.quizResultState.scormStatus === "error" &&
|
|
1483
|
+
if (this.quizResultState.scormStatus === "error" && this.quizResultState.scormError?.includes("initialization failed")) {
|
|
1554
1484
|
results.scormStatus = "error";
|
|
1555
1485
|
results.scormError = this.quizResultState.scormError;
|
|
1556
1486
|
return;
|
|
@@ -1559,15 +1489,15 @@ var QuizEngine = class {
|
|
|
1559
1489
|
try {
|
|
1560
1490
|
this.scormService.setScore(results.score, results.maxScore, 0);
|
|
1561
1491
|
let lessonStatusSetting = "completed";
|
|
1562
|
-
if (
|
|
1492
|
+
if (this.config.settings?.passingScorePercent !== void 0 && this.config.settings?.passingScorePercent !== null) {
|
|
1563
1493
|
lessonStatusSetting = results.passed ? "passed" : "failed";
|
|
1564
|
-
} else if (
|
|
1494
|
+
} else if (this.config.settings?.scorm?.setCompletionOnFinish) {
|
|
1565
1495
|
lessonStatusSetting = "completed";
|
|
1566
1496
|
}
|
|
1567
1497
|
this.scormService.setLessonStatus(lessonStatusSetting, results.passed);
|
|
1568
1498
|
if (results.totalTimeSpentSeconds !== void 0 && this.scormService.formatCMITime) {
|
|
1569
1499
|
const cmiTime = this.scormService.formatCMITime(results.totalTimeSpentSeconds);
|
|
1570
|
-
const sessionTimeVar =
|
|
1500
|
+
const sessionTimeVar = this.config.settings?.scorm?.sessionTimeVar || (this.scormService.getSCORMVersion() === "2004" ? "cmi.session_time" : "cmi.core.session_time");
|
|
1571
1501
|
if (sessionTimeVar) this.scormService.setValue(sessionTimeVar, cmiTime);
|
|
1572
1502
|
}
|
|
1573
1503
|
const commitResult = this.scormService.commit();
|
|
@@ -1586,114 +1516,96 @@ var QuizEngine = class {
|
|
|
1586
1516
|
function cn(...inputs) {
|
|
1587
1517
|
return twMerge(clsx(inputs));
|
|
1588
1518
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
var RadioGroup = React28.forwardRef((_a, ref) => {
|
|
1592
|
-
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1593
|
-
return /* @__PURE__ */ React28.createElement(
|
|
1519
|
+
var RadioGroup = React9.forwardRef(({ className, ...props }, ref) => {
|
|
1520
|
+
return /* @__PURE__ */ jsx(
|
|
1594
1521
|
RadioGroupPrimitive.Root,
|
|
1595
|
-
|
|
1596
|
-
className: cn("grid gap-2", className)
|
|
1597
|
-
|
|
1522
|
+
{
|
|
1523
|
+
className: cn("grid gap-2", className),
|
|
1524
|
+
...props,
|
|
1598
1525
|
ref
|
|
1599
|
-
}
|
|
1526
|
+
}
|
|
1600
1527
|
);
|
|
1601
1528
|
});
|
|
1602
1529
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
1603
|
-
var RadioGroupItem =
|
|
1604
|
-
|
|
1605
|
-
return /* @__PURE__ */ React28.createElement(
|
|
1530
|
+
var RadioGroupItem = React9.forwardRef(({ className, ...props }, ref) => {
|
|
1531
|
+
return /* @__PURE__ */ jsx(
|
|
1606
1532
|
RadioGroupPrimitive.Item,
|
|
1607
|
-
|
|
1533
|
+
{
|
|
1608
1534
|
ref,
|
|
1609
1535
|
className: cn(
|
|
1610
1536
|
"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",
|
|
1611
1537
|
className
|
|
1612
|
-
)
|
|
1613
|
-
|
|
1614
|
-
|
|
1538
|
+
),
|
|
1539
|
+
...props,
|
|
1540
|
+
children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
|
|
1541
|
+
}
|
|
1615
1542
|
);
|
|
1616
1543
|
});
|
|
1617
1544
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
1618
1545
|
var labelVariants = cva(
|
|
1619
1546
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1620
1547
|
);
|
|
1621
|
-
var Label =
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
);
|
|
1630
|
-
});
|
|
1548
|
+
var Label = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1549
|
+
LabelPrimitive.Root,
|
|
1550
|
+
{
|
|
1551
|
+
ref,
|
|
1552
|
+
className: cn(labelVariants(), className),
|
|
1553
|
+
...props
|
|
1554
|
+
}
|
|
1555
|
+
));
|
|
1631
1556
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
1632
|
-
var Card =
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
className
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
);
|
|
1644
|
-
});
|
|
1557
|
+
var Card = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1558
|
+
"div",
|
|
1559
|
+
{
|
|
1560
|
+
ref,
|
|
1561
|
+
className: cn(
|
|
1562
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
1563
|
+
className
|
|
1564
|
+
),
|
|
1565
|
+
...props
|
|
1566
|
+
}
|
|
1567
|
+
));
|
|
1645
1568
|
Card.displayName = "Card";
|
|
1646
|
-
var CardHeader =
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
);
|
|
1655
|
-
});
|
|
1569
|
+
var CardHeader = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1570
|
+
"div",
|
|
1571
|
+
{
|
|
1572
|
+
ref,
|
|
1573
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1574
|
+
...props
|
|
1575
|
+
}
|
|
1576
|
+
));
|
|
1656
1577
|
CardHeader.displayName = "CardHeader";
|
|
1657
|
-
var CardTitle =
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
className
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
);
|
|
1669
|
-
});
|
|
1578
|
+
var CardTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1579
|
+
"div",
|
|
1580
|
+
{
|
|
1581
|
+
ref,
|
|
1582
|
+
className: cn(
|
|
1583
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
1584
|
+
className
|
|
1585
|
+
),
|
|
1586
|
+
...props
|
|
1587
|
+
}
|
|
1588
|
+
));
|
|
1670
1589
|
CardTitle.displayName = "CardTitle";
|
|
1671
|
-
var CardDescription =
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
);
|
|
1680
|
-
});
|
|
1590
|
+
var CardDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1591
|
+
"div",
|
|
1592
|
+
{
|
|
1593
|
+
ref,
|
|
1594
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1595
|
+
...props
|
|
1596
|
+
}
|
|
1597
|
+
));
|
|
1681
1598
|
CardDescription.displayName = "CardDescription";
|
|
1682
|
-
var CardContent =
|
|
1683
|
-
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1684
|
-
return /* @__PURE__ */ React28.createElement("div", __spreadValues({ ref, className: cn("p-6 pt-0", className) }, props));
|
|
1685
|
-
});
|
|
1599
|
+
var CardContent = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1686
1600
|
CardContent.displayName = "CardContent";
|
|
1687
|
-
var CardFooter =
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
);
|
|
1696
|
-
});
|
|
1601
|
+
var CardFooter = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1602
|
+
"div",
|
|
1603
|
+
{
|
|
1604
|
+
ref,
|
|
1605
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
1606
|
+
...props
|
|
1607
|
+
}
|
|
1608
|
+
));
|
|
1697
1609
|
CardFooter.displayName = "CardFooter";
|
|
1698
1610
|
var MarkdownRenderer = ({
|
|
1699
1611
|
content,
|
|
@@ -1737,20 +1649,19 @@ var MarkdownRenderer = ({
|
|
|
1737
1649
|
const processedContent = processContentForVideos(content);
|
|
1738
1650
|
return (
|
|
1739
1651
|
// Using Tailwind Typography for beautiful default styling of markdown content
|
|
1740
|
-
/* @__PURE__ */
|
|
1652
|
+
/* @__PURE__ */ jsx("div", { className: `prose dark:prose-invert max-w-none ${className}`, children: /* @__PURE__ */ jsx(
|
|
1741
1653
|
ReactMarkdown,
|
|
1742
1654
|
{
|
|
1743
1655
|
remarkPlugins: [remarkGfm, remarkMath],
|
|
1744
1656
|
rehypePlugins: [rehypeHighlight, rehypeKatex],
|
|
1745
1657
|
components: {
|
|
1746
1658
|
// Override the default image component to handle videos and responsive images
|
|
1747
|
-
img: (
|
|
1748
|
-
var _b = _a, { node } = _b, props = __objRest(_b, ["node"]);
|
|
1659
|
+
img: ({ node, ...props }) => {
|
|
1749
1660
|
const src = props.src || "";
|
|
1750
1661
|
const { platform, id } = getVideoId(src);
|
|
1751
1662
|
if (platform && id) {
|
|
1752
1663
|
const videoSrc = platform === "youtube" ? `https://www.youtube.com/embed/${id}` : `https://player.vimeo.com/video/${id}`;
|
|
1753
|
-
return /* @__PURE__ */
|
|
1664
|
+
return /* @__PURE__ */ jsx("div", { className: "aspect-w-16 aspect-h-9 my-4", children: /* @__PURE__ */ jsx(
|
|
1754
1665
|
"iframe",
|
|
1755
1666
|
{
|
|
1756
1667
|
src: videoSrc,
|
|
@@ -1759,13 +1670,14 @@ var MarkdownRenderer = ({
|
|
|
1759
1670
|
allowFullScreen: true,
|
|
1760
1671
|
className: "w-full h-full rounded-md"
|
|
1761
1672
|
}
|
|
1762
|
-
));
|
|
1673
|
+
) });
|
|
1763
1674
|
}
|
|
1764
1675
|
return (
|
|
1765
1676
|
// eslint-disable-next-line @next/next/no-img-element
|
|
1766
|
-
/* @__PURE__ */
|
|
1677
|
+
/* @__PURE__ */ jsx(
|
|
1767
1678
|
"img",
|
|
1768
|
-
|
|
1679
|
+
{
|
|
1680
|
+
...props,
|
|
1769
1681
|
style: {
|
|
1770
1682
|
maxWidth: "100%",
|
|
1771
1683
|
height: "auto",
|
|
@@ -1773,33 +1685,26 @@ var MarkdownRenderer = ({
|
|
|
1773
1685
|
margin: "1rem 0"
|
|
1774
1686
|
},
|
|
1775
1687
|
alt: props.alt || ""
|
|
1776
|
-
}
|
|
1688
|
+
}
|
|
1777
1689
|
)
|
|
1778
1690
|
);
|
|
1779
1691
|
},
|
|
1780
1692
|
// Override the default table to add responsive wrapper
|
|
1781
|
-
table: (
|
|
1782
|
-
var _d = _c, { node } = _d, props = __objRest(_d, ["node"]);
|
|
1783
|
-
return /* @__PURE__ */ React28__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React28__default.createElement("table", __spreadProps(__spreadValues({}, props), { className: "my-4 w-full text-sm" })));
|
|
1784
|
-
},
|
|
1693
|
+
table: ({ node, ...props }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx("table", { ...props, className: "my-4 w-full text-sm" }) }),
|
|
1785
1694
|
// Override default blockquote for better styling
|
|
1786
|
-
blockquote: (
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
processedContent
|
|
1798
|
-
))
|
|
1695
|
+
blockquote: ({ node, ...props }) => /* @__PURE__ */ jsx(
|
|
1696
|
+
"blockquote",
|
|
1697
|
+
{
|
|
1698
|
+
...props,
|
|
1699
|
+
className: "border-l-4 border-primary bg-muted/50 p-4 my-4 italic"
|
|
1700
|
+
}
|
|
1701
|
+
)
|
|
1702
|
+
},
|
|
1703
|
+
children: processedContent
|
|
1704
|
+
}
|
|
1705
|
+
) })
|
|
1799
1706
|
);
|
|
1800
1707
|
};
|
|
1801
|
-
|
|
1802
|
-
// src/react-ui/components/ui/MultipleChoiceQuestionUI.tsx
|
|
1803
1708
|
var MultipleChoiceQuestionUI = ({
|
|
1804
1709
|
question,
|
|
1805
1710
|
onAnswerChange,
|
|
@@ -1810,40 +1715,58 @@ var MultipleChoiceQuestionUI = ({
|
|
|
1810
1715
|
const handleSelection = (value) => {
|
|
1811
1716
|
onAnswerChange(value);
|
|
1812
1717
|
};
|
|
1813
|
-
return /* @__PURE__ */
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
},
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1825
|
-
if (showCorrectAnswer) {
|
|
1826
|
-
if (isCorrect) {
|
|
1827
|
-
itemClassName += " border-green-500 bg-green-500/10";
|
|
1828
|
-
} else if (isSelected && !isCorrect) {
|
|
1829
|
-
itemClassName += " border-destructive bg-destructive/10";
|
|
1830
|
-
} else {
|
|
1831
|
-
itemClassName += " border-muted";
|
|
1832
|
-
}
|
|
1833
|
-
} else {
|
|
1834
|
-
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1835
|
-
}
|
|
1836
|
-
return /* @__PURE__ */ React28__default.createElement(
|
|
1837
|
-
Label,
|
|
1718
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1719
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1720
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1721
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1722
|
+
"Points: ",
|
|
1723
|
+
points
|
|
1724
|
+
] })
|
|
1725
|
+
] }),
|
|
1726
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
1727
|
+
/* @__PURE__ */ jsx(
|
|
1728
|
+
RadioGroup,
|
|
1838
1729
|
{
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
className:
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1730
|
+
value: userAnswer || void 0,
|
|
1731
|
+
onValueChange: handleSelection,
|
|
1732
|
+
className: "space-y-3",
|
|
1733
|
+
"aria-labelledby": `question-prompt-${questionId}`,
|
|
1734
|
+
children: options.map((option) => {
|
|
1735
|
+
const isSelected = userAnswer === option.id;
|
|
1736
|
+
const isCorrect = option.id === correctAnswerId;
|
|
1737
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1738
|
+
if (showCorrectAnswer) {
|
|
1739
|
+
if (isCorrect) {
|
|
1740
|
+
itemClassName += " border-green-500 bg-green-500/10";
|
|
1741
|
+
} else if (isSelected && !isCorrect) {
|
|
1742
|
+
itemClassName += " border-destructive bg-destructive/10";
|
|
1743
|
+
} else {
|
|
1744
|
+
itemClassName += " border-muted";
|
|
1745
|
+
}
|
|
1746
|
+
} else {
|
|
1747
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1748
|
+
}
|
|
1749
|
+
return /* @__PURE__ */ jsx(
|
|
1750
|
+
Label,
|
|
1751
|
+
{
|
|
1752
|
+
htmlFor: option.id,
|
|
1753
|
+
className: itemClassName,
|
|
1754
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
1755
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: option.id, id: option.id, className: "mr-3" }),
|
|
1756
|
+
/* @__PURE__ */ jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: option.text }) })
|
|
1757
|
+
] })
|
|
1758
|
+
},
|
|
1759
|
+
option.id
|
|
1760
|
+
);
|
|
1761
|
+
})
|
|
1762
|
+
}
|
|
1763
|
+
),
|
|
1764
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1765
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1766
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1767
|
+
] })
|
|
1768
|
+
] })
|
|
1769
|
+
] });
|
|
1847
1770
|
};
|
|
1848
1771
|
var TrueFalseQuestionUI = ({
|
|
1849
1772
|
question,
|
|
@@ -1859,64 +1782,78 @@ var TrueFalseQuestionUI = ({
|
|
|
1859
1782
|
const handleSelection = (value) => {
|
|
1860
1783
|
onAnswerChange(value);
|
|
1861
1784
|
};
|
|
1862
|
-
return /* @__PURE__ */
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
},
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1874
|
-
if (showCorrectAnswer) {
|
|
1875
|
-
if (isOptionCorrect) {
|
|
1876
|
-
itemClassName += " border-green-500 bg-green-500/10";
|
|
1877
|
-
} else if (isSelected && !isOptionCorrect) {
|
|
1878
|
-
itemClassName += " border-destructive bg-destructive/10";
|
|
1879
|
-
} else {
|
|
1880
|
-
itemClassName += " border-muted";
|
|
1881
|
-
}
|
|
1882
|
-
} else {
|
|
1883
|
-
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1884
|
-
}
|
|
1885
|
-
return /* @__PURE__ */ React28__default.createElement(
|
|
1886
|
-
Label,
|
|
1785
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1786
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1787
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1788
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1789
|
+
"Points: ",
|
|
1790
|
+
points
|
|
1791
|
+
] })
|
|
1792
|
+
] }),
|
|
1793
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
1794
|
+
/* @__PURE__ */ jsx(
|
|
1795
|
+
RadioGroup,
|
|
1887
1796
|
{
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
className:
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1797
|
+
value: userAnswer || void 0,
|
|
1798
|
+
onValueChange: handleSelection,
|
|
1799
|
+
className: "space-y-3",
|
|
1800
|
+
"aria-labelledby": `question-prompt-${questionId}`,
|
|
1801
|
+
children: options.map((option) => {
|
|
1802
|
+
const isSelected = userAnswer === option.value;
|
|
1803
|
+
const isOptionCorrect = option.value === "true" && correctAnswer === true || option.value === "false" && correctAnswer === false;
|
|
1804
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
|
|
1805
|
+
if (showCorrectAnswer) {
|
|
1806
|
+
if (isOptionCorrect) {
|
|
1807
|
+
itemClassName += " border-green-500 bg-green-500/10";
|
|
1808
|
+
} else if (isSelected && !isOptionCorrect) {
|
|
1809
|
+
itemClassName += " border-destructive bg-destructive/10";
|
|
1810
|
+
} else {
|
|
1811
|
+
itemClassName += " border-muted";
|
|
1812
|
+
}
|
|
1813
|
+
} else {
|
|
1814
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1815
|
+
}
|
|
1816
|
+
return /* @__PURE__ */ jsx(
|
|
1817
|
+
Label,
|
|
1818
|
+
{
|
|
1819
|
+
htmlFor: option.id,
|
|
1820
|
+
className: itemClassName,
|
|
1821
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
1822
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: option.value, id: option.id, className: "mr-3" }),
|
|
1823
|
+
/* @__PURE__ */ jsx("span", { className: "text-base", children: option.label })
|
|
1824
|
+
] })
|
|
1825
|
+
},
|
|
1826
|
+
option.id
|
|
1827
|
+
);
|
|
1828
|
+
})
|
|
1829
|
+
}
|
|
1830
|
+
),
|
|
1831
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1832
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1833
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1834
|
+
] })
|
|
1835
|
+
] })
|
|
1836
|
+
] });
|
|
1896
1837
|
};
|
|
1897
|
-
var Checkbox =
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
className
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
}, props),
|
|
1908
|
-
/* @__PURE__ */ React28.createElement(
|
|
1838
|
+
var Checkbox = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1839
|
+
CheckboxPrimitive.Root,
|
|
1840
|
+
{
|
|
1841
|
+
ref,
|
|
1842
|
+
className: cn(
|
|
1843
|
+
"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",
|
|
1844
|
+
className
|
|
1845
|
+
),
|
|
1846
|
+
...props,
|
|
1847
|
+
children: /* @__PURE__ */ jsx(
|
|
1909
1848
|
CheckboxPrimitive.Indicator,
|
|
1910
1849
|
{
|
|
1911
|
-
className: cn("flex items-center justify-center text-current")
|
|
1912
|
-
|
|
1913
|
-
|
|
1850
|
+
className: cn("flex items-center justify-center text-current"),
|
|
1851
|
+
children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
|
|
1852
|
+
}
|
|
1914
1853
|
)
|
|
1915
|
-
|
|
1916
|
-
|
|
1854
|
+
}
|
|
1855
|
+
));
|
|
1917
1856
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
1918
|
-
|
|
1919
|
-
// src/react-ui/components/ui/MultipleResponseQuestionUI.tsx
|
|
1920
1857
|
var MultipleResponseQuestionUI = ({
|
|
1921
1858
|
question,
|
|
1922
1859
|
onAnswerChange,
|
|
@@ -1938,59 +1875,74 @@ var MultipleResponseQuestionUI = ({
|
|
|
1938
1875
|
}
|
|
1939
1876
|
onAnswerChange(newAnswers.length > 0 ? newAnswers : null);
|
|
1940
1877
|
};
|
|
1941
|
-
return /* @__PURE__ */
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
Checkbox,
|
|
1963
|
-
{
|
|
1964
|
-
id: option.id,
|
|
1965
|
-
checked: isSelected,
|
|
1966
|
-
onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
|
|
1967
|
-
className: "mr-3",
|
|
1968
|
-
"aria-label": option.text.replace(/<[^>]*>?/gm, "")
|
|
1878
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1879
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1880
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1881
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1882
|
+
"Points: ",
|
|
1883
|
+
points
|
|
1884
|
+
] })
|
|
1885
|
+
] }),
|
|
1886
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
1887
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", role: "group", "aria-labelledby": `question-prompt-${questionId}`, children: options.map((option) => {
|
|
1888
|
+
const isSelected = Array.isArray(userAnswer) && userAnswer.includes(option.id);
|
|
1889
|
+
const isCorrectOption = correctAnswerIds.includes(option.id);
|
|
1890
|
+
let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary flex items-center";
|
|
1891
|
+
if (showCorrectAnswer) {
|
|
1892
|
+
if (isCorrectOption) {
|
|
1893
|
+
itemClassName += isSelected ? " border-green-500 bg-green-500/10" : " border-green-500";
|
|
1894
|
+
} else {
|
|
1895
|
+
itemClassName += isSelected ? " border-destructive bg-destructive/10" : " border-muted";
|
|
1896
|
+
}
|
|
1897
|
+
} else {
|
|
1898
|
+
itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
|
|
1969
1899
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1900
|
+
return /* @__PURE__ */ jsxs(
|
|
1901
|
+
Label,
|
|
1902
|
+
{
|
|
1903
|
+
htmlFor: option.id,
|
|
1904
|
+
className: itemClassName,
|
|
1905
|
+
children: [
|
|
1906
|
+
/* @__PURE__ */ jsx(
|
|
1907
|
+
Checkbox,
|
|
1908
|
+
{
|
|
1909
|
+
id: option.id,
|
|
1910
|
+
checked: isSelected,
|
|
1911
|
+
onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
|
|
1912
|
+
className: "mr-3",
|
|
1913
|
+
"aria-label": option.text.replace(/<[^>]*>?/gm, "")
|
|
1914
|
+
}
|
|
1915
|
+
),
|
|
1916
|
+
/* @__PURE__ */ jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: option.text }) })
|
|
1917
|
+
]
|
|
1918
|
+
},
|
|
1919
|
+
option.id
|
|
1920
|
+
);
|
|
1921
|
+
}) }),
|
|
1922
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1923
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
1924
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
1925
|
+
] })
|
|
1926
|
+
] })
|
|
1927
|
+
] });
|
|
1974
1928
|
};
|
|
1975
|
-
var Input =
|
|
1976
|
-
(
|
|
1977
|
-
|
|
1978
|
-
return /* @__PURE__ */ React28.createElement(
|
|
1929
|
+
var Input = React9.forwardRef(
|
|
1930
|
+
({ className, type, ...props }, ref) => {
|
|
1931
|
+
return /* @__PURE__ */ jsx(
|
|
1979
1932
|
"input",
|
|
1980
|
-
|
|
1933
|
+
{
|
|
1981
1934
|
type,
|
|
1982
1935
|
className: cn(
|
|
1983
1936
|
"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",
|
|
1984
1937
|
className
|
|
1985
1938
|
),
|
|
1986
|
-
ref
|
|
1987
|
-
|
|
1939
|
+
ref,
|
|
1940
|
+
...props
|
|
1941
|
+
}
|
|
1988
1942
|
);
|
|
1989
1943
|
}
|
|
1990
1944
|
);
|
|
1991
1945
|
Input.displayName = "Input";
|
|
1992
|
-
|
|
1993
|
-
// src/react-ui/components/ui/ShortAnswerQuestionUI.tsx
|
|
1994
1946
|
var ShortAnswerQuestionUI = ({
|
|
1995
1947
|
question,
|
|
1996
1948
|
onAnswerChange,
|
|
@@ -2009,20 +1961,44 @@ var ShortAnswerQuestionUI = ({
|
|
|
2009
1961
|
(accAns) => isCaseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
|
|
2010
1962
|
);
|
|
2011
1963
|
}
|
|
2012
|
-
return /* @__PURE__ */
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
className:
|
|
1964
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
1965
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
1966
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
1967
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
1968
|
+
"Points: ",
|
|
1969
|
+
points
|
|
1970
|
+
] })
|
|
1971
|
+
] }),
|
|
1972
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
1973
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `short-answer-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
|
|
1974
|
+
/* @__PURE__ */ jsx(
|
|
1975
|
+
Input,
|
|
1976
|
+
{
|
|
1977
|
+
id: `short-answer-input-${questionId}`,
|
|
1978
|
+
type: "text",
|
|
1979
|
+
value: displayUserAnswer,
|
|
1980
|
+
onChange: handleInputChange,
|
|
1981
|
+
placeholder: "Type your answer here...",
|
|
1982
|
+
"aria-describedby": explanation ? `explanation-${questionId}` : void 0,
|
|
1983
|
+
className: `
|
|
2022
1984
|
${showCorrectAnswer && userAnswer ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
|
|
2023
1985
|
`
|
|
2024
|
-
|
|
2025
|
-
|
|
1986
|
+
}
|
|
1987
|
+
),
|
|
1988
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
1989
|
+
userAnswer && !isActuallyCorrect && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
|
|
1990
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
1991
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Accepted Answers:" }),
|
|
1992
|
+
/* @__PURE__ */ jsx("ul", { className: "list-disc list-inside text-sm text-accent-foreground/80", children: acceptedAnswers.map((ans, idx) => /* @__PURE__ */ jsx("li", { children: ans }, idx)) }),
|
|
1993
|
+
isCaseSensitive && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "(Case-sensitive)" })
|
|
1994
|
+
] }),
|
|
1995
|
+
explanation && /* @__PURE__ */ jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
1996
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
|
|
1997
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
1998
|
+
] })
|
|
1999
|
+
] })
|
|
2000
|
+
] })
|
|
2001
|
+
] });
|
|
2026
2002
|
};
|
|
2027
2003
|
var NumericQuestionUI = ({
|
|
2028
2004
|
question,
|
|
@@ -2045,22 +2021,48 @@ var NumericQuestionUI = ({
|
|
|
2045
2021
|
isActuallyCorrect = tolerance !== void 0 && tolerance !== null ? Math.abs(userAnswerNum - correctAnswerValue) <= tolerance : userAnswerNum === correctAnswerValue;
|
|
2046
2022
|
}
|
|
2047
2023
|
}
|
|
2048
|
-
return /* @__PURE__ */
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2024
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2025
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2026
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2027
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2028
|
+
"Points: ",
|
|
2029
|
+
points
|
|
2030
|
+
] })
|
|
2031
|
+
] }),
|
|
2032
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
2033
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `numeric-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
|
|
2034
|
+
/* @__PURE__ */ jsx(
|
|
2035
|
+
Input,
|
|
2036
|
+
{
|
|
2037
|
+
id: `numeric-input-${questionId}`,
|
|
2038
|
+
type: "text",
|
|
2039
|
+
inputMode: "numeric",
|
|
2040
|
+
pattern: "[0-9]*\\.?[0-9]*",
|
|
2041
|
+
value: displayUserAnswer,
|
|
2042
|
+
onChange: handleInputChange,
|
|
2043
|
+
placeholder: "Enter a number",
|
|
2044
|
+
"aria-describedby": explanation ? `explanation-${questionId}` : void 0,
|
|
2045
|
+
className: `
|
|
2060
2046
|
${showCorrectAnswer && userAnswer !== null && userAnswer !== "" ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
|
|
2061
2047
|
`
|
|
2062
|
-
|
|
2063
|
-
|
|
2048
|
+
}
|
|
2049
|
+
),
|
|
2050
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2051
|
+
userAnswer !== null && userAnswer !== "" && !isActuallyCorrect && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
|
|
2052
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2053
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Correct Answer:" }),
|
|
2054
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-accent-foreground/80", children: [
|
|
2055
|
+
correctAnswerValue,
|
|
2056
|
+
tolerance !== void 0 && tolerance !== null && tolerance > 0 && ` (Tolerance: \xB1${tolerance}, Accepted range: ${correctAnswerValue - tolerance} to ${correctAnswerValue + tolerance})`
|
|
2057
|
+
] })
|
|
2058
|
+
] }),
|
|
2059
|
+
explanation && /* @__PURE__ */ jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
2060
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
|
|
2061
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
2062
|
+
] })
|
|
2063
|
+
] })
|
|
2064
|
+
] })
|
|
2065
|
+
] });
|
|
2064
2066
|
};
|
|
2065
2067
|
var FillInTheBlanksQuestionUI = ({
|
|
2066
2068
|
question,
|
|
@@ -2084,15 +2086,14 @@ var FillInTheBlanksQuestionUI = ({
|
|
|
2084
2086
|
}
|
|
2085
2087
|
}, [segments, userAnswer]);
|
|
2086
2088
|
const handleInputChange = (blankId, value) => {
|
|
2087
|
-
const newInputs =
|
|
2089
|
+
const newInputs = { ...userInputs, [blankId]: value };
|
|
2088
2090
|
setUserInputs(newInputs);
|
|
2089
2091
|
const hasValue = Object.values(newInputs).some((val) => val.trim() !== "");
|
|
2090
2092
|
onAnswerChange(hasValue ? newInputs : null);
|
|
2091
2093
|
};
|
|
2092
2094
|
const getCorrectnessForBlank = (blankId) => {
|
|
2093
|
-
var _a;
|
|
2094
2095
|
if (!showCorrectAnswer || !userInputs[blankId]) return null;
|
|
2095
|
-
const userAnswerForBlank =
|
|
2096
|
+
const userAnswerForBlank = userInputs[blankId]?.trim();
|
|
2096
2097
|
const correctAnswerDef = correctAnswersMap.find((a) => a.blankId === blankId);
|
|
2097
2098
|
if (!correctAnswerDef || !userAnswerForBlank) return false;
|
|
2098
2099
|
const caseSensitive = isCaseSensitive === void 0 ? false : isCaseSensitive;
|
|
@@ -2100,49 +2101,78 @@ var FillInTheBlanksQuestionUI = ({
|
|
|
2100
2101
|
(accVal) => caseSensitive ? accVal.trim() === userAnswerForBlank : accVal.trim().toLowerCase() === userAnswerForBlank.toLowerCase()
|
|
2101
2102
|
);
|
|
2102
2103
|
};
|
|
2103
|
-
return /* @__PURE__ */
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2104
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2105
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2106
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2107
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2108
|
+
"Points: ",
|
|
2109
|
+
points
|
|
2110
|
+
] })
|
|
2111
|
+
] }),
|
|
2112
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
2113
|
+
/* @__PURE__ */ 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) => {
|
|
2114
|
+
if (segment.type === "text") {
|
|
2115
|
+
return /* @__PURE__ */ jsx(
|
|
2116
|
+
MarkdownRenderer,
|
|
2117
|
+
{
|
|
2118
|
+
content: segment.content || "",
|
|
2119
|
+
className: "inline"
|
|
2120
|
+
},
|
|
2121
|
+
`text-${index}`
|
|
2122
|
+
);
|
|
2112
2123
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2124
|
+
if (segment.type === "blank" && segment.id) {
|
|
2125
|
+
const blankId = segment.id;
|
|
2126
|
+
const isCorrect = getCorrectnessForBlank(blankId);
|
|
2127
|
+
let inputClassName = "inline-block w-auto min-w-[100px] max-w-[200px] h-8 mx-1 align-baseline text-base";
|
|
2128
|
+
if (showCorrectAnswer && userInputs[blankId]?.trim()) {
|
|
2129
|
+
inputClassName += isCorrect ? " border-green-500 focus-visible:ring-green-500" : " border-destructive focus-visible:ring-destructive";
|
|
2130
|
+
} else {
|
|
2131
|
+
inputClassName += " border-input";
|
|
2132
|
+
}
|
|
2133
|
+
return /* @__PURE__ */ jsx(
|
|
2134
|
+
Input,
|
|
2135
|
+
{
|
|
2136
|
+
id: blankId,
|
|
2137
|
+
type: "text",
|
|
2138
|
+
value: userInputs[blankId] || "",
|
|
2139
|
+
onChange: (e) => handleInputChange(blankId, e.target.value),
|
|
2140
|
+
placeholder: "\u0110i\u1EC1n...",
|
|
2141
|
+
className: inputClassName,
|
|
2142
|
+
"aria-label": `Blank ${index + 1}`,
|
|
2143
|
+
disabled: showCorrectAnswer
|
|
2144
|
+
},
|
|
2145
|
+
blankId
|
|
2146
|
+
);
|
|
2136
2147
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2148
|
+
return null;
|
|
2149
|
+
}) }),
|
|
2150
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2151
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch chung:" }),
|
|
2152
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2153
|
+
] }),
|
|
2154
|
+
showCorrectAnswer && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-3", children: correctAnswersMap.map((ansDef) => {
|
|
2155
|
+
const isBlankCorrect = getCorrectnessForBlank(ansDef.blankId);
|
|
2156
|
+
const userAnswerDisplay = userInputs[ansDef.blankId] || "Ch\u01B0a tr\u1EA3 l\u1EDDi";
|
|
2157
|
+
return /* @__PURE__ */ jsxs("div", { className: `p-2 border rounded-md ${isBlankCorrect ? "border-green-500/50 bg-green-500/10" : "border-destructive/50 bg-destructive/10"}`, children: [
|
|
2158
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
2159
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold", children: [
|
|
2160
|
+
"\xD4 tr\u1ED1ng '",
|
|
2161
|
+
segments.find((s) => s.id === ansDef.blankId && s.type === "blank")?.id || ansDef.blankId,
|
|
2162
|
+
"':"
|
|
2163
|
+
] }),
|
|
2164
|
+
' B\u1EA1n \u0111\xE3 \u0111i\u1EC1n: "',
|
|
2165
|
+
userAnswerDisplay,
|
|
2166
|
+
'".'
|
|
2167
|
+
] }),
|
|
2168
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs", children: [
|
|
2169
|
+
"\u0110\xE1p \xE1n ch\u1EA5p nh\u1EADn: ",
|
|
2170
|
+
ansDef.acceptedValues.join(", ")
|
|
2171
|
+
] })
|
|
2172
|
+
] }, `feedback-${ansDef.blankId}`);
|
|
2173
|
+
}) })
|
|
2174
|
+
] })
|
|
2175
|
+
] });
|
|
2146
2176
|
};
|
|
2147
2177
|
var buttonVariants = cva(
|
|
2148
2178
|
"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",
|
|
@@ -2169,16 +2199,16 @@ var buttonVariants = cva(
|
|
|
2169
2199
|
}
|
|
2170
2200
|
}
|
|
2171
2201
|
);
|
|
2172
|
-
var Button =
|
|
2173
|
-
(
|
|
2174
|
-
var _b = _a, { className, variant, size, asChild = false } = _b, props = __objRest(_b, ["className", "variant", "size", "asChild"]);
|
|
2202
|
+
var Button = React9.forwardRef(
|
|
2203
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
2175
2204
|
const Comp = asChild ? Slot : "button";
|
|
2176
|
-
return /* @__PURE__ */
|
|
2205
|
+
return /* @__PURE__ */ jsx(
|
|
2177
2206
|
Comp,
|
|
2178
|
-
|
|
2207
|
+
{
|
|
2179
2208
|
className: cn(buttonVariants({ variant, size, className })),
|
|
2180
|
-
ref
|
|
2181
|
-
|
|
2209
|
+
ref,
|
|
2210
|
+
...props
|
|
2211
|
+
}
|
|
2182
2212
|
);
|
|
2183
2213
|
}
|
|
2184
2214
|
);
|
|
@@ -2233,145 +2263,180 @@ var SequenceQuestionUI = ({
|
|
|
2233
2263
|
if (!showCorrectAnswer || !Array.isArray(userAnswer) || userAnswer.length <= index) return null;
|
|
2234
2264
|
const userItemId = userAnswer[index];
|
|
2235
2265
|
const correctItemId = correctOrder[index];
|
|
2236
|
-
return userItemId === correctItemId ? /* @__PURE__ */
|
|
2266
|
+
return userItemId === correctItemId ? /* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4 text-green-500 ml-2 flex-shrink-0" }) : /* @__PURE__ */ jsx(XCircle, { className: "h-4 w-4 text-destructive ml-2 flex-shrink-0" });
|
|
2237
2267
|
};
|
|
2238
|
-
return /* @__PURE__ */
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2268
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2269
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2270
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2271
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2272
|
+
"Points: ",
|
|
2273
|
+
points
|
|
2274
|
+
] })
|
|
2275
|
+
] }),
|
|
2276
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0 space-y-6", children: [
|
|
2277
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2278
|
+
/* @__PURE__ */ jsx(Label, { className: "font-semibold", children: "S\u1EAFp x\u1EBFp c\xE1c m\u1EE5c sau theo \u0111\xFAng th\u1EE9 t\u1EF1:" }),
|
|
2279
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2", children: availableItems.map((item) => /* @__PURE__ */ jsx(
|
|
2280
|
+
Button,
|
|
2281
|
+
{
|
|
2282
|
+
variant: "outline",
|
|
2283
|
+
onClick: () => handleSelectItem(item),
|
|
2284
|
+
className: "justify-start text-left h-auto py-2 px-3 whitespace-normal",
|
|
2285
|
+
disabled: showCorrectAnswer,
|
|
2286
|
+
children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: item.content })
|
|
2287
|
+
},
|
|
2288
|
+
item.id
|
|
2289
|
+
)) }),
|
|
2290
|
+
availableItems.length === 0 && selectedSequence.length > 0 && /* @__PURE__ */ 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.' })
|
|
2291
|
+
] }),
|
|
2292
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2293
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
2294
|
+
/* @__PURE__ */ jsx(Label, { className: "font-semibold", children: "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn:" }),
|
|
2295
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: handleResetSequence, disabled: showCorrectAnswer || selectedSequence.length === 0, children: [
|
|
2296
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "mr-2 h-3.5 w-3.5" }),
|
|
2297
|
+
" \u0110\u1EB7t l\u1EA1i"
|
|
2298
|
+
] })
|
|
2299
|
+
] }),
|
|
2300
|
+
selectedSequence.length === 0 ? /* @__PURE__ */ 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__ */ jsx("ul", { className: "space-y-2", children: selectedSequence.map((item, index) => /* @__PURE__ */ jsxs(
|
|
2301
|
+
"li",
|
|
2302
|
+
{
|
|
2303
|
+
onClick: () => handleRemoveFromSequence(item, index),
|
|
2304
|
+
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`,
|
|
2305
|
+
children: [
|
|
2306
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-grow flex items-center", children: [
|
|
2307
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold mr-2", children: [
|
|
2308
|
+
index + 1,
|
|
2309
|
+
"."
|
|
2310
|
+
] }),
|
|
2311
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: item.content })
|
|
2312
|
+
] }),
|
|
2313
|
+
showCorrectAnswer ? getFeedbackIcon(index) : /* @__PURE__ */ jsx(XCircle, { className: "h-4 w-4 text-muted-foreground hover:text-destructive flex-shrink-0" })
|
|
2314
|
+
]
|
|
2315
|
+
},
|
|
2316
|
+
item.id
|
|
2317
|
+
)) })
|
|
2318
|
+
] }),
|
|
2319
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2320
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2321
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Th\u1EE9 t\u1EF1 \u0111\xFAng:" }),
|
|
2322
|
+
/* @__PURE__ */ jsx("ol", { className: "list-decimal list-inside text-sm text-accent-foreground/80 space-y-1 mt-1", children: correctOrder.map((itemId) => {
|
|
2323
|
+
const item = items.find((i) => i.id === itemId);
|
|
2324
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: item ? item.content : "Kh\xF4ng t\xECm th\u1EA5y m\u1EE5c" }) }, itemId);
|
|
2325
|
+
}) })
|
|
2326
|
+
] }),
|
|
2327
|
+
explanation && /* @__PURE__ */ jsxs("div", { className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
|
|
2328
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2329
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
|
|
2330
|
+
] })
|
|
2331
|
+
] })
|
|
2332
|
+
] })
|
|
2333
|
+
] });
|
|
2261
2334
|
};
|
|
2262
2335
|
var Select = SelectPrimitive.Root;
|
|
2263
2336
|
var SelectValue = SelectPrimitive.Value;
|
|
2264
|
-
var SelectTrigger =
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
className
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2337
|
+
var SelectTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
2338
|
+
SelectPrimitive.Trigger,
|
|
2339
|
+
{
|
|
2340
|
+
ref,
|
|
2341
|
+
className: cn(
|
|
2342
|
+
"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",
|
|
2343
|
+
className
|
|
2344
|
+
),
|
|
2345
|
+
...props,
|
|
2346
|
+
children: [
|
|
2347
|
+
children,
|
|
2348
|
+
/* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
2349
|
+
]
|
|
2350
|
+
}
|
|
2351
|
+
));
|
|
2279
2352
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
2280
|
-
var SelectScrollUpButton =
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
className
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
);
|
|
2293
|
-
});
|
|
2353
|
+
var SelectScrollUpButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2354
|
+
SelectPrimitive.ScrollUpButton,
|
|
2355
|
+
{
|
|
2356
|
+
ref,
|
|
2357
|
+
className: cn(
|
|
2358
|
+
"flex cursor-default items-center justify-center py-1",
|
|
2359
|
+
className
|
|
2360
|
+
),
|
|
2361
|
+
...props,
|
|
2362
|
+
children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
|
|
2363
|
+
}
|
|
2364
|
+
));
|
|
2294
2365
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
2295
|
-
var SelectScrollDownButton =
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
className
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
);
|
|
2308
|
-
});
|
|
2366
|
+
var SelectScrollDownButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2367
|
+
SelectPrimitive.ScrollDownButton,
|
|
2368
|
+
{
|
|
2369
|
+
ref,
|
|
2370
|
+
className: cn(
|
|
2371
|
+
"flex cursor-default items-center justify-center py-1",
|
|
2372
|
+
className
|
|
2373
|
+
),
|
|
2374
|
+
...props,
|
|
2375
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
|
|
2376
|
+
}
|
|
2377
|
+
));
|
|
2309
2378
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
2310
|
-
var SelectContent =
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
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",
|
|
2319
|
-
className
|
|
2320
|
-
),
|
|
2321
|
-
position
|
|
2322
|
-
}, props),
|
|
2323
|
-
/* @__PURE__ */ React28.createElement(SelectScrollUpButton, null),
|
|
2324
|
-
/* @__PURE__ */ React28.createElement(
|
|
2325
|
-
SelectPrimitive.Viewport,
|
|
2326
|
-
{
|
|
2327
|
-
className: cn(
|
|
2328
|
-
"p-1",
|
|
2329
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
2330
|
-
)
|
|
2331
|
-
},
|
|
2332
|
-
children
|
|
2379
|
+
var SelectContent = React9.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
2380
|
+
SelectPrimitive.Content,
|
|
2381
|
+
{
|
|
2382
|
+
ref,
|
|
2383
|
+
className: cn(
|
|
2384
|
+
"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",
|
|
2385
|
+
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",
|
|
2386
|
+
className
|
|
2333
2387
|
),
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2388
|
+
position,
|
|
2389
|
+
...props,
|
|
2390
|
+
children: [
|
|
2391
|
+
/* @__PURE__ */ jsx(SelectScrollUpButton, {}),
|
|
2392
|
+
/* @__PURE__ */ jsx(
|
|
2393
|
+
SelectPrimitive.Viewport,
|
|
2394
|
+
{
|
|
2395
|
+
className: cn(
|
|
2396
|
+
"p-1",
|
|
2397
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
2398
|
+
),
|
|
2399
|
+
children
|
|
2400
|
+
}
|
|
2401
|
+
),
|
|
2402
|
+
/* @__PURE__ */ jsx(SelectScrollDownButton, {})
|
|
2403
|
+
]
|
|
2404
|
+
}
|
|
2405
|
+
) }));
|
|
2337
2406
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
2338
|
-
var SelectLabel =
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
);
|
|
2347
|
-
});
|
|
2407
|
+
var SelectLabel = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2408
|
+
SelectPrimitive.Label,
|
|
2409
|
+
{
|
|
2410
|
+
ref,
|
|
2411
|
+
className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
|
|
2412
|
+
...props
|
|
2413
|
+
}
|
|
2414
|
+
));
|
|
2348
2415
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
2349
|
-
var SelectItem =
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
className
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2416
|
+
var SelectItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
2417
|
+
SelectPrimitive.Item,
|
|
2418
|
+
{
|
|
2419
|
+
ref,
|
|
2420
|
+
className: cn(
|
|
2421
|
+
"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",
|
|
2422
|
+
className
|
|
2423
|
+
),
|
|
2424
|
+
...props,
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
|
|
2427
|
+
/* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
|
|
2428
|
+
]
|
|
2429
|
+
}
|
|
2430
|
+
));
|
|
2364
2431
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
2365
|
-
var SelectSeparator =
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
);
|
|
2374
|
-
});
|
|
2432
|
+
var SelectSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2433
|
+
SelectPrimitive.Separator,
|
|
2434
|
+
{
|
|
2435
|
+
ref,
|
|
2436
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
2437
|
+
...props
|
|
2438
|
+
}
|
|
2439
|
+
));
|
|
2375
2440
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
2376
2441
|
var MatchingQuestionUI = ({
|
|
2377
2442
|
question,
|
|
@@ -2399,14 +2464,13 @@ var MatchingQuestionUI = ({
|
|
|
2399
2464
|
}
|
|
2400
2465
|
}, [userAnswer, prompts]);
|
|
2401
2466
|
const handleSelectChange = (promptId, optionId) => {
|
|
2402
|
-
const newAnswers =
|
|
2467
|
+
const newAnswers = { ...currentAnswers, [promptId]: optionId };
|
|
2403
2468
|
setCurrentAnswers(newAnswers);
|
|
2404
2469
|
const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
|
|
2405
2470
|
onAnswerChange(hasSelection ? newAnswers : null);
|
|
2406
2471
|
};
|
|
2407
2472
|
const getCorrectOptionIdForPrompt = (promptId) => {
|
|
2408
|
-
|
|
2409
|
-
return (_a = correctAnswerMap.find((map) => map.promptId === promptId)) == null ? void 0 : _a.optionId;
|
|
2473
|
+
return correctAnswerMap.find((map) => map.promptId === promptId)?.optionId;
|
|
2410
2474
|
};
|
|
2411
2475
|
const getPlainText = (htmlString) => {
|
|
2412
2476
|
if (!htmlString) return "";
|
|
@@ -2417,31 +2481,58 @@ var MatchingQuestionUI = ({
|
|
|
2417
2481
|
}
|
|
2418
2482
|
return htmlString.replace(/<[^>]*>?/gm, "");
|
|
2419
2483
|
};
|
|
2420
|
-
return /* @__PURE__ */
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2484
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2485
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2486
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2487
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2488
|
+
"Points: ",
|
|
2489
|
+
points
|
|
2490
|
+
] })
|
|
2491
|
+
] }),
|
|
2492
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2493
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-4", children: prompts.map((promptItem) => {
|
|
2494
|
+
const selectedOptionId = currentAnswers[promptItem.id] || "";
|
|
2495
|
+
const correctOptionId = getCorrectOptionIdForPrompt(promptItem.id);
|
|
2496
|
+
const isSelectionCorrect = showCorrectAnswer && selectedOptionId ? selectedOptionId === correctOptionId : null;
|
|
2497
|
+
let borderColor = "border-muted";
|
|
2498
|
+
if (showCorrectAnswer && selectedOptionId) {
|
|
2499
|
+
borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
|
|
2500
|
+
}
|
|
2501
|
+
return /* @__PURE__ */ jsxs("div", { className: `p-3 border rounded-md ${borderColor} transition-colors bg-background`, children: [
|
|
2502
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: promptItem.content }) }),
|
|
2503
|
+
/* @__PURE__ */ jsxs(
|
|
2504
|
+
Select,
|
|
2505
|
+
{
|
|
2506
|
+
value: selectedOptionId,
|
|
2507
|
+
onValueChange: (value) => handleSelectChange(promptItem.id, value),
|
|
2508
|
+
disabled: showCorrectAnswer,
|
|
2509
|
+
children: [
|
|
2510
|
+
/* @__PURE__ */ jsx(SelectTrigger, { id: `select-prompt-${promptItem.id}`, children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Ch\u1ECDn \u0111\xE1p \xE1n..." }) }),
|
|
2511
|
+
/* @__PURE__ */ jsx(SelectContent, { children: shuffledOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.id, className: "whitespace-normal", children: getPlainText(option.content) }, option.id)) })
|
|
2512
|
+
]
|
|
2513
|
+
}
|
|
2514
|
+
),
|
|
2515
|
+
showCorrectAnswer && selectedOptionId && (isSelectionCorrect ? /* @__PURE__ */ jsx(CheckCircle, { className: "h-5 w-5 text-green-500 mt-2 inline-block" }) : /* @__PURE__ */ jsx(XCircle, { className: "h-5 w-5 text-destructive mt-2 inline-block" })),
|
|
2516
|
+
showCorrectAnswer && !selectedOptionId && correctOptionId && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
2517
|
+
"Ch\u01B0a ch\u1ECDn. \u0110\xE1p \xE1n \u0111\xFAng: ",
|
|
2518
|
+
getPlainText(shuffledOptions.find((o) => o.id === correctOptionId)?.content)
|
|
2519
|
+
] })
|
|
2520
|
+
] }, promptItem.id);
|
|
2521
|
+
}) }),
|
|
2522
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2523
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2524
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2525
|
+
] }),
|
|
2526
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2527
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
|
|
2528
|
+
/* @__PURE__ */ jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: correctAnswerMap.map((map) => {
|
|
2529
|
+
const promptText = prompts.find((p) => p.id === map.promptId)?.content;
|
|
2530
|
+
const optionText = initialOptions.find((o) => o.id === map.optionId)?.content;
|
|
2531
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: `<strong>${getPlainText(promptText) || "N/A"}</strong> gh\xE9p v\u1EDBi <strong>${getPlainText(optionText) || "N/A"}</strong>` }) }, map.promptId);
|
|
2532
|
+
}) })
|
|
2533
|
+
] })
|
|
2534
|
+
] })
|
|
2535
|
+
] });
|
|
2445
2536
|
};
|
|
2446
2537
|
var DragAndDropQuestionUI = ({
|
|
2447
2538
|
question,
|
|
@@ -2461,41 +2552,70 @@ var DragAndDropQuestionUI = ({
|
|
|
2461
2552
|
}
|
|
2462
2553
|
}, [userAnswer, draggableItems]);
|
|
2463
2554
|
const handleSelectChange = (draggableItemId, dropZoneId) => {
|
|
2464
|
-
const newAnswers =
|
|
2555
|
+
const newAnswers = { ...currentAnswers, [draggableItemId]: dropZoneId };
|
|
2465
2556
|
setCurrentAnswers(newAnswers);
|
|
2466
2557
|
const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
|
|
2467
2558
|
onAnswerChange(hasSelection ? newAnswers : null);
|
|
2468
2559
|
};
|
|
2469
2560
|
const getCorrectDropZoneIdForDraggable = (draggableItemId) => {
|
|
2470
|
-
|
|
2471
|
-
return (_a = answerMap.find((map) => map.draggableId === draggableItemId)) == null ? void 0 : _a.dropZoneId;
|
|
2561
|
+
return answerMap.find((map) => map.draggableId === draggableItemId)?.dropZoneId;
|
|
2472
2562
|
};
|
|
2473
|
-
return /* @__PURE__ */
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2563
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2564
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2565
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2566
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2567
|
+
"Points: ",
|
|
2568
|
+
points
|
|
2569
|
+
] })
|
|
2570
|
+
] }),
|
|
2571
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2572
|
+
backgroundImageUrl && /* @__PURE__ */ jsx("div", { className: "mb-4 overflow-hidden rounded-md border", children: /* @__PURE__ */ 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" }) }),
|
|
2573
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2574
|
+
/* @__PURE__ */ jsx(Label, { className: "font-semibold", children: "Gh\xE9p c\xE1c m\u1EE5c sau v\xE0o \u0111\xFAng v\u1ECB tr\xED:" }),
|
|
2575
|
+
draggableItems.map((item) => {
|
|
2576
|
+
const selectedDropZoneId = currentAnswers[item.id] || "";
|
|
2577
|
+
const correctDropZoneId = getCorrectDropZoneIdForDraggable(item.id);
|
|
2578
|
+
const isSelectionCorrect = showCorrectAnswer && selectedDropZoneId ? selectedDropZoneId === correctDropZoneId : null;
|
|
2579
|
+
let itemStyle = "flex flex-col sm:flex-row items-start sm:items-center justify-between p-3 border rounded-md transition-colors bg-background";
|
|
2580
|
+
if (showCorrectAnswer && selectedDropZoneId) {
|
|
2581
|
+
itemStyle += isSelectionCorrect ? " border-green-500" : " border-destructive";
|
|
2582
|
+
} else {
|
|
2583
|
+
itemStyle += " border-muted";
|
|
2584
|
+
}
|
|
2585
|
+
return /* @__PURE__ */ jsxs("div", { className: itemStyle, children: [
|
|
2586
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `select-draggable-${item.id}`, className: "font-medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: item.content }) }),
|
|
2587
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 w-full sm:w-auto", children: [
|
|
2588
|
+
/* @__PURE__ */ jsxs(
|
|
2589
|
+
Select,
|
|
2590
|
+
{
|
|
2591
|
+
value: selectedDropZoneId,
|
|
2592
|
+
onValueChange: (value) => handleSelectChange(item.id, value),
|
|
2593
|
+
disabled: showCorrectAnswer,
|
|
2594
|
+
children: [
|
|
2595
|
+
/* @__PURE__ */ jsx(SelectTrigger, { id: `select-draggable-${item.id}`, className: "w-full sm:min-w-[200px]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Ch\u1ECDn v\u1ECB tr\xED..." }) }),
|
|
2596
|
+
/* @__PURE__ */ jsx(SelectContent, { children: dropZones.map((zone) => /* @__PURE__ */ jsx(SelectItem, { value: zone.id, children: zone.label.replace(/<[^>]*>?/gm, "") }, zone.id)) })
|
|
2597
|
+
]
|
|
2598
|
+
}
|
|
2599
|
+
),
|
|
2600
|
+
showCorrectAnswer && selectedDropZoneId && (isSelectionCorrect ? /* @__PURE__ */ jsx(CheckCircle, { className: "h-5 w-5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ jsx(XCircle, { className: "h-5 w-5 text-destructive flex-shrink-0" }))
|
|
2601
|
+
] })
|
|
2602
|
+
] }, item.id);
|
|
2603
|
+
})
|
|
2604
|
+
] }),
|
|
2605
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2606
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
|
|
2607
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2608
|
+
] }),
|
|
2609
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2610
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
|
|
2611
|
+
/* @__PURE__ */ jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: answerMap.map((map) => {
|
|
2612
|
+
const draggableText = draggableItems.find((d) => d.id === map.draggableId)?.content;
|
|
2613
|
+
const dropZoneText = dropZones.find((z3) => z3.id === map.dropZoneId)?.label;
|
|
2614
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: `<strong>${draggableText || "N/A"}</strong> v\xE0o <strong>${dropZoneText || "N/A"}</strong>` }) }, map.draggableId);
|
|
2615
|
+
}) })
|
|
2616
|
+
] })
|
|
2617
|
+
] })
|
|
2618
|
+
] });
|
|
2499
2619
|
};
|
|
2500
2620
|
var HotspotQuestionUI = ({
|
|
2501
2621
|
question,
|
|
@@ -2565,32 +2685,54 @@ var HotspotQuestionUI = ({
|
|
|
2565
2685
|
tempDiv.innerHTML = htmlString;
|
|
2566
2686
|
return tempDiv.textContent || tempDiv.innerText || "";
|
|
2567
2687
|
};
|
|
2568
|
-
return /* @__PURE__ */
|
|
2569
|
-
"
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2688
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
2689
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
2690
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: prompt }) }),
|
|
2691
|
+
points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
2692
|
+
"Points: ",
|
|
2693
|
+
points
|
|
2694
|
+
] })
|
|
2695
|
+
] }),
|
|
2696
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
2697
|
+
/* @__PURE__ */ jsxs("div", { className: "relative w-full border border-muted rounded-md overflow-hidden", style: { maxWidth: "100%", maxHeight: "500px" }, children: [
|
|
2698
|
+
/* @__PURE__ */ jsx(
|
|
2699
|
+
"img",
|
|
2700
|
+
{
|
|
2701
|
+
src: imageUrl,
|
|
2702
|
+
alt: imageAltText || "Hotspot image",
|
|
2703
|
+
className: "block max-w-full max-h-full object-contain",
|
|
2704
|
+
"data-ai-hint": question.imageAltText ? question.imageAltText.split(" ").slice(0, 2).join(" ") : "diagram illustration"
|
|
2705
|
+
}
|
|
2706
|
+
),
|
|
2707
|
+
hotspots.map((hotspot) => /* @__PURE__ */ jsx(
|
|
2708
|
+
"div",
|
|
2709
|
+
{
|
|
2710
|
+
title: getPlainText(hotspot.description) || `Hotspot ${hotspot.id}`,
|
|
2711
|
+
style: getHotspotStyle(hotspot),
|
|
2712
|
+
onClick: () => handleHotspotClick(hotspot.id),
|
|
2713
|
+
"aria-pressed": selectedIds.includes(hotspot.id),
|
|
2714
|
+
role: "button",
|
|
2715
|
+
tabIndex: showCorrectAnswer ? -1 : 0,
|
|
2716
|
+
onKeyDown: (e) => {
|
|
2717
|
+
if (e.key === "Enter" || e.key === " ") handleHotspotClick(hotspot.id);
|
|
2718
|
+
}
|
|
2719
|
+
},
|
|
2720
|
+
hotspot.id
|
|
2721
|
+
))
|
|
2722
|
+
] }),
|
|
2723
|
+
showCorrectAnswer && explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
2724
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
2725
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
|
|
2726
|
+
] }),
|
|
2727
|
+
showCorrectAnswer && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
2728
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-md", children: "Correct Hotspots:" }),
|
|
2729
|
+
/* @__PURE__ */ jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: (correctAnswerIds || []).map((id) => {
|
|
2730
|
+
const hotspot = hotspots.find((h) => h.id === id);
|
|
2731
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: hotspot?.description || hotspot?.id || "N/A", className: "inline" }) }, id);
|
|
2732
|
+
}) })
|
|
2733
|
+
] })
|
|
2734
|
+
] })
|
|
2735
|
+
] });
|
|
2594
2736
|
};
|
|
2595
2737
|
var loadScript = (src, async = true) => {
|
|
2596
2738
|
return new Promise((resolve, reject) => {
|
|
@@ -2623,8 +2765,7 @@ var loadScript = (src, async = true) => {
|
|
|
2623
2765
|
};
|
|
2624
2766
|
var loadBlocklyScript = () => {
|
|
2625
2767
|
return new Promise((resolve, reject) => {
|
|
2626
|
-
|
|
2627
|
-
if (typeof ((_a = window.Blockly) == null ? void 0 : _a.Blocks) !== "undefined" && typeof ((_b = window.Blockly) == null ? void 0 : _b.JavaScript) !== "undefined") {
|
|
2768
|
+
if (typeof window.Blockly?.Blocks !== "undefined" && typeof window.Blockly?.JavaScript !== "undefined") {
|
|
2628
2769
|
resolve();
|
|
2629
2770
|
return;
|
|
2630
2771
|
}
|
|
@@ -2652,7 +2793,6 @@ var loadBlocklyScript = () => {
|
|
|
2652
2793
|
}
|
|
2653
2794
|
];
|
|
2654
2795
|
const tryLoadFromCDN = async (cdnIndex) => {
|
|
2655
|
-
var _a2, _b2, _c;
|
|
2656
2796
|
if (cdnIndex >= cdnOptions.length) {
|
|
2657
2797
|
throw new Error("All Blockly CDN loading options failed");
|
|
2658
2798
|
}
|
|
@@ -2661,9 +2801,9 @@ var loadBlocklyScript = () => {
|
|
|
2661
2801
|
await loadScript(cdn.mainSrc);
|
|
2662
2802
|
const BlocklyGlobal = window.Blockly;
|
|
2663
2803
|
if (typeof BlocklyGlobal === "undefined") throw new Error(`Blockly global not found from ${cdn.name}.`);
|
|
2664
|
-
if (
|
|
2804
|
+
if (BlocklyGlobal.utils?.global?.setPaths) {
|
|
2665
2805
|
BlocklyGlobal.utils.global.setPaths(cdn.mediaPath);
|
|
2666
|
-
} else if (
|
|
2806
|
+
} else if (BlocklyGlobal.utils?.global) {
|
|
2667
2807
|
BlocklyGlobal.utils.global.blocklyPath = cdn.mediaPath;
|
|
2668
2808
|
BlocklyGlobal.MEDIA = cdn.mediaPath;
|
|
2669
2809
|
} else {
|
|
@@ -2707,7 +2847,7 @@ var useBlocklyLoader = () => {
|
|
|
2707
2847
|
}, []);
|
|
2708
2848
|
return { isLoading, loadError, isReady, retry };
|
|
2709
2849
|
};
|
|
2710
|
-
var BlocklyProgrammingQuestionUI =
|
|
2850
|
+
var BlocklyProgrammingQuestionUI = React9__default.forwardRef(({
|
|
2711
2851
|
question,
|
|
2712
2852
|
userAnswer,
|
|
2713
2853
|
showCorrectAnswer = false
|
|
@@ -2719,10 +2859,9 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2719
2859
|
const { isLoading: blocklyLoading, loadError: blocklyLoadError, isReady: blocklyReady, retry } = useBlocklyLoader();
|
|
2720
2860
|
useImperativeHandle(ref, () => ({
|
|
2721
2861
|
getWorkspaceXml: () => {
|
|
2722
|
-
var _a, _b;
|
|
2723
2862
|
if (workspaceRef.current && blocklyReady) {
|
|
2724
2863
|
const LocalBlockly = window.Blockly;
|
|
2725
|
-
if (!
|
|
2864
|
+
if (!LocalBlockly?.Xml?.workspaceToDom || !LocalBlockly?.Xml?.domToText) {
|
|
2726
2865
|
console.warn("Blockly.Xml methods not available for XML serialization in getWorkspaceXml.");
|
|
2727
2866
|
return null;
|
|
2728
2867
|
}
|
|
@@ -2738,10 +2877,9 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2738
2877
|
}
|
|
2739
2878
|
}));
|
|
2740
2879
|
const initializeBlocklyWorkspace = useCallback(() => {
|
|
2741
|
-
var _a;
|
|
2742
2880
|
if (!blocklyReady || !blocklyDivRef.current) return;
|
|
2743
2881
|
const LocalBlockly = window.Blockly;
|
|
2744
|
-
if (!
|
|
2882
|
+
if (!LocalBlockly?.inject || !LocalBlockly?.Xml || !LocalBlockly?.Events || !LocalBlockly?.Themes) {
|
|
2745
2883
|
setComponentError("Blockly library not fully loaded.");
|
|
2746
2884
|
setIsInitializingComponent(false);
|
|
2747
2885
|
return;
|
|
@@ -2773,7 +2911,7 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2773
2911
|
}
|
|
2774
2912
|
}
|
|
2775
2913
|
setIsInitializingComponent(true);
|
|
2776
|
-
if (
|
|
2914
|
+
if (workspaceRef.current?.dispose) {
|
|
2777
2915
|
try {
|
|
2778
2916
|
workspaceRef.current.dispose();
|
|
2779
2917
|
} catch (e) {
|
|
@@ -2850,8 +2988,7 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2850
2988
|
initializeBlocklyWorkspace();
|
|
2851
2989
|
}
|
|
2852
2990
|
return () => {
|
|
2853
|
-
|
|
2854
|
-
if ((_a = workspaceRef.current) == null ? void 0 : _a.dispose) {
|
|
2991
|
+
if (workspaceRef.current?.dispose) {
|
|
2855
2992
|
try {
|
|
2856
2993
|
workspaceRef.current.dispose();
|
|
2857
2994
|
} catch (disposeError) {
|
|
@@ -2868,7 +3005,7 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2868
3005
|
resizeTimeout = setTimeout(() => {
|
|
2869
3006
|
if (workspaceRef.current && blocklyReady) {
|
|
2870
3007
|
const LocalBlockly = window.Blockly;
|
|
2871
|
-
if (LocalBlockly
|
|
3008
|
+
if (LocalBlockly?.svgResize) {
|
|
2872
3009
|
LocalBlockly.svgResize(workspaceRef.current);
|
|
2873
3010
|
}
|
|
2874
3011
|
}
|
|
@@ -2882,64 +3019,91 @@ var BlocklyProgrammingQuestionUI = React28__default.forwardRef(({
|
|
|
2882
3019
|
}, [blocklyReady]);
|
|
2883
3020
|
const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
|
|
2884
3021
|
const workspaceContainerId = `blockly-workspace-container-${question.id}`;
|
|
2885
|
-
return /* @__PURE__ */
|
|
2886
|
-
"
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
3022
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3023
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3024
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: question.prompt }) }),
|
|
3025
|
+
question.points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3026
|
+
"Points: ",
|
|
3027
|
+
question.points
|
|
3028
|
+
] })
|
|
3029
|
+
] }),
|
|
3030
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
3031
|
+
blocklyLoading && /* @__PURE__ */ jsx(
|
|
3032
|
+
"div",
|
|
3033
|
+
{
|
|
3034
|
+
style: {
|
|
3035
|
+
height: workspaceHeight,
|
|
3036
|
+
width: "100%",
|
|
3037
|
+
borderRadius: "0.375rem",
|
|
3038
|
+
border: "1px solid hsl(var(--border))",
|
|
3039
|
+
backgroundColor: "hsl(var(--background))"
|
|
3040
|
+
},
|
|
3041
|
+
className: "flex items-center justify-center",
|
|
3042
|
+
children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
3043
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground animate-pulse mb-2", children: "Loading Blockly Environment..." }),
|
|
3044
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto" })
|
|
3045
|
+
] })
|
|
3046
|
+
}
|
|
3047
|
+
),
|
|
3048
|
+
blocklyLoadError && !blocklyLoading && /* @__PURE__ */ jsx(
|
|
3049
|
+
"div",
|
|
3050
|
+
{
|
|
3051
|
+
style: {
|
|
3052
|
+
height: workspaceHeight,
|
|
3053
|
+
width: "100%",
|
|
3054
|
+
borderRadius: "0.375rem",
|
|
3055
|
+
border: "1px solid hsl(var(--destructive))",
|
|
3056
|
+
backgroundColor: "hsl(var(--card))"
|
|
3057
|
+
},
|
|
3058
|
+
className: "flex items-center justify-center p-4",
|
|
3059
|
+
children: /* @__PURE__ */ jsxs("div", { className: "text-destructive text-center", children: [
|
|
3060
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-lg", children: "Failed to load Blockly" }),
|
|
3061
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-2 mb-3", children: blocklyLoadError }),
|
|
3062
|
+
/* @__PURE__ */ jsxs("div", { className: "space-x-2", children: [
|
|
3063
|
+
/* @__PURE__ */ jsx(
|
|
3064
|
+
"button",
|
|
3065
|
+
{
|
|
3066
|
+
onClick: retry,
|
|
3067
|
+
className: "px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors text-sm",
|
|
3068
|
+
children: "Try Again"
|
|
3069
|
+
}
|
|
3070
|
+
),
|
|
3071
|
+
/* @__PURE__ */ jsx(
|
|
3072
|
+
"button",
|
|
3073
|
+
{
|
|
3074
|
+
onClick: () => window.location.reload(),
|
|
3075
|
+
className: "px-4 py-2 bg-destructive text-destructive-foreground rounded hover:bg-destructive/90 transition-colors text-sm",
|
|
3076
|
+
children: "Refresh Page"
|
|
3077
|
+
}
|
|
3078
|
+
)
|
|
3079
|
+
] })
|
|
3080
|
+
] })
|
|
3081
|
+
}
|
|
3082
|
+
),
|
|
3083
|
+
!blocklyLoading && !blocklyLoadError && /* @__PURE__ */ jsx(
|
|
3084
|
+
"div",
|
|
3085
|
+
{
|
|
3086
|
+
id: workspaceContainerId,
|
|
3087
|
+
ref: blocklyDivRef,
|
|
3088
|
+
style: {
|
|
3089
|
+
height: workspaceHeight,
|
|
3090
|
+
width: "100%",
|
|
3091
|
+
borderRadius: "0.375rem",
|
|
3092
|
+
border: `1px solid ${componentError ? "hsl(var(--destructive))" : "hsl(var(--border))"}`,
|
|
3093
|
+
backgroundColor: "hsl(var(--card))",
|
|
3094
|
+
position: "relative",
|
|
3095
|
+
userSelect: "none",
|
|
3096
|
+
overflow: "hidden"
|
|
3097
|
+
},
|
|
3098
|
+
"aria-label": `Blockly programming workspace for question: ${question.prompt}`
|
|
3099
|
+
}
|
|
3100
|
+
),
|
|
3101
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3102
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3103
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
|
|
3104
|
+
] })
|
|
3105
|
+
] })
|
|
3106
|
+
] });
|
|
2943
3107
|
});
|
|
2944
3108
|
BlocklyProgrammingQuestionUI.displayName = "BlocklyProgrammingQuestionUI";
|
|
2945
3109
|
var SCRATCH_JS_ENGINE_LAYOUT_PATH = "/static/scratch-blocks/js/blockly_compressed_vertical.js";
|
|
@@ -3020,7 +3184,6 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3020
3184
|
const [componentError, setComponentError] = useState(null);
|
|
3021
3185
|
const [isLoadingScripts, setIsLoadingScripts] = useState(true);
|
|
3022
3186
|
const attemptLoadScripts = useCallback(async () => {
|
|
3023
|
-
var _a, _b, _c, _d;
|
|
3024
3187
|
setIsLoadingScripts(true);
|
|
3025
3188
|
setComponentError(null);
|
|
3026
3189
|
console.log("ScratchUI: Starting script loading sequence...");
|
|
@@ -3031,19 +3194,19 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3031
3194
|
if (typeof BlocklyGlobalEngine === "undefined") {
|
|
3032
3195
|
throw new Error(`Blockly global object (window.Blockly) not found after loading engine script: ${SCRATCH_JS_ENGINE_LAYOUT_PATH}.`);
|
|
3033
3196
|
}
|
|
3034
|
-
console.log(`ScratchUI: After engine/layout load: Blockly defined: ${!!BlocklyGlobalEngine}, Blockly.Blocks defined: ${!!
|
|
3197
|
+
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}`);
|
|
3035
3198
|
console.log("ScratchUI: Attempting to load Messages:", SCRATCH_JS_MSG_EN_PATH);
|
|
3036
3199
|
await loadScript2(SCRATCH_JS_MSG_EN_PATH);
|
|
3037
3200
|
let BlocklyGlobalAfterMsg = window.Blockly;
|
|
3038
3201
|
if (!BlocklyGlobalAfterMsg) throw new Error("Blockly global disappeared after loading message script.");
|
|
3039
|
-
console.log(`ScratchUI: After en.js load: Blockly defined: ${!!BlocklyGlobalAfterMsg}, Blockly.Msg defined: ${!!
|
|
3202
|
+
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}`);
|
|
3040
3203
|
if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
|
|
3041
3204
|
console.warn("ScratchUI: Blockly.Msg appears unpopulated or empty. Checking if Blockly.ScratchMsgs.addLocaleData can be used...");
|
|
3042
3205
|
if (BlocklyGlobalAfterMsg.ScratchMsgs && typeof BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData === "function") {
|
|
3043
3206
|
console.log("ScratchUI: Blockly.ScratchMsgs.addLocaleData is available. Attempting to call Blockly.ScratchMsgs.addLocaleData('en').");
|
|
3044
3207
|
BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData("en");
|
|
3045
3208
|
BlocklyGlobalAfterMsg = window.Blockly;
|
|
3046
|
-
console.log(`ScratchUI: After attempting addLocaleData('en'): Keys in Blockly.Msg: ${
|
|
3209
|
+
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}`);
|
|
3047
3210
|
if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
|
|
3048
3211
|
throw new Error("Blockly.Msg still empty after calling addLocaleData. Message loading failed critically.");
|
|
3049
3212
|
}
|
|
@@ -3057,8 +3220,8 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3057
3220
|
if (!BlocklyGlobalBlocks || !BlocklyGlobalBlocks.Blocks) {
|
|
3058
3221
|
throw new Error(`Blockly.Blocks not defined after loading ${SCRATCH_JS_BLOCK_DEFINITIONS_PATH}.`);
|
|
3059
3222
|
}
|
|
3060
|
-
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: ${!!
|
|
3061
|
-
if (typeof
|
|
3223
|
+
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}`);
|
|
3224
|
+
if (typeof BlocklyGlobalBlocks.Blocks?.motion_movesteps === "undefined") {
|
|
3062
3225
|
throw new Error(`Essential Scratch blocks (e.g., motion_movesteps) not found after loading block definitions. Available blocks: ${Object.keys(BlocklyGlobalBlocks.Blocks || {}).join(", ")}`);
|
|
3063
3226
|
}
|
|
3064
3227
|
setIsBlocklyReady(true);
|
|
@@ -3077,7 +3240,7 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3077
3240
|
useImperativeHandle(ref, () => ({
|
|
3078
3241
|
getWorkspaceXml: () => {
|
|
3079
3242
|
const LocalBlockly = window.Blockly;
|
|
3080
|
-
if (workspaceRef.current &&
|
|
3243
|
+
if (workspaceRef.current && LocalBlockly?.Xml) {
|
|
3081
3244
|
try {
|
|
3082
3245
|
const xml = LocalBlockly.Xml.workspaceToDom(workspaceRef.current);
|
|
3083
3246
|
return LocalBlockly.Xml.domToText(xml);
|
|
@@ -3090,7 +3253,6 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3090
3253
|
}
|
|
3091
3254
|
}));
|
|
3092
3255
|
const initializeWorkspace = useCallback(() => {
|
|
3093
|
-
var _a, _b;
|
|
3094
3256
|
const LocalBlockly = window.Blockly;
|
|
3095
3257
|
if (!isBlocklyReady || !blocklyDivRef.current || !LocalBlockly) {
|
|
3096
3258
|
console.warn("ScratchUI: Conditions not met for workspace initialization. isBlocklyReady:", isBlocklyReady, "blocklyDivRef.current:", !!blocklyDivRef.current, "LocalBlockly:", !!LocalBlockly);
|
|
@@ -3105,7 +3267,7 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3105
3267
|
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);
|
|
3106
3268
|
return;
|
|
3107
3269
|
}
|
|
3108
|
-
if (typeof
|
|
3270
|
+
if (typeof LocalBlockly.Blocks?.motion_movesteps === "undefined" || typeof LocalBlockly.Blocks?.event_whenflagclicked === "undefined") {
|
|
3109
3271
|
const availableBlocks = Object.keys(LocalBlockly.Blocks || {}).join(", ");
|
|
3110
3272
|
setComponentError(`ScratchUI: Essential Scratch block definitions (e.g., motion_movesteps, event_whenflagclicked) are missing before injection. Available blocks: ${availableBlocks}`);
|
|
3111
3273
|
return;
|
|
@@ -3199,7 +3361,7 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3199
3361
|
}
|
|
3200
3362
|
const handleResize = () => {
|
|
3201
3363
|
const LocalBlocklyResize = window.Blockly;
|
|
3202
|
-
if (workspaceRef.current &&
|
|
3364
|
+
if (workspaceRef.current && LocalBlocklyResize?.svgResize) {
|
|
3203
3365
|
LocalBlocklyResize.svgResize(workspaceRef.current);
|
|
3204
3366
|
}
|
|
3205
3367
|
};
|
|
@@ -3220,75 +3382,106 @@ var ScratchProgrammingQuestionUI = forwardRef(({
|
|
|
3220
3382
|
}, [isBlocklyReady, initializeWorkspace]);
|
|
3221
3383
|
const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
|
|
3222
3384
|
if (isLoadingScripts) {
|
|
3223
|
-
return /* @__PURE__ */
|
|
3385
|
+
return /* @__PURE__ */ 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__ */ jsxs("div", { className: "text-center", children: [
|
|
3386
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto mb-2" }),
|
|
3387
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Loading Scratch Assets..." })
|
|
3388
|
+
] }) });
|
|
3224
3389
|
}
|
|
3225
3390
|
if (componentError) {
|
|
3226
|
-
return /* @__PURE__ */
|
|
3391
|
+
return /* @__PURE__ */ 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: [
|
|
3392
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-lg mb-2", children: "Failed to load Scratch Workspace." }),
|
|
3393
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-3 text-center", children: componentError }),
|
|
3394
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mb-4 text-center", children: [
|
|
3395
|
+
"Please ensure all Scratch/Blockly JavaScript files are correctly copied to your",
|
|
3396
|
+
/* @__PURE__ */ jsx("code", { children: "public/static/scratch-blocks/js" }),
|
|
3397
|
+
" directory. Check browser console for more details.",
|
|
3398
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
3399
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
3400
|
+
"CRITICAL: Ensure you have copied the CSS files from ",
|
|
3401
|
+
/* @__PURE__ */ jsx("code", { children: "node_modules/scratch-blocks/css/" }),
|
|
3402
|
+
" (e.g., ",
|
|
3403
|
+
/* @__PURE__ */ jsx("code", { children: "vertical.css" }),
|
|
3404
|
+
") to ",
|
|
3405
|
+
/* @__PURE__ */ jsx("code", { children: "public/static/scratch-blocks/css/" }),
|
|
3406
|
+
" and linked it in your main layout. Without CSS, blocks will not render correctly."
|
|
3407
|
+
] })
|
|
3408
|
+
] }),
|
|
3409
|
+
/* @__PURE__ */ jsx(Button, { onClick: attemptLoadScripts, variant: "outline", children: "Try Reloading Scripts" })
|
|
3410
|
+
] });
|
|
3227
3411
|
}
|
|
3228
3412
|
if (!isBlocklyReady && !isLoadingScripts) {
|
|
3229
|
-
return /* @__PURE__ */
|
|
3413
|
+
return /* @__PURE__ */ 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__ */ jsx("p", { className: "text-muted-foreground", children: "Scratch environment did not initialize (Blockly not ready). Check console for script loading errors." }) });
|
|
3230
3414
|
}
|
|
3231
|
-
return /* @__PURE__ */
|
|
3232
|
-
"
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3415
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3416
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3417
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: question.prompt }),
|
|
3418
|
+
question.points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3419
|
+
"Points: ",
|
|
3420
|
+
question.points
|
|
3421
|
+
] })
|
|
3422
|
+
] }),
|
|
3423
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
|
|
3424
|
+
/* @__PURE__ */ jsx(
|
|
3425
|
+
"div",
|
|
3426
|
+
{
|
|
3427
|
+
ref: blocklyDivRef,
|
|
3428
|
+
style: {
|
|
3429
|
+
height: workspaceHeight,
|
|
3430
|
+
width: "100%",
|
|
3431
|
+
borderRadius: "0.375rem",
|
|
3432
|
+
backgroundColor: "hsl(var(--card))",
|
|
3433
|
+
position: "relative",
|
|
3434
|
+
userSelect: "none",
|
|
3435
|
+
overflow: "hidden",
|
|
3436
|
+
display: isLoadingScripts || componentError || !isBlocklyReady ? "none" : "block"
|
|
3437
|
+
},
|
|
3438
|
+
"aria-label": `Scratch programming workspace for question: ${question.prompt}`
|
|
3439
|
+
}
|
|
3440
|
+
),
|
|
3441
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3442
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3443
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-accent-foreground/80", children: question.explanation })
|
|
3444
|
+
] })
|
|
3445
|
+
] })
|
|
3446
|
+
] });
|
|
3248
3447
|
});
|
|
3249
3448
|
ScratchProgrammingQuestionUI.displayName = "ScratchProgrammingQuestionUI";
|
|
3250
3449
|
var Tabs = TabsPrimitive.Root;
|
|
3251
|
-
var TabsList =
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
className
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
);
|
|
3263
|
-
});
|
|
3450
|
+
var TabsList = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3451
|
+
TabsPrimitive.List,
|
|
3452
|
+
{
|
|
3453
|
+
ref,
|
|
3454
|
+
className: cn(
|
|
3455
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
3456
|
+
className
|
|
3457
|
+
),
|
|
3458
|
+
...props
|
|
3459
|
+
}
|
|
3460
|
+
));
|
|
3264
3461
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
3265
|
-
var TabsTrigger =
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
className
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
);
|
|
3277
|
-
});
|
|
3462
|
+
var TabsTrigger = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3463
|
+
TabsPrimitive.Trigger,
|
|
3464
|
+
{
|
|
3465
|
+
ref,
|
|
3466
|
+
className: cn(
|
|
3467
|
+
"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",
|
|
3468
|
+
className
|
|
3469
|
+
),
|
|
3470
|
+
...props
|
|
3471
|
+
}
|
|
3472
|
+
));
|
|
3278
3473
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
3279
|
-
var TabsContent =
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
className
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
);
|
|
3291
|
-
});
|
|
3474
|
+
var TabsContent = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3475
|
+
TabsPrimitive.Content,
|
|
3476
|
+
{
|
|
3477
|
+
ref,
|
|
3478
|
+
className: cn(
|
|
3479
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
3480
|
+
className
|
|
3481
|
+
),
|
|
3482
|
+
...props
|
|
3483
|
+
}
|
|
3484
|
+
));
|
|
3292
3485
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
3293
3486
|
var TOAST_LIMIT = 1;
|
|
3294
3487
|
var TOAST_REMOVE_DELAY = 1e6;
|
|
@@ -3314,15 +3507,17 @@ var addToRemoveQueue = (toastId) => {
|
|
|
3314
3507
|
var reducer = (state, action) => {
|
|
3315
3508
|
switch (action.type) {
|
|
3316
3509
|
case "ADD_TOAST":
|
|
3317
|
-
return
|
|
3510
|
+
return {
|
|
3511
|
+
...state,
|
|
3318
3512
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
|
3319
|
-
}
|
|
3513
|
+
};
|
|
3320
3514
|
case "UPDATE_TOAST":
|
|
3321
|
-
return
|
|
3515
|
+
return {
|
|
3516
|
+
...state,
|
|
3322
3517
|
toasts: state.toasts.map(
|
|
3323
|
-
(t) => t.id === action.toast.id ?
|
|
3518
|
+
(t) => t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
3324
3519
|
)
|
|
3325
|
-
}
|
|
3520
|
+
};
|
|
3326
3521
|
case "DISMISS_TOAST": {
|
|
3327
3522
|
const { toastId } = action;
|
|
3328
3523
|
if (toastId) {
|
|
@@ -3332,23 +3527,27 @@ var reducer = (state, action) => {
|
|
|
3332
3527
|
addToRemoveQueue(toast2.id);
|
|
3333
3528
|
});
|
|
3334
3529
|
}
|
|
3335
|
-
return
|
|
3530
|
+
return {
|
|
3531
|
+
...state,
|
|
3336
3532
|
toasts: state.toasts.map(
|
|
3337
|
-
(t) => t.id === toastId || toastId === void 0 ?
|
|
3533
|
+
(t) => t.id === toastId || toastId === void 0 ? {
|
|
3534
|
+
...t,
|
|
3338
3535
|
open: false
|
|
3339
|
-
}
|
|
3536
|
+
} : t
|
|
3340
3537
|
)
|
|
3341
|
-
}
|
|
3538
|
+
};
|
|
3342
3539
|
}
|
|
3343
3540
|
case "REMOVE_TOAST":
|
|
3344
3541
|
if (action.toastId === void 0) {
|
|
3345
|
-
return
|
|
3542
|
+
return {
|
|
3543
|
+
...state,
|
|
3346
3544
|
toasts: []
|
|
3347
|
-
}
|
|
3545
|
+
};
|
|
3348
3546
|
}
|
|
3349
|
-
return
|
|
3547
|
+
return {
|
|
3548
|
+
...state,
|
|
3350
3549
|
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
|
3351
|
-
}
|
|
3550
|
+
};
|
|
3352
3551
|
}
|
|
3353
3552
|
};
|
|
3354
3553
|
var listeners = [];
|
|
@@ -3359,23 +3558,23 @@ function dispatch(action) {
|
|
|
3359
3558
|
listener(memoryState);
|
|
3360
3559
|
});
|
|
3361
3560
|
}
|
|
3362
|
-
function toast(
|
|
3363
|
-
var props = __objRest(_a, []);
|
|
3561
|
+
function toast({ ...props }) {
|
|
3364
3562
|
const id = genId();
|
|
3365
3563
|
const update = (props2) => dispatch({
|
|
3366
3564
|
type: "UPDATE_TOAST",
|
|
3367
|
-
toast:
|
|
3565
|
+
toast: { ...props2, id }
|
|
3368
3566
|
});
|
|
3369
3567
|
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
3370
3568
|
dispatch({
|
|
3371
3569
|
type: "ADD_TOAST",
|
|
3372
|
-
toast:
|
|
3570
|
+
toast: {
|
|
3571
|
+
...props,
|
|
3373
3572
|
id,
|
|
3374
3573
|
open: true,
|
|
3375
3574
|
onOpenChange: (open) => {
|
|
3376
3575
|
if (!open) dismiss();
|
|
3377
3576
|
}
|
|
3378
|
-
}
|
|
3577
|
+
}
|
|
3379
3578
|
});
|
|
3380
3579
|
return {
|
|
3381
3580
|
id,
|
|
@@ -3384,8 +3583,8 @@ function toast(_a) {
|
|
|
3384
3583
|
};
|
|
3385
3584
|
}
|
|
3386
3585
|
function useToast() {
|
|
3387
|
-
const [state, setState] =
|
|
3388
|
-
|
|
3586
|
+
const [state, setState] = React9.useState(memoryState);
|
|
3587
|
+
React9.useEffect(() => {
|
|
3389
3588
|
listeners.push(setState);
|
|
3390
3589
|
return () => {
|
|
3391
3590
|
const index = listeners.indexOf(setState);
|
|
@@ -3394,13 +3593,12 @@ function useToast() {
|
|
|
3394
3593
|
}
|
|
3395
3594
|
};
|
|
3396
3595
|
}, [state]);
|
|
3397
|
-
return
|
|
3596
|
+
return {
|
|
3597
|
+
...state,
|
|
3398
3598
|
toast,
|
|
3399
3599
|
dismiss: (toastId) => dispatch({ type: "DISMISS_TOAST", toastId })
|
|
3400
|
-
}
|
|
3600
|
+
};
|
|
3401
3601
|
}
|
|
3402
|
-
|
|
3403
|
-
// src/react-ui/components/ui/CodingQuestionUI.tsx
|
|
3404
3602
|
var languageMap = {
|
|
3405
3603
|
cpp: cpp(),
|
|
3406
3604
|
javascript: javascript({ typescript: true }),
|
|
@@ -3447,28 +3645,67 @@ var CodingQuestionUI = ({
|
|
|
3447
3645
|
}
|
|
3448
3646
|
};
|
|
3449
3647
|
const langExtension = languageMap[question.codingLanguage];
|
|
3450
|
-
return /* @__PURE__ */
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3648
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full border-none shadow-none", children: [
|
|
3649
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "p-0 pb-4", children: [
|
|
3650
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: question.prompt }) }),
|
|
3651
|
+
question.points && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
|
|
3652
|
+
"Points: ",
|
|
3653
|
+
question.points
|
|
3654
|
+
] })
|
|
3655
|
+
] }),
|
|
3656
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "p-0 space-y-4", children: [
|
|
3657
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
3658
|
+
CodeMirror,
|
|
3659
|
+
{
|
|
3660
|
+
value: code,
|
|
3661
|
+
height: "300px",
|
|
3662
|
+
extensions: [langExtension],
|
|
3663
|
+
onChange: handleCodeChange,
|
|
3664
|
+
readOnly: showCorrectAnswer,
|
|
3665
|
+
theme: "dark"
|
|
3666
|
+
}
|
|
3667
|
+
) }),
|
|
3668
|
+
!showCorrectAnswer && /* @__PURE__ */ jsxs(Button, { onClick: handleRunPublicTests, disabled: isRunningTests, children: [
|
|
3669
|
+
isRunningTests ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "mr-2 h-4 w-4" }),
|
|
3670
|
+
isRunningTests ? "Running..." : "Run Public Tests"
|
|
3671
|
+
] }),
|
|
3672
|
+
/* @__PURE__ */ jsxs(Tabs, { defaultValue: "tests", className: "w-full", children: [
|
|
3673
|
+
/* @__PURE__ */ jsxs(TabsList, { children: [
|
|
3674
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "tests", children: "Test Cases" }),
|
|
3675
|
+
showCorrectAnswer && /* @__PURE__ */ jsx(TabsTrigger, { value: "solution", children: "Solution" })
|
|
3676
|
+
] }),
|
|
3677
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "tests", children: /* @__PURE__ */ jsx("div", { className: "space-y-2 p-2 border rounded-md min-h-[100px]", children: testResults.length > 0 ? testResults.map((result, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm", children: [
|
|
3678
|
+
result.passed ? /* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4 text-green-500 mr-2" }) : /* @__PURE__ */ jsx(XCircle, { className: "h-4 w-4 text-destructive mr-2" }),
|
|
3679
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
3680
|
+
"Test Case #",
|
|
3681
|
+
index + 1,
|
|
3682
|
+
": ",
|
|
3683
|
+
result.passed ? "Passed" : "Failed"
|
|
3684
|
+
] }),
|
|
3685
|
+
!result.passed && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
|
|
3686
|
+
"- ",
|
|
3687
|
+
result.reasoning
|
|
3688
|
+
] })
|
|
3689
|
+
] }, result.testCaseId)) : /* @__PURE__ */ 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." }) }) }),
|
|
3690
|
+
showCorrectAnswer && /* @__PURE__ */ jsx(TabsContent, { value: "solution", children: /* @__PURE__ */ jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
3691
|
+
CodeMirror,
|
|
3692
|
+
{
|
|
3693
|
+
value: question.solutionCode,
|
|
3694
|
+
height: "300px",
|
|
3695
|
+
extensions: [langExtension],
|
|
3696
|
+
readOnly: true,
|
|
3697
|
+
theme: "dark"
|
|
3698
|
+
}
|
|
3699
|
+
) }) })
|
|
3700
|
+
] }),
|
|
3701
|
+
showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
|
|
3702
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
|
|
3703
|
+
/* @__PURE__ */ jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
|
|
3704
|
+
] })
|
|
3705
|
+
] })
|
|
3706
|
+
] });
|
|
3470
3707
|
};
|
|
3471
|
-
var QuestionRenderer =
|
|
3708
|
+
var QuestionRenderer = React9__default.forwardRef(({
|
|
3472
3709
|
question,
|
|
3473
3710
|
onAnswerChange,
|
|
3474
3711
|
userAnswer,
|
|
@@ -3483,131 +3720,125 @@ var QuestionRenderer = React28__default.forwardRef(({
|
|
|
3483
3720
|
};
|
|
3484
3721
|
switch (question.questionType) {
|
|
3485
3722
|
case "multiple_choice":
|
|
3486
|
-
return /* @__PURE__ */
|
|
3723
|
+
return /* @__PURE__ */ jsx(MultipleChoiceQuestionUI, { ...commonProps, question });
|
|
3487
3724
|
case "true_false":
|
|
3488
|
-
return /* @__PURE__ */
|
|
3725
|
+
return /* @__PURE__ */ jsx(TrueFalseQuestionUI, { ...commonProps, question });
|
|
3489
3726
|
case "multiple_response":
|
|
3490
|
-
return /* @__PURE__ */
|
|
3727
|
+
return /* @__PURE__ */ jsx(MultipleResponseQuestionUI, { ...commonProps, question });
|
|
3491
3728
|
case "short_answer":
|
|
3492
|
-
return /* @__PURE__ */
|
|
3729
|
+
return /* @__PURE__ */ jsx(ShortAnswerQuestionUI, { ...commonProps, question });
|
|
3493
3730
|
case "numeric":
|
|
3494
|
-
return /* @__PURE__ */
|
|
3731
|
+
return /* @__PURE__ */ jsx(NumericQuestionUI, { ...commonProps, question });
|
|
3495
3732
|
case "fill_in_the_blanks":
|
|
3496
|
-
return /* @__PURE__ */
|
|
3733
|
+
return /* @__PURE__ */ jsx(FillInTheBlanksQuestionUI, { ...commonProps, question });
|
|
3497
3734
|
case "sequence":
|
|
3498
|
-
return /* @__PURE__ */
|
|
3735
|
+
return /* @__PURE__ */ jsx(SequenceQuestionUI, { ...commonProps, question });
|
|
3499
3736
|
case "matching":
|
|
3500
|
-
return /* @__PURE__ */
|
|
3737
|
+
return /* @__PURE__ */ jsx(MatchingQuestionUI, { ...commonProps, question });
|
|
3501
3738
|
case "drag_and_drop":
|
|
3502
|
-
return /* @__PURE__ */
|
|
3739
|
+
return /* @__PURE__ */ jsx(DragAndDropQuestionUI, { ...commonProps, question });
|
|
3503
3740
|
case "hotspot":
|
|
3504
|
-
return /* @__PURE__ */
|
|
3741
|
+
return /* @__PURE__ */ jsx(HotspotQuestionUI, { ...commonProps, question });
|
|
3505
3742
|
case "blockly_programming":
|
|
3506
|
-
return /* @__PURE__ */
|
|
3743
|
+
return /* @__PURE__ */ jsx(BlocklyProgrammingQuestionUI, { ...commonProps, question, ref });
|
|
3507
3744
|
case "scratch_programming":
|
|
3508
|
-
return /* @__PURE__ */
|
|
3745
|
+
return /* @__PURE__ */ jsx(ScratchProgrammingQuestionUI, { ...commonProps, question, ref });
|
|
3509
3746
|
case "coding":
|
|
3510
|
-
return /* @__PURE__ */
|
|
3747
|
+
return /* @__PURE__ */ jsx(CodingQuestionUI, { ...commonProps, question });
|
|
3511
3748
|
default:
|
|
3512
|
-
return /* @__PURE__ */
|
|
3749
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-4 border border-destructive bg-destructive/10 rounded-md", children: [
|
|
3750
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-destructive", children: t("unsupportedQuestionType") }),
|
|
3751
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("unsupportedQuestionTypeDescription") }),
|
|
3752
|
+
/* @__PURE__ */ jsx("pre", { className: "mt-2 p-2 bg-muted rounded text-xs font-code overflow-x-auto", children: JSON.stringify(question, null, 2) })
|
|
3753
|
+
] });
|
|
3513
3754
|
}
|
|
3514
3755
|
});
|
|
3515
3756
|
QuestionRenderer.displayName = "QuestionRenderer";
|
|
3516
|
-
var Progress =
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
className
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
}, props),
|
|
3527
|
-
/* @__PURE__ */ React28.createElement(
|
|
3757
|
+
var Progress = React9.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3758
|
+
ProgressPrimitive.Root,
|
|
3759
|
+
{
|
|
3760
|
+
ref,
|
|
3761
|
+
className: cn(
|
|
3762
|
+
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
|
3763
|
+
className
|
|
3764
|
+
),
|
|
3765
|
+
...props,
|
|
3766
|
+
children: /* @__PURE__ */ jsx(
|
|
3528
3767
|
ProgressPrimitive.Indicator,
|
|
3529
3768
|
{
|
|
3530
3769
|
className: "h-full w-full flex-1 bg-primary transition-all",
|
|
3531
3770
|
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
3532
3771
|
}
|
|
3533
3772
|
)
|
|
3534
|
-
|
|
3535
|
-
|
|
3773
|
+
}
|
|
3774
|
+
));
|
|
3536
3775
|
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
3537
3776
|
var Accordion = AccordionPrimitive.Root;
|
|
3538
|
-
var AccordionItem =
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
);
|
|
3547
|
-
});
|
|
3777
|
+
var AccordionItem = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3778
|
+
AccordionPrimitive.Item,
|
|
3779
|
+
{
|
|
3780
|
+
ref,
|
|
3781
|
+
className: cn("border-b", className),
|
|
3782
|
+
...props
|
|
3783
|
+
}
|
|
3784
|
+
));
|
|
3548
3785
|
AccordionItem.displayName = "AccordionItem";
|
|
3549
|
-
var AccordionTrigger =
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
className
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
});
|
|
3786
|
+
var AccordionTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(AccordionPrimitive.Header, { className: "flex", children: /* @__PURE__ */ jsxs(
|
|
3787
|
+
AccordionPrimitive.Trigger,
|
|
3788
|
+
{
|
|
3789
|
+
ref,
|
|
3790
|
+
className: cn(
|
|
3791
|
+
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
3792
|
+
className
|
|
3793
|
+
),
|
|
3794
|
+
...props,
|
|
3795
|
+
children: [
|
|
3796
|
+
children,
|
|
3797
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200" })
|
|
3798
|
+
]
|
|
3799
|
+
}
|
|
3800
|
+
) }));
|
|
3564
3801
|
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
3565
|
-
var AccordionContent =
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
);
|
|
3575
|
-
});
|
|
3802
|
+
var AccordionContent = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3803
|
+
AccordionPrimitive.Content,
|
|
3804
|
+
{
|
|
3805
|
+
ref,
|
|
3806
|
+
className: "overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
|
3807
|
+
...props,
|
|
3808
|
+
children: /* @__PURE__ */ jsx("div", { className: cn("pb-4 pt-0", className), children })
|
|
3809
|
+
}
|
|
3810
|
+
));
|
|
3576
3811
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
3577
|
-
var ScrollArea =
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3812
|
+
var ScrollArea = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
3813
|
+
ScrollAreaPrimitive.Root,
|
|
3814
|
+
{
|
|
3815
|
+
ref,
|
|
3816
|
+
className: cn("relative overflow-hidden", className),
|
|
3817
|
+
...props,
|
|
3818
|
+
children: [
|
|
3819
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
|
|
3820
|
+
/* @__PURE__ */ jsx(ScrollBar, {}),
|
|
3821
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
|
|
3822
|
+
]
|
|
3823
|
+
}
|
|
3824
|
+
));
|
|
3590
3825
|
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
3591
|
-
var ScrollBar =
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
);
|
|
3607
|
-
});
|
|
3826
|
+
var ScrollBar = React9.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3827
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
3828
|
+
{
|
|
3829
|
+
ref,
|
|
3830
|
+
orientation,
|
|
3831
|
+
className: cn(
|
|
3832
|
+
"flex touch-none select-none transition-colors",
|
|
3833
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
3834
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
3835
|
+
className
|
|
3836
|
+
),
|
|
3837
|
+
...props,
|
|
3838
|
+
children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
|
|
3839
|
+
}
|
|
3840
|
+
));
|
|
3608
3841
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
3609
|
-
|
|
3610
|
-
// src/react-ui/components/ui/QuizResult.tsx
|
|
3611
3842
|
var QuizResult = ({
|
|
3612
3843
|
result,
|
|
3613
3844
|
quizTitle,
|
|
@@ -3616,7 +3847,6 @@ var QuizResult = ({
|
|
|
3616
3847
|
showReviewButton = false,
|
|
3617
3848
|
isReviewLoading = false
|
|
3618
3849
|
}) => {
|
|
3619
|
-
var _a, _b, _c, _d;
|
|
3620
3850
|
const { t } = useTranslation();
|
|
3621
3851
|
const getAnswerDisplay = (answer) => {
|
|
3622
3852
|
if (answer === null || answer === void 0) return t("practiceFlow.results.notAnswered");
|
|
@@ -3635,24 +3865,150 @@ var QuizResult = ({
|
|
|
3635
3865
|
}
|
|
3636
3866
|
return String(answer);
|
|
3637
3867
|
};
|
|
3638
|
-
return /* @__PURE__ */
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
{
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3868
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
|
|
3869
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
3870
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-3xl font-headline text-center", children: t("practiceFlow.results.title", { quizTitle }) }),
|
|
3871
|
+
/* @__PURE__ */ jsx(CardDescription, { className: "text-center text-lg", children: t("practiceFlow.results.description") })
|
|
3872
|
+
] }),
|
|
3873
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-6", children: [
|
|
3874
|
+
/* @__PURE__ */ jsxs(Card, { className: "bg-secondary/50", children: [
|
|
3875
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-xl flex items-center", children: [
|
|
3876
|
+
/* @__PURE__ */ jsx(BarChart2, { className: "mr-2 h-5 w-5 text-primary" }),
|
|
3877
|
+
t("practiceFlow.results.overallScore")
|
|
3878
|
+
] }) }),
|
|
3879
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center", children: [
|
|
3880
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3881
|
+
/* @__PURE__ */ jsxs("p", { className: "text-3xl font-bold text-primary", children: [
|
|
3882
|
+
result.score,
|
|
3883
|
+
" / ",
|
|
3884
|
+
result.maxScore
|
|
3885
|
+
] }),
|
|
3886
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.points") })
|
|
3887
|
+
] }),
|
|
3888
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3889
|
+
/* @__PURE__ */ jsxs("p", { className: "text-3xl font-bold text-primary", children: [
|
|
3890
|
+
result.percentage.toFixed(2),
|
|
3891
|
+
"%"
|
|
3892
|
+
] }),
|
|
3893
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.percentage") })
|
|
3894
|
+
] }),
|
|
3895
|
+
/* @__PURE__ */ jsx("div", { children: result.passed !== void 0 && (result.passed ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center text-green-600", children: [
|
|
3896
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "h-10 w-10" }),
|
|
3897
|
+
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.passed") })
|
|
3898
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center text-destructive", children: [
|
|
3899
|
+
/* @__PURE__ */ jsx(XCircle, { className: "h-10 w-10" }),
|
|
3900
|
+
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.failed") })
|
|
3901
|
+
] })) })
|
|
3902
|
+
] })
|
|
3903
|
+
] }),
|
|
3904
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm", children: [
|
|
3905
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
|
|
3906
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-5 w-5 text-primary" }),
|
|
3907
|
+
/* @__PURE__ */ jsx("span", { children: t("practiceFlow.results.timeSpent") }),
|
|
3908
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold", children: [
|
|
3909
|
+
result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A",
|
|
3910
|
+
" ",
|
|
3911
|
+
t("practiceFlow.results.timeUnit")
|
|
3912
|
+
] })
|
|
3913
|
+
] }),
|
|
3914
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
|
|
3915
|
+
/* @__PURE__ */ jsx(Percent, { className: "h-5 w-5 text-primary" }),
|
|
3916
|
+
/* @__PURE__ */ jsx("span", { children: t("practiceFlow.results.avgTimePerQuestion") }),
|
|
3917
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold", children: [
|
|
3918
|
+
result.averageTimePerQuestionSeconds?.toFixed(1) ?? "N/A",
|
|
3919
|
+
" ",
|
|
3920
|
+
t("practiceFlow.results.timeUnit")
|
|
3921
|
+
] })
|
|
3922
|
+
] })
|
|
3923
|
+
] }),
|
|
3924
|
+
result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ jsxs(Card, { children: [
|
|
3925
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-lg", children: "SCORM Sync Status" }) }),
|
|
3926
|
+
/* @__PURE__ */ jsxs(CardContent, { children: [
|
|
3927
|
+
/* @__PURE__ */ jsxs("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
3928
|
+
result.scormStatus === "error" && /* @__PURE__ */ jsx(AlertTriangle, { className: "mr-2 h-4 w-4" }),
|
|
3929
|
+
"Status: ",
|
|
3930
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold ml-1", children: result.scormStatus })
|
|
3931
|
+
] }),
|
|
3932
|
+
result.scormError && /* @__PURE__ */ jsxs("p", { className: "text-xs text-destructive mt-1", children: [
|
|
3933
|
+
"Details: ",
|
|
3934
|
+
result.scormError
|
|
3935
|
+
] })
|
|
3936
|
+
] })
|
|
3937
|
+
] }),
|
|
3938
|
+
result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ jsxs(Card, { children: [
|
|
3939
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-lg", children: "Webhook Sync Status" }) }),
|
|
3940
|
+
/* @__PURE__ */ jsxs(CardContent, { children: [
|
|
3941
|
+
/* @__PURE__ */ jsxs("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
3942
|
+
result.webhookStatus === "error" && /* @__PURE__ */ jsx(AlertTriangle, { className: "mr-2 h-4 w-4" }),
|
|
3943
|
+
"Status: ",
|
|
3944
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold ml-1", children: result.webhookStatus })
|
|
3945
|
+
] }),
|
|
3946
|
+
result.webhookError && /* @__PURE__ */ jsxs("p", { className: "text-xs text-destructive mt-1", children: [
|
|
3947
|
+
"Details: ",
|
|
3948
|
+
result.webhookError
|
|
3949
|
+
] })
|
|
3950
|
+
] })
|
|
3951
|
+
] }),
|
|
3952
|
+
/* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "question-breakdown", children: [
|
|
3953
|
+
/* @__PURE__ */ jsx(AccordionTrigger, { className: "text-lg font-semibold", children: t("practiceFlow.results.questionBreakdown") }),
|
|
3954
|
+
/* @__PURE__ */ jsx(AccordionContent, { children: /* @__PURE__ */ jsx(ScrollArea, { className: "h-[300px] pr-4", children: /* @__PURE__ */ jsx("ul", { className: "space-y-4", children: result.questionResults.map((qResult, index) => /* @__PURE__ */ jsxs("li", { className: "p-4 border rounded-md bg-background", children: [
|
|
3955
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-2", children: [
|
|
3956
|
+
/* @__PURE__ */ jsx("h4", { className: "font-semibold", children: t("common.questions", { count: index + 1 }) }),
|
|
3957
|
+
qResult.isCorrect ? /* @__PURE__ */ jsxs("span", { className: "text-green-600 font-medium flex items-center", children: [
|
|
3958
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "mr-1 h-4 w-4" }),
|
|
3959
|
+
" ",
|
|
3960
|
+
t("practiceFlow.results.passed")
|
|
3961
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: "text-destructive font-medium flex items-center", children: [
|
|
3962
|
+
/* @__PURE__ */ jsx(XCircle, { className: "mr-1 h-4 w-4" }),
|
|
3963
|
+
" ",
|
|
3964
|
+
t("practiceFlow.results.failed")
|
|
3965
|
+
] })
|
|
3966
|
+
] }),
|
|
3967
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
3968
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: t("practiceFlow.results.yourAnswer") }),
|
|
3969
|
+
" ",
|
|
3970
|
+
getAnswerDisplay(qResult.userAnswer)
|
|
3971
|
+
] }),
|
|
3972
|
+
!qResult.isCorrect && /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
3973
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: t("practiceFlow.results.correctAnswer") }),
|
|
3974
|
+
" ",
|
|
3975
|
+
getAnswerDisplay(qResult.correctAnswer)
|
|
3976
|
+
] }),
|
|
3977
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
3978
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: t("practiceFlow.results.pointsEarned") }),
|
|
3979
|
+
" ",
|
|
3980
|
+
qResult.pointsEarned
|
|
3981
|
+
] }),
|
|
3982
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
3983
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: t("practiceFlow.results.timeSpent") }),
|
|
3984
|
+
" ",
|
|
3985
|
+
qResult.timeSpentSeconds?.toFixed(0) ?? "N/A",
|
|
3986
|
+
t("practiceFlow.results.timeUnit")
|
|
3987
|
+
] })
|
|
3988
|
+
] }, qResult.questionId)) }) }) })
|
|
3989
|
+
] }) })
|
|
3990
|
+
] }),
|
|
3991
|
+
/* @__PURE__ */ jsxs(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2", children: [
|
|
3992
|
+
onExitQuiz && /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto", children: [
|
|
3993
|
+
/* @__PURE__ */ jsx(LogOut, { className: "mr-2 h-4 w-4" }),
|
|
3994
|
+
t("common.exit")
|
|
3995
|
+
] }),
|
|
3996
|
+
showReviewButton && onGenerateReview && /* @__PURE__ */ jsxs(
|
|
3997
|
+
Button,
|
|
3998
|
+
{
|
|
3999
|
+
onClick: onGenerateReview,
|
|
4000
|
+
disabled: isReviewLoading,
|
|
4001
|
+
className: "w-full sm:w-auto",
|
|
4002
|
+
children: [
|
|
4003
|
+
isReviewLoading ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Wand2, { className: "mr-2 h-4 w-4" }),
|
|
4004
|
+
isReviewLoading ? t("practiceFlow.results.generatingReview") : t("practiceFlow.results.generateReview")
|
|
4005
|
+
]
|
|
4006
|
+
}
|
|
4007
|
+
)
|
|
4008
|
+
] })
|
|
4009
|
+
] });
|
|
3651
4010
|
};
|
|
3652
|
-
|
|
3653
|
-
// src/react-ui/components/ui/QuizPlayer.tsx
|
|
3654
4011
|
var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
3655
|
-
var _a;
|
|
3656
4012
|
const [engine, setEngine] = useState(null);
|
|
3657
4013
|
const [currentQuestion, setCurrentQuestion] = useState(null);
|
|
3658
4014
|
const [currentQuestionNumber, setCurrentQuestionNumber] = useState(0);
|
|
@@ -3676,11 +4032,10 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3676
4032
|
setIsLoading(false);
|
|
3677
4033
|
},
|
|
3678
4034
|
onQuestionChange: (question, qNum, total) => {
|
|
3679
|
-
var _a2;
|
|
3680
4035
|
setCurrentQuestion(question);
|
|
3681
4036
|
setCurrentQuestionNumber(qNum);
|
|
3682
4037
|
setTotalQuestions(total);
|
|
3683
|
-
const existingAnswer =
|
|
4038
|
+
const existingAnswer = engineRef.current?.getUserAnswer(question?.id || "");
|
|
3684
4039
|
setUserAnswer(existingAnswer !== void 0 ? existingAnswer : null);
|
|
3685
4040
|
},
|
|
3686
4041
|
onQuizFinish: (results) => {
|
|
@@ -3697,10 +4052,10 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3697
4052
|
}
|
|
3698
4053
|
}), [onQuizComplete]);
|
|
3699
4054
|
const handleAnswerChange = useCallback((answer) => {
|
|
3700
|
-
if (
|
|
4055
|
+
if (currentQuestion?.questionType !== "blockly_programming" && currentQuestion?.questionType !== "scratch_programming") {
|
|
3701
4056
|
setUserAnswer(answer);
|
|
3702
4057
|
}
|
|
3703
|
-
}, [currentQuestion
|
|
4058
|
+
}, [currentQuestion?.questionType]);
|
|
3704
4059
|
const quizConfigKey = useMemo(() => {
|
|
3705
4060
|
return JSON.stringify({
|
|
3706
4061
|
id: quizConfig.id,
|
|
@@ -3745,7 +4100,6 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3745
4100
|
};
|
|
3746
4101
|
}, [quizConfigKey, callbacks, quizConfig]);
|
|
3747
4102
|
const handleSubmitAnswer = useCallback(() => {
|
|
3748
|
-
var _a2;
|
|
3749
4103
|
const currentEngine = engineRef.current;
|
|
3750
4104
|
if (!currentEngine || !currentQuestion) return;
|
|
3751
4105
|
let answerToSubmit = null;
|
|
@@ -3753,7 +4107,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3753
4107
|
if (programmingQuestionRef.current && typeof programmingQuestionRef.current.getWorkspaceXml === "function") {
|
|
3754
4108
|
answerToSubmit = programmingQuestionRef.current.getWorkspaceXml();
|
|
3755
4109
|
} else {
|
|
3756
|
-
answerToSubmit =
|
|
4110
|
+
answerToSubmit = currentEngine.getUserAnswer(currentQuestion.id) ?? null;
|
|
3757
4111
|
}
|
|
3758
4112
|
} else {
|
|
3759
4113
|
answerToSubmit = userAnswer;
|
|
@@ -3796,53 +4150,104 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
|
|
|
3796
4150
|
return `${mins}:${secs < 10 ? "0" : ""}${secs}`;
|
|
3797
4151
|
}, []);
|
|
3798
4152
|
if (isLoading) {
|
|
3799
|
-
return /* @__PURE__ */
|
|
4153
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-64", children: [
|
|
4154
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-12 w-12 animate-spin text-primary" }),
|
|
4155
|
+
/* @__PURE__ */ jsx("p", { className: "mt-4 text-muted-foreground", children: t("common.loading") })
|
|
4156
|
+
] });
|
|
3800
4157
|
}
|
|
3801
4158
|
if (error) {
|
|
3802
|
-
return /* @__PURE__ */
|
|
4159
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
|
|
4160
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-destructive flex items-center", children: [
|
|
4161
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "mr-2 h-6 w-6" }),
|
|
4162
|
+
"Quiz Error"
|
|
4163
|
+
] }) }),
|
|
4164
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { children: error }) }),
|
|
4165
|
+
/* @__PURE__ */ jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
|
|
4166
|
+
/* @__PURE__ */ jsx(LogOut, { className: "mr-2 h-4 w-4" }),
|
|
4167
|
+
" ",
|
|
4168
|
+
t("common.exit")
|
|
4169
|
+
] }) })
|
|
4170
|
+
] });
|
|
3803
4171
|
}
|
|
3804
4172
|
if (quizFinished && finalResult) {
|
|
3805
|
-
return /* @__PURE__ */
|
|
4173
|
+
return /* @__PURE__ */ jsx(QuizResult, { result: finalResult, onExitQuiz, quizTitle: quizConfig.title });
|
|
3806
4174
|
}
|
|
3807
4175
|
if (!currentQuestion) {
|
|
3808
|
-
return /* @__PURE__ */
|
|
4176
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
|
|
4177
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
4178
|
+
/* @__PURE__ */ jsx(CardTitle, { children: "Quiz Ended" }),
|
|
4179
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "No more questions, or quiz not loaded correctly." })
|
|
4180
|
+
] }),
|
|
4181
|
+
/* @__PURE__ */ jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
|
|
4182
|
+
/* @__PURE__ */ jsx(LogOut, { className: "mr-2 h-4 w-4" }),
|
|
4183
|
+
" ",
|
|
4184
|
+
t("common.exit")
|
|
4185
|
+
] }) })
|
|
4186
|
+
] });
|
|
3809
4187
|
}
|
|
3810
|
-
return /* @__PURE__ */
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
4188
|
+
return /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
|
|
4189
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
4190
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
4191
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-2xl font-headline", children: quizConfig.title }),
|
|
4192
|
+
timeLeft !== null && /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-muted-foreground", children: [
|
|
4193
|
+
/* @__PURE__ */ jsx(Clock, { className: "mr-1 h-4 w-4" }),
|
|
4194
|
+
t("practiceFlow.player.timeLeft", { time: formatTime(timeLeft) })
|
|
4195
|
+
] })
|
|
4196
|
+
] }),
|
|
4197
|
+
quizConfig.description && /* @__PURE__ */ jsx(CardDescription, { children: quizConfig.description }),
|
|
4198
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
|
|
4199
|
+
/* @__PURE__ */ jsx(
|
|
4200
|
+
Progress,
|
|
4201
|
+
{
|
|
4202
|
+
value: progressPercent,
|
|
4203
|
+
"aria-label": `Quiz progress: ${currentQuestionNumber} of ${totalQuestions} questions`,
|
|
4204
|
+
className: "w-full"
|
|
4205
|
+
}
|
|
4206
|
+
),
|
|
4207
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1 text-right", children: t("practiceFlow.player.questionProgress", { current: currentQuestionNumber, total: totalQuestions }) })
|
|
4208
|
+
] })
|
|
4209
|
+
] }),
|
|
4210
|
+
/* @__PURE__ */ jsx(CardContent, { className: "min-h-[200px]", children: /* @__PURE__ */ jsx(
|
|
4211
|
+
QuestionRenderer,
|
|
4212
|
+
{
|
|
4213
|
+
question: currentQuestion,
|
|
4214
|
+
onAnswerChange: handleAnswerChange,
|
|
4215
|
+
userAnswer,
|
|
4216
|
+
showCorrectAnswer: quizConfig.settings?.showCorrectAnswers === "immediately",
|
|
4217
|
+
ref: currentQuestion.questionType === "blockly_programming" || currentQuestion.questionType === "scratch_programming" ? programmingQuestionRef : null
|
|
4218
|
+
},
|
|
4219
|
+
currentQuestion.id
|
|
4220
|
+
) }),
|
|
4221
|
+
/* @__PURE__ */ jsxs(CardFooter, { className: "flex justify-between items-center", children: [
|
|
4222
|
+
/* @__PURE__ */ jsxs(
|
|
4223
|
+
Button,
|
|
4224
|
+
{
|
|
4225
|
+
variant: "outline",
|
|
4226
|
+
onClick: handlePrevious,
|
|
4227
|
+
disabled: currentQuestionNumber <= 1,
|
|
4228
|
+
children: [
|
|
4229
|
+
/* @__PURE__ */ jsx(ChevronLeft, { className: "mr-2 h-4 w-4" }),
|
|
4230
|
+
" ",
|
|
4231
|
+
t("common.previous")
|
|
4232
|
+
]
|
|
4233
|
+
}
|
|
4234
|
+
),
|
|
4235
|
+
onExitQuiz && /* @__PURE__ */ jsx(
|
|
4236
|
+
Button,
|
|
4237
|
+
{
|
|
4238
|
+
variant: "ghost",
|
|
4239
|
+
onClick: onExitQuiz,
|
|
4240
|
+
className: "text-muted-foreground hover:text-destructive",
|
|
4241
|
+
children: t("common.exit")
|
|
4242
|
+
}
|
|
4243
|
+
),
|
|
4244
|
+
/* @__PURE__ */ jsxs(Button, { onClick: handleNext, children: [
|
|
4245
|
+
currentQuestionNumber === totalQuestions ? t("practiceFlow.player.finishQuiz") : t("common.next"),
|
|
4246
|
+
currentQuestionNumber !== totalQuestions && /* @__PURE__ */ jsx(ChevronRight, { className: "ml-2 h-4 w-4" }),
|
|
4247
|
+
currentQuestionNumber === totalQuestions && /* @__PURE__ */ jsx(CheckCircle, { className: "ml-2 h-4 w-4" })
|
|
4248
|
+
] })
|
|
4249
|
+
] })
|
|
4250
|
+
] });
|
|
3846
4251
|
};
|
|
3847
4252
|
|
|
3848
4253
|
// src/player.ts
|
|
@@ -3871,20 +4276,20 @@ function mountQuizPlayer(targetElementId, quizConfig) {
|
|
|
3871
4276
|
}
|
|
3872
4277
|
};
|
|
3873
4278
|
if (quizResult) {
|
|
3874
|
-
return
|
|
4279
|
+
return React9__default.createElement(QuizResult, {
|
|
3875
4280
|
result: quizResult,
|
|
3876
4281
|
quizTitle: quizConfig.title,
|
|
3877
4282
|
onExitQuiz: handleExit
|
|
3878
4283
|
});
|
|
3879
4284
|
}
|
|
3880
|
-
return
|
|
4285
|
+
return React9__default.createElement(QuizPlayer, {
|
|
3881
4286
|
quizConfig,
|
|
3882
4287
|
onQuizComplete: handleQuizComplete,
|
|
3883
4288
|
onExitQuiz: handleExit
|
|
3884
4289
|
});
|
|
3885
4290
|
};
|
|
3886
4291
|
const root = ReactDOM.createRoot(targetElement);
|
|
3887
|
-
root.render(
|
|
4292
|
+
root.render(React9__default.createElement(React9__default.StrictMode, null, React9__default.createElement(AppContainer)));
|
|
3888
4293
|
}
|
|
3889
4294
|
|
|
3890
4295
|
export { mountQuizPlayer };
|