@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 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
- return {
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: "50%",
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,