@schoolio/player 1.4.2 → 1.4.4
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/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +461 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +461 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -39,6 +39,10 @@ interface QuizAnswerDetail {
|
|
|
39
39
|
isCorrect: boolean;
|
|
40
40
|
explanation?: string;
|
|
41
41
|
hint?: string;
|
|
42
|
+
items?: string[];
|
|
43
|
+
correctOrder?: number[];
|
|
44
|
+
leftItems?: string[];
|
|
45
|
+
rightItems?: string[];
|
|
42
46
|
}
|
|
43
47
|
type AttemptStatus = "in_progress" | "completed" | "abandoned";
|
|
44
48
|
interface ExternalQuizAttempt {
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,10 @@ interface QuizAnswerDetail {
|
|
|
39
39
|
isCorrect: boolean;
|
|
40
40
|
explanation?: string;
|
|
41
41
|
hint?: string;
|
|
42
|
+
items?: string[];
|
|
43
|
+
correctOrder?: number[];
|
|
44
|
+
leftItems?: string[];
|
|
45
|
+
rightItems?: string[];
|
|
42
46
|
}
|
|
43
47
|
type AttemptStatus = "in_progress" | "completed" | "abandoned";
|
|
44
48
|
interface ExternalQuizAttempt {
|
package/dist/index.js
CHANGED
|
@@ -191,15 +191,16 @@ function checkAnswer(question, selectedAnswer) {
|
|
|
191
191
|
return { isCorrect, pointsEarned: isCorrect ? points : 0 };
|
|
192
192
|
}
|
|
193
193
|
case "essay":
|
|
194
|
-
case "assessment":
|
|
195
194
|
return { isCorrect: false, pointsEarned: 0 };
|
|
195
|
+
case "assessment":
|
|
196
|
+
return { isCorrect: true, pointsEarned: points };
|
|
196
197
|
default:
|
|
197
198
|
return { isCorrect: false, pointsEarned: 0 };
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
function createAnswerDetail(question, selectedAnswer) {
|
|
201
202
|
const { isCorrect, pointsEarned } = checkAnswer(question, selectedAnswer);
|
|
202
|
-
|
|
203
|
+
const detail = {
|
|
203
204
|
questionId: question.id,
|
|
204
205
|
questionText: question.question,
|
|
205
206
|
questionType: question.type,
|
|
@@ -211,6 +212,14 @@ function createAnswerDetail(question, selectedAnswer) {
|
|
|
211
212
|
explanation: question.explanation,
|
|
212
213
|
hint: question.hint
|
|
213
214
|
};
|
|
215
|
+
if (question.type === "sorting") {
|
|
216
|
+
detail.items = question.items;
|
|
217
|
+
detail.correctOrder = question.correctOrder;
|
|
218
|
+
} else if (question.type === "matrix") {
|
|
219
|
+
detail.leftItems = question.leftItems;
|
|
220
|
+
detail.rightItems = question.rightItems;
|
|
221
|
+
}
|
|
222
|
+
return detail;
|
|
214
223
|
}
|
|
215
224
|
function calculateScore(answers) {
|
|
216
225
|
const totalPoints = answers.reduce((sum, a) => sum + a.points, 0);
|
|
@@ -1054,12 +1063,14 @@ var defaultStyles = {
|
|
|
1054
1063
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1055
1064
|
width: "100%",
|
|
1056
1065
|
height: "100%",
|
|
1066
|
+
maxHeight: "calc(100vh - 40px)",
|
|
1057
1067
|
padding: "20px",
|
|
1058
1068
|
backgroundColor: "#ffffff",
|
|
1059
1069
|
borderRadius: "12px",
|
|
1060
1070
|
boxSizing: "border-box",
|
|
1061
1071
|
display: "flex",
|
|
1062
|
-
flexDirection: "column"
|
|
1072
|
+
flexDirection: "column",
|
|
1073
|
+
overflow: "hidden"
|
|
1063
1074
|
},
|
|
1064
1075
|
header: {
|
|
1065
1076
|
marginBottom: "20px",
|
|
@@ -1086,8 +1097,7 @@ var defaultStyles = {
|
|
|
1086
1097
|
color: "#6b7280"
|
|
1087
1098
|
},
|
|
1088
1099
|
progressBar: {
|
|
1089
|
-
width: "
|
|
1090
|
-
maxWidth: "300px",
|
|
1100
|
+
width: "100%",
|
|
1091
1101
|
height: "8px",
|
|
1092
1102
|
backgroundColor: "#e5e7eb",
|
|
1093
1103
|
borderRadius: "4px",
|
|
@@ -1114,6 +1124,7 @@ var defaultStyles = {
|
|
|
1114
1124
|
gap: "8px"
|
|
1115
1125
|
},
|
|
1116
1126
|
option: {
|
|
1127
|
+
width: "100%",
|
|
1117
1128
|
padding: "12px 16px",
|
|
1118
1129
|
border: "2px solid #e5e7eb",
|
|
1119
1130
|
borderRadius: "8px",
|
|
@@ -1123,7 +1134,8 @@ var defaultStyles = {
|
|
|
1123
1134
|
boxShadow: "none",
|
|
1124
1135
|
backgroundColor: "#ffffff",
|
|
1125
1136
|
WebkitTapHighlightColor: "transparent",
|
|
1126
|
-
userSelect: "none"
|
|
1137
|
+
userSelect: "none",
|
|
1138
|
+
boxSizing: "border-box"
|
|
1127
1139
|
},
|
|
1128
1140
|
optionSelected: {
|
|
1129
1141
|
borderColor: "#6721b0",
|
|
@@ -1205,18 +1217,22 @@ var defaultStyles = {
|
|
|
1205
1217
|
gap: "24px",
|
|
1206
1218
|
flex: 1,
|
|
1207
1219
|
minHeight: 0,
|
|
1208
|
-
alignItems: "stretch"
|
|
1220
|
+
alignItems: "stretch",
|
|
1221
|
+
overflow: "hidden"
|
|
1209
1222
|
},
|
|
1210
1223
|
quizContent: {
|
|
1211
1224
|
flex: 1,
|
|
1212
1225
|
minWidth: 0,
|
|
1226
|
+
minHeight: 0,
|
|
1213
1227
|
overflow: "auto"
|
|
1214
1228
|
},
|
|
1215
1229
|
chatPanel: {
|
|
1216
1230
|
width: "320px",
|
|
1217
1231
|
flexShrink: 0,
|
|
1218
1232
|
display: "flex",
|
|
1219
|
-
flexDirection: "column"
|
|
1233
|
+
flexDirection: "column",
|
|
1234
|
+
minHeight: 0,
|
|
1235
|
+
overflow: "hidden"
|
|
1220
1236
|
},
|
|
1221
1237
|
timer: {
|
|
1222
1238
|
fontSize: "14px",
|
|
@@ -1342,6 +1358,13 @@ var defaultStyles = {
|
|
|
1342
1358
|
backgroundColor: "#fef2f2",
|
|
1343
1359
|
borderColor: "#ef4444"
|
|
1344
1360
|
},
|
|
1361
|
+
feedbackNeutral: {
|
|
1362
|
+
backgroundColor: "#f0f9ff",
|
|
1363
|
+
borderColor: "#0ea5e9"
|
|
1364
|
+
},
|
|
1365
|
+
feedbackTitleNeutral: {
|
|
1366
|
+
color: "#0369a1"
|
|
1367
|
+
},
|
|
1345
1368
|
feedbackTitle: {
|
|
1346
1369
|
fontSize: "16px",
|
|
1347
1370
|
fontWeight: "600",
|
|
@@ -1362,6 +1385,290 @@ var defaultStyles = {
|
|
|
1362
1385
|
lineHeight: "1.5"
|
|
1363
1386
|
}
|
|
1364
1387
|
};
|
|
1388
|
+
function SortingDragDrop({ items, currentOrder, correctOrder, showFeedback, onOrderChange }) {
|
|
1389
|
+
const [draggedIndex, setDraggedIndex] = (0, import_react3.useState)(null);
|
|
1390
|
+
const [dragOverIndex, setDragOverIndex] = (0, import_react3.useState)(null);
|
|
1391
|
+
const handleDragStart = (e, position) => {
|
|
1392
|
+
if (showFeedback) return;
|
|
1393
|
+
setDraggedIndex(position);
|
|
1394
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1395
|
+
e.dataTransfer.setData("text/plain", position.toString());
|
|
1396
|
+
};
|
|
1397
|
+
const handleDragOver = (e, position) => {
|
|
1398
|
+
e.preventDefault();
|
|
1399
|
+
if (showFeedback) return;
|
|
1400
|
+
e.dataTransfer.dropEffect = "move";
|
|
1401
|
+
setDragOverIndex(position);
|
|
1402
|
+
};
|
|
1403
|
+
const handleDragLeave = () => {
|
|
1404
|
+
setDragOverIndex(null);
|
|
1405
|
+
};
|
|
1406
|
+
const handleDrop = (e, toPosition) => {
|
|
1407
|
+
e.preventDefault();
|
|
1408
|
+
if (showFeedback) return;
|
|
1409
|
+
const fromPosition = parseInt(e.dataTransfer.getData("text/plain"), 10);
|
|
1410
|
+
if (fromPosition !== toPosition) {
|
|
1411
|
+
const newOrder = [...currentOrder];
|
|
1412
|
+
const [movedItem] = newOrder.splice(fromPosition, 1);
|
|
1413
|
+
newOrder.splice(toPosition, 0, movedItem);
|
|
1414
|
+
onOrderChange(newOrder);
|
|
1415
|
+
}
|
|
1416
|
+
setDraggedIndex(null);
|
|
1417
|
+
setDragOverIndex(null);
|
|
1418
|
+
};
|
|
1419
|
+
const handleDragEnd = () => {
|
|
1420
|
+
setDraggedIndex(null);
|
|
1421
|
+
setDragOverIndex(null);
|
|
1422
|
+
};
|
|
1423
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentOrder.map((itemIndex, position) => {
|
|
1424
|
+
const isCorrectPosition = correctOrder?.[position] === itemIndex;
|
|
1425
|
+
const isDragging = draggedIndex === position;
|
|
1426
|
+
const isDragOver = dragOverIndex === position;
|
|
1427
|
+
let itemStyle = {
|
|
1428
|
+
...defaultStyles.option,
|
|
1429
|
+
display: "flex",
|
|
1430
|
+
alignItems: "center",
|
|
1431
|
+
gap: "12px",
|
|
1432
|
+
cursor: showFeedback ? "default" : "grab",
|
|
1433
|
+
opacity: isDragging ? 0.5 : 1,
|
|
1434
|
+
transition: "all 0.2s ease",
|
|
1435
|
+
transform: isDragOver && !showFeedback ? "scale(1.02)" : "scale(1)"
|
|
1436
|
+
};
|
|
1437
|
+
if (showFeedback) {
|
|
1438
|
+
if (isCorrectPosition) {
|
|
1439
|
+
itemStyle = { ...itemStyle, ...defaultStyles.optionCorrect };
|
|
1440
|
+
} else {
|
|
1441
|
+
itemStyle = { ...itemStyle, ...defaultStyles.optionIncorrect };
|
|
1442
|
+
}
|
|
1443
|
+
} else if (isDragOver) {
|
|
1444
|
+
itemStyle = { ...itemStyle, borderColor: "#6366f1", backgroundColor: "#eef2ff" };
|
|
1445
|
+
}
|
|
1446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1447
|
+
"div",
|
|
1448
|
+
{
|
|
1449
|
+
style: itemStyle,
|
|
1450
|
+
"data-testid": `sorting-item-${position}`,
|
|
1451
|
+
draggable: !showFeedback,
|
|
1452
|
+
onDragStart: (e) => handleDragStart(e, position),
|
|
1453
|
+
onDragOver: (e) => handleDragOver(e, position),
|
|
1454
|
+
onDragLeave: handleDragLeave,
|
|
1455
|
+
onDrop: (e) => handleDrop(e, position),
|
|
1456
|
+
onDragEnd: handleDragEnd,
|
|
1457
|
+
children: [
|
|
1458
|
+
!showFeedback && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1459
|
+
cursor: "grab",
|
|
1460
|
+
padding: "4px",
|
|
1461
|
+
display: "flex",
|
|
1462
|
+
alignItems: "center",
|
|
1463
|
+
color: "#9ca3af"
|
|
1464
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "5", r: "1" }),
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "19", r: "1" }),
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "5", r: "1" }),
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
|
|
1470
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "19", r: "1" })
|
|
1471
|
+
] }) }),
|
|
1472
|
+
showFeedback && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1473
|
+
display: "flex",
|
|
1474
|
+
alignItems: "center",
|
|
1475
|
+
color: isCorrectPosition ? "#22c55e" : "#ef4444"
|
|
1476
|
+
}, children: isCorrectPosition ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "22 4 12 14.01 9 11.01" })
|
|
1479
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1480
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
|
|
1483
|
+
] }) }),
|
|
1484
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: items[itemIndex] }),
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: items[itemIndex], size: "sm" }) })
|
|
1486
|
+
]
|
|
1487
|
+
},
|
|
1488
|
+
itemIndex
|
|
1489
|
+
);
|
|
1490
|
+
}) });
|
|
1491
|
+
}
|
|
1492
|
+
function MatchingDragDrop({ leftItems, rightItems, currentMatches, correctMatches, showFeedback, onMatchChange }) {
|
|
1493
|
+
const [draggedItem, setDraggedItem] = (0, import_react3.useState)(null);
|
|
1494
|
+
const [dragOverLeft, setDragOverLeft] = (0, import_react3.useState)(null);
|
|
1495
|
+
const matchedRightItems = Object.values(currentMatches);
|
|
1496
|
+
const unmatchedRightItems = rightItems.filter((item) => !matchedRightItems.includes(item));
|
|
1497
|
+
const handleDragStart = (e, rightItem) => {
|
|
1498
|
+
if (showFeedback) return;
|
|
1499
|
+
setDraggedItem(rightItem);
|
|
1500
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1501
|
+
e.dataTransfer.setData("text/plain", rightItem);
|
|
1502
|
+
};
|
|
1503
|
+
const handleDragOver = (e, leftItem) => {
|
|
1504
|
+
e.preventDefault();
|
|
1505
|
+
if (showFeedback) return;
|
|
1506
|
+
e.dataTransfer.dropEffect = "move";
|
|
1507
|
+
setDragOverLeft(leftItem);
|
|
1508
|
+
};
|
|
1509
|
+
const handleDragLeave = () => {
|
|
1510
|
+
setDragOverLeft(null);
|
|
1511
|
+
};
|
|
1512
|
+
const handleDrop = (e, leftItem) => {
|
|
1513
|
+
e.preventDefault();
|
|
1514
|
+
if (showFeedback) return;
|
|
1515
|
+
const rightItem = e.dataTransfer.getData("text/plain");
|
|
1516
|
+
if (rightItem) {
|
|
1517
|
+
const newMatches = { ...currentMatches };
|
|
1518
|
+
Object.keys(newMatches).forEach((key) => {
|
|
1519
|
+
if (newMatches[key] === rightItem) {
|
|
1520
|
+
delete newMatches[key];
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
newMatches[leftItem] = rightItem;
|
|
1524
|
+
onMatchChange(newMatches);
|
|
1525
|
+
}
|
|
1526
|
+
setDraggedItem(null);
|
|
1527
|
+
setDragOverLeft(null);
|
|
1528
|
+
};
|
|
1529
|
+
const handleDragEnd = () => {
|
|
1530
|
+
setDraggedItem(null);
|
|
1531
|
+
setDragOverLeft(null);
|
|
1532
|
+
};
|
|
1533
|
+
const handleClearMatch = (leftItem) => {
|
|
1534
|
+
if (showFeedback) return;
|
|
1535
|
+
const newMatches = { ...currentMatches };
|
|
1536
|
+
delete newMatches[leftItem];
|
|
1537
|
+
onMatchChange(newMatches);
|
|
1538
|
+
};
|
|
1539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "24px", flexWrap: "wrap" }, children: [
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, minWidth: "200px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: "14px", fontWeight: "600", color: "#6b7280", marginBottom: "4px" }, children: "Match these items:" }),
|
|
1542
|
+
leftItems.map((leftItem, idx) => {
|
|
1543
|
+
const matchedRight = currentMatches[leftItem];
|
|
1544
|
+
const correctMatch = correctMatches?.[leftItem];
|
|
1545
|
+
const isCorrect = matchedRight === correctMatch;
|
|
1546
|
+
const isDragOver = dragOverLeft === leftItem;
|
|
1547
|
+
let rowStyle = {
|
|
1548
|
+
display: "flex",
|
|
1549
|
+
alignItems: "center",
|
|
1550
|
+
gap: "12px",
|
|
1551
|
+
padding: "12px 16px",
|
|
1552
|
+
border: "2px dashed #e5e7eb",
|
|
1553
|
+
borderRadius: "8px",
|
|
1554
|
+
backgroundColor: "#ffffff",
|
|
1555
|
+
minHeight: "56px",
|
|
1556
|
+
transition: "all 0.2s ease"
|
|
1557
|
+
};
|
|
1558
|
+
if (showFeedback) {
|
|
1559
|
+
rowStyle.borderStyle = "solid";
|
|
1560
|
+
if (isCorrect) {
|
|
1561
|
+
rowStyle = { ...rowStyle, borderColor: "#22c55e", backgroundColor: "#f0fdf4" };
|
|
1562
|
+
} else {
|
|
1563
|
+
rowStyle = { ...rowStyle, borderColor: "#ef4444", backgroundColor: "#fef2f2" };
|
|
1564
|
+
}
|
|
1565
|
+
} else if (isDragOver) {
|
|
1566
|
+
rowStyle = { ...rowStyle, borderColor: "#6366f1", backgroundColor: "#eef2ff", borderStyle: "solid" };
|
|
1567
|
+
} else if (matchedRight) {
|
|
1568
|
+
rowStyle = { ...rowStyle, borderStyle: "solid", borderColor: "#22c55e" };
|
|
1569
|
+
}
|
|
1570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1571
|
+
"div",
|
|
1572
|
+
{
|
|
1573
|
+
style: rowStyle,
|
|
1574
|
+
"data-testid": `matrix-row-${idx}`,
|
|
1575
|
+
onDragOver: (e) => handleDragOver(e, leftItem),
|
|
1576
|
+
onDragLeave: handleDragLeave,
|
|
1577
|
+
onDrop: (e) => handleDrop(e, leftItem),
|
|
1578
|
+
children: [
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1, fontWeight: "500" }, children: leftItem }),
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#6b7280" }, children: "\u2192" }),
|
|
1581
|
+
matchedRight ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1582
|
+
display: "flex",
|
|
1583
|
+
alignItems: "center",
|
|
1584
|
+
gap: "8px",
|
|
1585
|
+
padding: "6px 12px",
|
|
1586
|
+
backgroundColor: showFeedback ? isCorrect ? "#dcfce7" : "#fee2e2" : "#e0e7ff",
|
|
1587
|
+
borderRadius: "6px",
|
|
1588
|
+
fontSize: "14px"
|
|
1589
|
+
}, children: [
|
|
1590
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: matchedRight }),
|
|
1591
|
+
!showFeedback && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1592
|
+
"button",
|
|
1593
|
+
{
|
|
1594
|
+
onClick: () => handleClearMatch(leftItem),
|
|
1595
|
+
style: {
|
|
1596
|
+
background: "none",
|
|
1597
|
+
border: "none",
|
|
1598
|
+
cursor: "pointer",
|
|
1599
|
+
padding: "2px",
|
|
1600
|
+
display: "flex",
|
|
1601
|
+
color: "#6b7280"
|
|
1602
|
+
},
|
|
1603
|
+
"aria-label": "Remove match",
|
|
1604
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1606
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1607
|
+
] })
|
|
1608
|
+
}
|
|
1609
|
+
),
|
|
1610
|
+
showFeedback && (isCorrect ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#22c55e", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#ef4444", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1613
|
+
] }))
|
|
1614
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#9ca3af", fontSize: "14px", fontStyle: "italic" }, children: "Drop here" }),
|
|
1615
|
+
showFeedback && !isCorrect && correctMatch && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { color: "#166534", fontSize: "13px", marginLeft: "8px" }, children: [
|
|
1616
|
+
"(Correct: ",
|
|
1617
|
+
correctMatch,
|
|
1618
|
+
")"
|
|
1619
|
+
] })
|
|
1620
|
+
]
|
|
1621
|
+
},
|
|
1622
|
+
idx
|
|
1623
|
+
);
|
|
1624
|
+
})
|
|
1625
|
+
] }),
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, minWidth: "200px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
|
|
1627
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: "14px", fontWeight: "600", color: "#6b7280", marginBottom: "4px" }, children: "Drag to match:" }),
|
|
1628
|
+
unmatchedRightItems.length > 0 ? unmatchedRightItems.map((rightItem, idx) => {
|
|
1629
|
+
const isDragging = draggedItem === rightItem;
|
|
1630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1631
|
+
"div",
|
|
1632
|
+
{
|
|
1633
|
+
style: {
|
|
1634
|
+
padding: "12px 16px",
|
|
1635
|
+
border: "2px solid #e5e7eb",
|
|
1636
|
+
borderRadius: "8px",
|
|
1637
|
+
backgroundColor: "#ffffff",
|
|
1638
|
+
cursor: showFeedback ? "default" : "grab",
|
|
1639
|
+
opacity: isDragging ? 0.5 : 1,
|
|
1640
|
+
display: "flex",
|
|
1641
|
+
alignItems: "center",
|
|
1642
|
+
gap: "8px",
|
|
1643
|
+
transition: "all 0.2s ease"
|
|
1644
|
+
},
|
|
1645
|
+
draggable: !showFeedback,
|
|
1646
|
+
onDragStart: (e) => handleDragStart(e, rightItem),
|
|
1647
|
+
onDragEnd: handleDragEnd,
|
|
1648
|
+
"data-testid": `draggable-right-${idx}`,
|
|
1649
|
+
children: [
|
|
1650
|
+
!showFeedback && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#9ca3af", display: "flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "5", r: "1" }),
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "9", cy: "19", r: "1" }),
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "5", r: "1" }),
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
|
|
1656
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "15", cy: "19", r: "1" })
|
|
1657
|
+
] }) }),
|
|
1658
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: rightItem })
|
|
1659
|
+
]
|
|
1660
|
+
},
|
|
1661
|
+
idx
|
|
1662
|
+
);
|
|
1663
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1664
|
+
padding: "16px",
|
|
1665
|
+
textAlign: "center",
|
|
1666
|
+
color: "#22c55e",
|
|
1667
|
+
fontSize: "14px"
|
|
1668
|
+
}, children: "All items matched!" })
|
|
1669
|
+
] })
|
|
1670
|
+
] });
|
|
1671
|
+
}
|
|
1365
1672
|
function Spinner({ size = 16, color = "#ffffff" }) {
|
|
1366
1673
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1367
1674
|
"span",
|
|
@@ -2238,14 +2545,123 @@ function QuizPlayer({
|
|
|
2238
2545
|
},
|
|
2239
2546
|
idx
|
|
2240
2547
|
)) }),
|
|
2548
|
+
currentQuestion.type === "sorting" && currentQuestion.items && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2549
|
+
SortingDragDrop,
|
|
2550
|
+
{
|
|
2551
|
+
items: currentQuestion.items,
|
|
2552
|
+
currentOrder: Array.isArray(selectedAnswer) ? selectedAnswer : currentQuestion.items.map((_, i) => i),
|
|
2553
|
+
correctOrder: currentQuestion.correctOrder,
|
|
2554
|
+
showFeedback,
|
|
2555
|
+
onOrderChange: handleAnswerChange
|
|
2556
|
+
}
|
|
2557
|
+
),
|
|
2558
|
+
currentQuestion.type === "matrix" && currentQuestion.leftItems && currentQuestion.rightItems && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2559
|
+
MatchingDragDrop,
|
|
2560
|
+
{
|
|
2561
|
+
leftItems: currentQuestion.leftItems,
|
|
2562
|
+
rightItems: currentQuestion.rightItems,
|
|
2563
|
+
currentMatches: typeof selectedAnswer === "object" && selectedAnswer !== null && !Array.isArray(selectedAnswer) ? selectedAnswer : {},
|
|
2564
|
+
correctMatches: currentQuestion.correctMatches,
|
|
2565
|
+
showFeedback,
|
|
2566
|
+
onMatchChange: handleAnswerChange
|
|
2567
|
+
}
|
|
2568
|
+
),
|
|
2569
|
+
currentQuestion.type === "assessment" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: (() => {
|
|
2570
|
+
const scaleType = currentQuestion.scaleType || "likert";
|
|
2571
|
+
if (scaleType === "yes-no") {
|
|
2572
|
+
const options = ["Yes", "No"];
|
|
2573
|
+
return options.map((option, idx) => {
|
|
2574
|
+
const isSelected = selectedAnswer === option;
|
|
2575
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2576
|
+
if (isSelected) {
|
|
2577
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2578
|
+
}
|
|
2579
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2580
|
+
"div",
|
|
2581
|
+
{
|
|
2582
|
+
style: {
|
|
2583
|
+
...optionStyle,
|
|
2584
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
2585
|
+
display: "flex",
|
|
2586
|
+
alignItems: "center",
|
|
2587
|
+
gap: "8px"
|
|
2588
|
+
},
|
|
2589
|
+
onClick: () => !showFeedback && handleAnswerChange(option),
|
|
2590
|
+
"data-testid": `assessment-option-${option.toLowerCase()}`,
|
|
2591
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
2592
|
+
},
|
|
2593
|
+
idx
|
|
2594
|
+
);
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
if (scaleType === "rating") {
|
|
2598
|
+
const min = currentQuestion.scaleMin || 1;
|
|
2599
|
+
const max = currentQuestion.scaleMax || 5;
|
|
2600
|
+
const ratings = Array.from({ length: max - min + 1 }, (_, i) => min + i);
|
|
2601
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", justifyContent: "center" }, children: ratings.map((rating) => {
|
|
2602
|
+
const isSelected = selectedAnswer === rating;
|
|
2603
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2604
|
+
"button",
|
|
2605
|
+
{
|
|
2606
|
+
onClick: () => !showFeedback && handleAnswerChange(rating),
|
|
2607
|
+
disabled: showFeedback,
|
|
2608
|
+
style: {
|
|
2609
|
+
width: "48px",
|
|
2610
|
+
height: "48px",
|
|
2611
|
+
borderRadius: "50%",
|
|
2612
|
+
border: isSelected ? "2px solid #6721b0" : "2px solid #e5e7eb",
|
|
2613
|
+
backgroundColor: isSelected ? "#f3e8ff" : "#ffffff",
|
|
2614
|
+
cursor: showFeedback ? "not-allowed" : "pointer",
|
|
2615
|
+
fontSize: "18px",
|
|
2616
|
+
fontWeight: "600",
|
|
2617
|
+
color: isSelected ? "#6721b0" : "#374151"
|
|
2618
|
+
},
|
|
2619
|
+
"data-testid": `assessment-rating-${rating}`,
|
|
2620
|
+
children: rating
|
|
2621
|
+
},
|
|
2622
|
+
rating
|
|
2623
|
+
);
|
|
2624
|
+
}) });
|
|
2625
|
+
}
|
|
2626
|
+
const likertOptions = [
|
|
2627
|
+
"Strongly Disagree",
|
|
2628
|
+
"Disagree",
|
|
2629
|
+
"Neutral",
|
|
2630
|
+
"Agree",
|
|
2631
|
+
"Strongly Agree"
|
|
2632
|
+
];
|
|
2633
|
+
return likertOptions.map((option, idx) => {
|
|
2634
|
+
const isSelected = selectedAnswer === option;
|
|
2635
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2636
|
+
if (isSelected) {
|
|
2637
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2638
|
+
}
|
|
2639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2640
|
+
"div",
|
|
2641
|
+
{
|
|
2642
|
+
style: {
|
|
2643
|
+
...optionStyle,
|
|
2644
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
2645
|
+
display: "flex",
|
|
2646
|
+
alignItems: "center",
|
|
2647
|
+
gap: "8px"
|
|
2648
|
+
},
|
|
2649
|
+
onClick: () => !showFeedback && handleAnswerChange(option),
|
|
2650
|
+
"data-testid": `assessment-likert-${idx}`,
|
|
2651
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
2652
|
+
},
|
|
2653
|
+
idx
|
|
2654
|
+
);
|
|
2655
|
+
});
|
|
2656
|
+
})() }),
|
|
2241
2657
|
showFeedback && currentAnswerDetail && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
2242
2658
|
...defaultStyles.feedback,
|
|
2243
|
-
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
|
|
2659
|
+
...currentQuestion.type === "assessment" ? defaultStyles.feedbackNeutral : currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
|
|
2244
2660
|
}, children: [
|
|
2245
2661
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
2246
2662
|
...defaultStyles.feedbackTitle,
|
|
2247
|
-
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
|
|
2248
|
-
}, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
|
|
2663
|
+
...currentQuestion.type === "assessment" ? defaultStyles.feedbackTitleNeutral : currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
|
|
2664
|
+
}, children: currentQuestion.type === "assessment" ? "\u2713 Response recorded" : currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
|
|
2249
2665
|
currentQuestion.explanation && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
|
|
2250
2666
|
] })
|
|
2251
2667
|
] }),
|
|
@@ -2567,7 +2983,7 @@ function QuizPlayer({
|
|
|
2567
2983
|
lessonId,
|
|
2568
2984
|
courseId,
|
|
2569
2985
|
answerResult: showFeedback && currentAnswerDetail ? {
|
|
2570
|
-
wasIncorrect: !currentAnswerDetail.isCorrect,
|
|
2986
|
+
wasIncorrect: currentQuestion.type !== "assessment" && !currentAnswerDetail.isCorrect,
|
|
2571
2987
|
selectedAnswer: typeof selectedAnswer === "string" ? selectedAnswer : Array.isArray(selectedAnswer) ? selectedAnswer.join(", ") : void 0,
|
|
2572
2988
|
correctAnswer: typeof currentQuestion.correctAnswer === "string" ? currentQuestion.correctAnswer : Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer.join(", ") : void 0,
|
|
2573
2989
|
explanation: currentQuestion.explanation
|
|
@@ -2775,10 +3191,31 @@ var spinnerKeyframes = `
|
|
|
2775
3191
|
to { transform: rotate(360deg); }
|
|
2776
3192
|
}
|
|
2777
3193
|
`;
|
|
2778
|
-
function formatAnswer(answer) {
|
|
3194
|
+
function formatAnswer(answer, questionType, items, leftItems) {
|
|
2779
3195
|
if (answer === null || answer === void 0) {
|
|
2780
3196
|
return "No answer";
|
|
2781
3197
|
}
|
|
3198
|
+
if (questionType === "sorting" && Array.isArray(answer)) {
|
|
3199
|
+
const indices = answer;
|
|
3200
|
+
if (items && items.length > 0) {
|
|
3201
|
+
return indices.map((idx, pos) => `${pos + 1}. ${items[idx] ?? `Item ${idx + 1}`}`).join(", ");
|
|
3202
|
+
}
|
|
3203
|
+
return indices.map((idx, pos) => `Position ${pos + 1}: Item ${idx + 1}`).join(", ");
|
|
3204
|
+
}
|
|
3205
|
+
if (questionType === "matrix" && typeof answer === "object" && !Array.isArray(answer)) {
|
|
3206
|
+
const matches = answer;
|
|
3207
|
+
if (leftItems && leftItems.length > 0) {
|
|
3208
|
+
return leftItems.map((left) => {
|
|
3209
|
+
const right = matches[left];
|
|
3210
|
+
return `${left} \u2192 ${right || "No answer"}`;
|
|
3211
|
+
}).join(", ");
|
|
3212
|
+
}
|
|
3213
|
+
const entries = Object.entries(matches);
|
|
3214
|
+
if (entries.length === 0) {
|
|
3215
|
+
return "No answer";
|
|
3216
|
+
}
|
|
3217
|
+
return entries.map(([left, right]) => `${left} \u2192 ${right || "No answer"}`).join(", ");
|
|
3218
|
+
}
|
|
2782
3219
|
if (typeof answer === "string") {
|
|
2783
3220
|
return answer;
|
|
2784
3221
|
}
|
|
@@ -2790,6 +3227,15 @@ function formatAnswer(answer) {
|
|
|
2790
3227
|
}
|
|
2791
3228
|
return String(answer);
|
|
2792
3229
|
}
|
|
3230
|
+
function formatCorrectSortingAnswer(items, correctOrder) {
|
|
3231
|
+
return correctOrder.map((idx, pos) => `${pos + 1}. ${items[idx] ?? `Item ${idx + 1}`}`).join(", ");
|
|
3232
|
+
}
|
|
3233
|
+
function formatCorrectMatrixAnswer(correctMatches, leftItems) {
|
|
3234
|
+
if (leftItems && leftItems.length > 0) {
|
|
3235
|
+
return leftItems.map((left) => `${left} \u2192 ${correctMatches[left] || "N/A"}`).join(", ");
|
|
3236
|
+
}
|
|
3237
|
+
return Object.entries(correctMatches).map(([left, right]) => `${left} \u2192 ${right}`).join(", ");
|
|
3238
|
+
}
|
|
2793
3239
|
function AttemptViewer({
|
|
2794
3240
|
attemptId,
|
|
2795
3241
|
apiBaseUrl,
|
|
@@ -2926,11 +3372,11 @@ function AttemptViewer({
|
|
|
2926
3372
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.questionText, children: answer.questionText }),
|
|
2927
3373
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.answerSection, children: [
|
|
2928
3374
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.answerLabel, children: "Your answer:" }),
|
|
2929
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer) })
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer, answer.questionType, answer.items, answer.leftItems) })
|
|
2930
3376
|
] }),
|
|
2931
3377
|
!answer.isCorrect && answer.correctAnswer && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.answerSection, children: [
|
|
2932
3378
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.answerLabel, children: "Correct answer:" }),
|
|
2933
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.correctAnswer, children: formatAnswer(answer.correctAnswer) })
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.correctAnswer, children: answer.questionType === "sorting" && answer.items && answer.correctOrder ? formatCorrectSortingAnswer(answer.items, answer.correctOrder) : answer.questionType === "matrix" && answer.correctAnswer && typeof answer.correctAnswer === "object" && !Array.isArray(answer.correctAnswer) ? formatCorrectMatrixAnswer(answer.correctAnswer, answer.leftItems) : formatAnswer(answer.correctAnswer, answer.questionType, answer.items, answer.leftItems) })
|
|
2934
3380
|
] }),
|
|
2935
3381
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.points, children: [
|
|
2936
3382
|
answer.pointsEarned,
|