@schoolio/player 1.4.3 → 1.4.5

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.js CHANGED
@@ -21,13 +21,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AttemptViewer: () => AttemptViewer,
24
+ ERROR_DEFINITIONS: () => ERROR_DEFINITIONS,
25
+ ErrorLogsPanel: () => ErrorLogsPanel,
26
+ ErrorTypesPanel: () => ErrorTypesPanel,
27
+ MaintenanceScreen: () => MaintenanceScreen,
24
28
  QuizApiClient: () => QuizApiClient,
25
29
  QuizPlayer: () => QuizPlayer,
26
30
  TextToSpeech: () => TextToSpeech,
27
31
  calculateScore: () => calculateScore,
28
32
  checkAnswer: () => checkAnswer,
29
33
  createAnswerDetail: () => createAnswerDetail,
30
- formatTime: () => formatTime
34
+ formatTime: () => formatTime,
35
+ getErrorFromHttpStatus: () => getErrorFromHttpStatus,
36
+ getErrorFromMessage: () => getErrorFromMessage
31
37
  });
32
38
  module.exports = __toCommonJS(index_exports);
33
39
 
@@ -136,6 +142,13 @@ var QuizApiClient = class {
136
142
  }
137
143
  return response.blob();
138
144
  }
145
+ async logError(params) {
146
+ try {
147
+ await this.request("POST", "/api/external/log-error", params);
148
+ } catch (e) {
149
+ console.warn("[QuizEngine] Failed to log error:", e);
150
+ }
151
+ }
139
152
  };
140
153
 
141
154
  // src/utils.ts
@@ -191,15 +204,16 @@ function checkAnswer(question, selectedAnswer) {
191
204
  return { isCorrect, pointsEarned: isCorrect ? points : 0 };
192
205
  }
193
206
  case "essay":
194
- case "assessment":
195
207
  return { isCorrect: false, pointsEarned: 0 };
208
+ case "assessment":
209
+ return { isCorrect: true, pointsEarned: points };
196
210
  default:
197
211
  return { isCorrect: false, pointsEarned: 0 };
198
212
  }
199
213
  }
200
214
  function createAnswerDetail(question, selectedAnswer) {
201
215
  const { isCorrect, pointsEarned } = checkAnswer(question, selectedAnswer);
202
- return {
216
+ const detail = {
203
217
  questionId: question.id,
204
218
  questionText: question.question,
205
219
  questionType: question.type,
@@ -211,6 +225,14 @@ function createAnswerDetail(question, selectedAnswer) {
211
225
  explanation: question.explanation,
212
226
  hint: question.hint
213
227
  };
228
+ if (question.type === "sorting") {
229
+ detail.items = question.items;
230
+ detail.correctOrder = question.correctOrder;
231
+ } else if (question.type === "matrix") {
232
+ detail.leftItems = question.leftItems;
233
+ detail.rightItems = question.rightItems;
234
+ }
235
+ return detail;
214
236
  }
215
237
  function calculateScore(answers) {
216
238
  const totalPoints = answers.reduce((sum, a) => sum + a.points, 0);
@@ -1047,8 +1069,191 @@ function QuestionChatPanel({
1047
1069
  ] });
1048
1070
  }
1049
1071
 
1050
- // src/QuizPlayer.tsx
1072
+ // src/errors.ts
1073
+ var ERROR_DEFINITIONS = {
1074
+ QUIZ_NOT_FOUND: {
1075
+ code: "QUIZ_NOT_FOUND",
1076
+ userMessage: "We couldn't find this quiz",
1077
+ subMessage: "The quiz may have been removed or the link is incorrect.",
1078
+ cause: "The quiz ID does not exist in the database, or the quiz has been deleted.",
1079
+ isBlocking: true
1080
+ },
1081
+ ATTEMPT_NOT_FOUND: {
1082
+ code: "ATTEMPT_NOT_FOUND",
1083
+ userMessage: "We couldn't find this quiz attempt",
1084
+ subMessage: "The attempt may have expired or the link is incorrect.",
1085
+ cause: "The attempt ID does not exist, or the attempt has been deleted/archived.",
1086
+ isBlocking: true
1087
+ },
1088
+ QUIZ_EXPIRED: {
1089
+ code: "QUIZ_EXPIRED",
1090
+ userMessage: "This quiz has expired",
1091
+ subMessage: "The deadline for this quiz has passed.",
1092
+ cause: "The quiz end date/time has passed and submissions are no longer accepted.",
1093
+ isBlocking: true
1094
+ },
1095
+ QUIZ_NOT_STARTED: {
1096
+ code: "QUIZ_NOT_STARTED",
1097
+ userMessage: "This quiz is not available yet",
1098
+ subMessage: "Please check back when the quiz opens.",
1099
+ cause: "The quiz start date/time has not yet been reached.",
1100
+ isBlocking: true
1101
+ },
1102
+ NETWORK_ERROR: {
1103
+ code: "NETWORK_ERROR",
1104
+ userMessage: "Connection problem",
1105
+ subMessage: "Please check your internet connection and try again.",
1106
+ cause: "Unable to reach the server due to network connectivity issues.",
1107
+ isBlocking: true
1108
+ },
1109
+ SERVER_ERROR: {
1110
+ code: "SERVER_ERROR",
1111
+ userMessage: "Something went wrong on our end",
1112
+ subMessage: "Our team has been notified. Please try again later.",
1113
+ cause: "The server encountered an internal error (HTTP 500+).",
1114
+ isBlocking: true
1115
+ },
1116
+ UNAUTHORIZED: {
1117
+ code: "UNAUTHORIZED",
1118
+ userMessage: "Please sign in to continue",
1119
+ subMessage: "You need to be logged in to access this quiz.",
1120
+ cause: "The user is not authenticated (HTTP 401).",
1121
+ isBlocking: true
1122
+ },
1123
+ FORBIDDEN: {
1124
+ code: "FORBIDDEN",
1125
+ userMessage: "You don't have access to this quiz",
1126
+ subMessage: "Contact your instructor if you believe this is a mistake.",
1127
+ cause: "The user does not have permission to access this resource (HTTP 403).",
1128
+ isBlocking: true
1129
+ },
1130
+ TTS_FAILED: {
1131
+ code: "TTS_FAILED",
1132
+ userMessage: "Text-to-speech unavailable",
1133
+ subMessage: "Audio features are temporarily unavailable.",
1134
+ cause: "The text-to-speech service failed or is unreachable.",
1135
+ isBlocking: false
1136
+ },
1137
+ CHAT_FAILED: {
1138
+ code: "CHAT_FAILED",
1139
+ userMessage: "Chat assistance unavailable",
1140
+ subMessage: "The AI helper is temporarily unavailable.",
1141
+ cause: "The chat/AI service failed to initialize or respond.",
1142
+ isBlocking: false
1143
+ },
1144
+ SUBMISSION_FAILED: {
1145
+ code: "SUBMISSION_FAILED",
1146
+ userMessage: "Failed to submit your answer",
1147
+ subMessage: "Please try again. Your progress has been saved.",
1148
+ cause: "The answer submission request failed due to network or server issues.",
1149
+ isBlocking: false
1150
+ },
1151
+ UNKNOWN_ERROR: {
1152
+ code: "UNKNOWN_ERROR",
1153
+ userMessage: "Something unexpected happened",
1154
+ subMessage: "Please try refreshing the page.",
1155
+ cause: "An unclassified error occurred.",
1156
+ isBlocking: true
1157
+ }
1158
+ };
1159
+ function getErrorFromHttpStatus(status, context) {
1160
+ switch (status) {
1161
+ case 401:
1162
+ return "UNAUTHORIZED";
1163
+ case 403:
1164
+ return "FORBIDDEN";
1165
+ case 404:
1166
+ return context === "attempt" ? "ATTEMPT_NOT_FOUND" : "QUIZ_NOT_FOUND";
1167
+ case 410:
1168
+ return "QUIZ_EXPIRED";
1169
+ case 500:
1170
+ case 502:
1171
+ case 503:
1172
+ case 504:
1173
+ return "SERVER_ERROR";
1174
+ default:
1175
+ if (status >= 400 && status < 500) {
1176
+ return context === "attempt" ? "ATTEMPT_NOT_FOUND" : "QUIZ_NOT_FOUND";
1177
+ }
1178
+ return "UNKNOWN_ERROR";
1179
+ }
1180
+ }
1181
+ function getErrorFromMessage(message, context) {
1182
+ const lowerMessage = message.toLowerCase();
1183
+ if (lowerMessage.includes("network") || lowerMessage.includes("fetch") || lowerMessage.includes("connection")) {
1184
+ return "NETWORK_ERROR";
1185
+ }
1186
+ if (lowerMessage.includes("not found") || lowerMessage.includes("404")) {
1187
+ return context === "attempt" ? "ATTEMPT_NOT_FOUND" : "QUIZ_NOT_FOUND";
1188
+ }
1189
+ if (lowerMessage.includes("unauthorized") || lowerMessage.includes("401")) {
1190
+ return "UNAUTHORIZED";
1191
+ }
1192
+ if (lowerMessage.includes("forbidden") || lowerMessage.includes("403")) {
1193
+ return "FORBIDDEN";
1194
+ }
1195
+ if (lowerMessage.includes("expired")) {
1196
+ return "QUIZ_EXPIRED";
1197
+ }
1198
+ if (lowerMessage.includes("tts") || lowerMessage.includes("speech")) {
1199
+ return "TTS_FAILED";
1200
+ }
1201
+ if (lowerMessage.includes("chat")) {
1202
+ return "CHAT_FAILED";
1203
+ }
1204
+ return "UNKNOWN_ERROR";
1205
+ }
1206
+
1207
+ // src/MaintenanceScreen.tsx
1051
1208
  var import_jsx_runtime3 = require("react/jsx-runtime");
1209
+ function MaintenanceScreen({ errorCode }) {
1210
+ const errorDef = errorCode ? ERROR_DEFINITIONS[errorCode] : null;
1211
+ const message = errorDef?.userMessage || "Your quiz is on its way...";
1212
+ const subMessage = errorDef?.subMessage || "Please check back soon!";
1213
+ const containerStyle = {
1214
+ display: "flex",
1215
+ flexDirection: "column",
1216
+ alignItems: "center",
1217
+ justifyContent: "center",
1218
+ minHeight: "300px",
1219
+ padding: "40px",
1220
+ background: "linear-gradient(135deg, #f8f7ff 0%, #e8e4f8 50%, #f0ebff 100%)",
1221
+ borderRadius: "16px"
1222
+ };
1223
+ const iconStyle = {
1224
+ fontSize: "48px",
1225
+ marginBottom: "24px",
1226
+ color: "#8b5cf6"
1227
+ };
1228
+ const messageStyle = {
1229
+ fontSize: "20px",
1230
+ fontWeight: "600",
1231
+ color: "#4c1d95",
1232
+ textAlign: "center",
1233
+ marginBottom: "12px"
1234
+ };
1235
+ const submessageStyle = {
1236
+ fontSize: "14px",
1237
+ color: "#7c3aed",
1238
+ textAlign: "center",
1239
+ opacity: 0.8
1240
+ };
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: containerStyle, "data-testid": "maintenance-screen", "data-error-code": errorCode || "none", children: [
1242
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: iconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 8v4" }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 16h.01" })
1246
+ ] }) }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: messageStyle, "data-testid": "text-error-message", children: message }),
1248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: submessageStyle, "data-testid": "text-error-submessage", children: subMessage })
1249
+ ] });
1250
+ }
1251
+
1252
+ // src/assets/astronautData.ts
1253
+ var astronautImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAMAAADfDTFxAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwBQTFRFYGBgsVEZcHBwq6urhYSEkZGRiomJuJsBMxoHJCAhUFBQQEBAXZu4ATtbnp6ev7+/dDURJnqMdnR1enl5ABoqAm2rVSYMamhpLCkqKCUlz8/PMS0uWksA39/fbmxtAViJEBAQAAAAzunzMDAwPDo6qKioj46OgICAoqKiOC8AOTU2oKCgaq3LNDExibbNQD4+mpqaV1RVRUJCZmRlpqampKSkSEVGlpaWUE5OTElKnJycIm5+mJiYXlxdf35+lJSUXFlaU1FSZGFiWFZWAV+UBGaebtLoWlhZqMjWTMjiS0hJc3FyAUlyidvtbGpraGZmb25ujYyMYF5ed3Z2F0lUFw4CAVB+////AUJnLCgpecXVKXyqfHp75eXlT0xN1dXVNzQ1hai8ksXZPzw9qNLlTn6UsOfywMDAICAgASxFwNzsEz1GgH5/R0RFJ1ZwOjc4JiIjNjM0KicoRmt4tra2HhscMi8vXVtbACU5CwsLLisreJ+xKUhWBHS0gYCBOzg5fnx9YmBghIOD/NQBITlHgH9/PMPgWVdYJyQlMCwtESYyIx8gTUtLgoGBPV5qSUdH8m4jg4KCVVNTAA8XfHt7DwcCAAcLAHW4FRQV7+/vr6+vBAwOOLfSDzE4LZKoHmJwRkNEQj9APjs8CyUqCBgcMZ62i4qLj4+P42chfXx8aWdoDw4OUk9QZWNjNavExVkcAGahGlViKYaal0UWjo2NbWtsiYiI1GAfh4aH7McBfmoBl5eXlZWVk5KSn5+fdHNzpaWlnZ2dfn19cW9viD4UkI+Qjo2OeHd33boBnoUBATNQzawBjncBbl0AnuHw7/b63+/3EBcbFH256urqEkpq+vr6mZmZElJ1GhgYU1xeo6OjGG6fm5ubEh4jRVdejIuM9fX1lpWWnp2elJOTICcrnJuboaGhornEE3WtQU5Wv9HaJzg8h5icQZSmz9jeeo+XY2xucHd7EjtTkJ2iBwYGfYqNGV+HMTtAQoWr8/v9v8bIw8vNIi0yZHyHFygs////mvWEFwAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAMcVSURBVHja7L3viyRnuf9f9CRCuhgLtAeTlj5TRcdTwSUkRo+bBDXG2SDoMIkubI/g8Sz4wBow5AdnzczAJsfAYGRXgo7MTPWjGA5pNAr7QEXQVNSQE9hnIsoiHvacj99z/oJ9dJ747brv+v2ju7q7qqa79/V6oMlmd3709vSrr+u+rvet/B0AAABqR+EhAAAAQMAAAAAIGAAAABAwAAAAAgYAAAAEDAAAgIABAAAAAQMAACBgAAAAQMAAAAAIGAAAAAEDAAAAAgaA6dE0zTkQKOqQhvzn4a92eGwAEDAAlE1bOzhQVd0YSVdVDhxMDICAAaCMktc5UC1jItTmQR8NAyBgAJjWvQOla0yLrjawMAACBoDJsPsNNb/CFTTNIfIfcytkqzlo82ACIGAAKFb5NjIKX0tVzIGmdXo5aFrf9XFGKdwcUAkDIGAAGE3HaaYKXsXU2r3CaI6ZKomtRp+HFgABA0CefQeJ0lc1+xOoN6bh5Pmxrjg2jzAAAgaAMfa1lMGU7g2wNTPekm46PMwACBgAIthON3Zs63R6JaGZsY+saDzYAAgYACR9JRqnYbZ75WL3lUiEh3XATBYAAgaAv9sHVrRJ3OlVQrsR/SyMZAEgYIDbHE2J2tfuVUjUwdYBE1kACBjg9sUJh6S6g06vcrSwF60rdKIBEDDAbYntWKEN272aiDi/yUAWAAIGuP30exAUo12nVyedRvCZVRQMgIABblf91lf8RsrgLgoGQMAAtyGBfnXT7p0KWhMFAyBggNuM4OzXck5Jv6ITHcxfNxnHAkDAAMuPFuq3d7qEClZYSgJAwADLTUedF/3GFKwf8FcDgIABlhf7YJ70G1OwxVEwAAIGWFb61imPXmUqWOUoGAABAywzgekac6RfMRHtLSXpA/6S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";
1254
+
1255
+ // src/QuizPlayer.tsx
1256
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1052
1257
  var defaultStyles = {
1053
1258
  container: {
1054
1259
  fontFamily: "system-ui, -apple-system, sans-serif",
@@ -1349,6 +1554,13 @@ var defaultStyles = {
1349
1554
  backgroundColor: "#fef2f2",
1350
1555
  borderColor: "#ef4444"
1351
1556
  },
1557
+ feedbackNeutral: {
1558
+ backgroundColor: "#f0f9ff",
1559
+ borderColor: "#0ea5e9"
1560
+ },
1561
+ feedbackTitleNeutral: {
1562
+ color: "#0369a1"
1563
+ },
1352
1564
  feedbackTitle: {
1353
1565
  fontSize: "16px",
1354
1566
  fontWeight: "600",
@@ -1369,8 +1581,292 @@ var defaultStyles = {
1369
1581
  lineHeight: "1.5"
1370
1582
  }
1371
1583
  };
1584
+ function SortingDragDrop({ items, currentOrder, correctOrder, showFeedback, onOrderChange }) {
1585
+ const [draggedIndex, setDraggedIndex] = (0, import_react3.useState)(null);
1586
+ const [dragOverIndex, setDragOverIndex] = (0, import_react3.useState)(null);
1587
+ const handleDragStart = (e, position) => {
1588
+ if (showFeedback) return;
1589
+ setDraggedIndex(position);
1590
+ e.dataTransfer.effectAllowed = "move";
1591
+ e.dataTransfer.setData("text/plain", position.toString());
1592
+ };
1593
+ const handleDragOver = (e, position) => {
1594
+ e.preventDefault();
1595
+ if (showFeedback) return;
1596
+ e.dataTransfer.dropEffect = "move";
1597
+ setDragOverIndex(position);
1598
+ };
1599
+ const handleDragLeave = () => {
1600
+ setDragOverIndex(null);
1601
+ };
1602
+ const handleDrop = (e, toPosition) => {
1603
+ e.preventDefault();
1604
+ if (showFeedback) return;
1605
+ const fromPosition = parseInt(e.dataTransfer.getData("text/plain"), 10);
1606
+ if (fromPosition !== toPosition) {
1607
+ const newOrder = [...currentOrder];
1608
+ const [movedItem] = newOrder.splice(fromPosition, 1);
1609
+ newOrder.splice(toPosition, 0, movedItem);
1610
+ onOrderChange(newOrder);
1611
+ }
1612
+ setDraggedIndex(null);
1613
+ setDragOverIndex(null);
1614
+ };
1615
+ const handleDragEnd = () => {
1616
+ setDraggedIndex(null);
1617
+ setDragOverIndex(null);
1618
+ };
1619
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.options, children: currentOrder.map((itemIndex, position) => {
1620
+ const isCorrectPosition = correctOrder?.[position] === itemIndex;
1621
+ const isDragging = draggedIndex === position;
1622
+ const isDragOver = dragOverIndex === position;
1623
+ let itemStyle = {
1624
+ ...defaultStyles.option,
1625
+ display: "flex",
1626
+ alignItems: "center",
1627
+ gap: "12px",
1628
+ cursor: showFeedback ? "default" : "grab",
1629
+ opacity: isDragging ? 0.5 : 1,
1630
+ transition: "all 0.2s ease",
1631
+ transform: isDragOver && !showFeedback ? "scale(1.02)" : "scale(1)"
1632
+ };
1633
+ if (showFeedback) {
1634
+ if (isCorrectPosition) {
1635
+ itemStyle = { ...itemStyle, ...defaultStyles.optionCorrect };
1636
+ } else {
1637
+ itemStyle = { ...itemStyle, ...defaultStyles.optionIncorrect };
1638
+ }
1639
+ } else if (isDragOver) {
1640
+ itemStyle = { ...itemStyle, borderColor: "#6366f1", backgroundColor: "#eef2ff" };
1641
+ }
1642
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1643
+ "div",
1644
+ {
1645
+ style: itemStyle,
1646
+ "data-testid": `sorting-item-${position}`,
1647
+ draggable: !showFeedback,
1648
+ onDragStart: (e) => handleDragStart(e, position),
1649
+ onDragOver: (e) => handleDragOver(e, position),
1650
+ onDragLeave: handleDragLeave,
1651
+ onDrop: (e) => handleDrop(e, position),
1652
+ onDragEnd: handleDragEnd,
1653
+ children: [
1654
+ !showFeedback && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1655
+ cursor: "grab",
1656
+ padding: "4px",
1657
+ display: "flex",
1658
+ alignItems: "center",
1659
+ color: "#9ca3af"
1660
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "5", r: "1" }),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "19", r: "1" }),
1664
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "5", r: "1" }),
1665
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
1666
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "19", r: "1" })
1667
+ ] }) }),
1668
+ showFeedback && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1669
+ display: "flex",
1670
+ alignItems: "center",
1671
+ color: isCorrectPosition ? "#22c55e" : "#ef4444"
1672
+ }, children: isCorrectPosition ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1673
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
1674
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "22 4 12 14.01 9 11.01" })
1675
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1676
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1677
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
1679
+ ] }) }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: items[itemIndex] }),
1681
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextToSpeech, { text: items[itemIndex], size: "sm" }) })
1682
+ ]
1683
+ },
1684
+ itemIndex
1685
+ );
1686
+ }) });
1687
+ }
1688
+ function MatchingDragDrop({ leftItems, rightItems, currentMatches, correctMatches, showFeedback, onMatchChange }) {
1689
+ const [draggedItem, setDraggedItem] = (0, import_react3.useState)(null);
1690
+ const [dragOverLeft, setDragOverLeft] = (0, import_react3.useState)(null);
1691
+ const matchedRightItems = Object.values(currentMatches);
1692
+ const unmatchedRightItems = rightItems.filter((item) => !matchedRightItems.includes(item));
1693
+ const handleDragStart = (e, rightItem) => {
1694
+ if (showFeedback) return;
1695
+ setDraggedItem(rightItem);
1696
+ e.dataTransfer.effectAllowed = "move";
1697
+ e.dataTransfer.setData("text/plain", rightItem);
1698
+ };
1699
+ const handleDragOver = (e, leftItem) => {
1700
+ e.preventDefault();
1701
+ if (showFeedback) return;
1702
+ e.dataTransfer.dropEffect = "move";
1703
+ setDragOverLeft(leftItem);
1704
+ };
1705
+ const handleDragLeave = () => {
1706
+ setDragOverLeft(null);
1707
+ };
1708
+ const handleDrop = (e, leftItem) => {
1709
+ e.preventDefault();
1710
+ if (showFeedback) return;
1711
+ const rightItem = e.dataTransfer.getData("text/plain");
1712
+ if (rightItem) {
1713
+ const newMatches = { ...currentMatches };
1714
+ Object.keys(newMatches).forEach((key) => {
1715
+ if (newMatches[key] === rightItem) {
1716
+ delete newMatches[key];
1717
+ }
1718
+ });
1719
+ newMatches[leftItem] = rightItem;
1720
+ onMatchChange(newMatches);
1721
+ }
1722
+ setDraggedItem(null);
1723
+ setDragOverLeft(null);
1724
+ };
1725
+ const handleDragEnd = () => {
1726
+ setDraggedItem(null);
1727
+ setDragOverLeft(null);
1728
+ };
1729
+ const handleClearMatch = (leftItem) => {
1730
+ if (showFeedback) return;
1731
+ const newMatches = { ...currentMatches };
1732
+ delete newMatches[leftItem];
1733
+ onMatchChange(newMatches);
1734
+ };
1735
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "24px", flexWrap: "wrap" }, children: [
1736
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: "200px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "14px", fontWeight: "600", color: "#6b7280", marginBottom: "4px" }, children: "Match these items:" }),
1738
+ leftItems.map((leftItem, idx) => {
1739
+ const matchedRight = currentMatches[leftItem];
1740
+ const correctMatch = correctMatches?.[leftItem];
1741
+ const isCorrect = matchedRight === correctMatch;
1742
+ const isDragOver = dragOverLeft === leftItem;
1743
+ let rowStyle = {
1744
+ display: "flex",
1745
+ alignItems: "center",
1746
+ gap: "12px",
1747
+ padding: "12px 16px",
1748
+ border: "2px dashed #e5e7eb",
1749
+ borderRadius: "8px",
1750
+ backgroundColor: "#ffffff",
1751
+ minHeight: "56px",
1752
+ transition: "all 0.2s ease"
1753
+ };
1754
+ if (showFeedback) {
1755
+ rowStyle.borderStyle = "solid";
1756
+ if (isCorrect) {
1757
+ rowStyle = { ...rowStyle, borderColor: "#22c55e", backgroundColor: "#f0fdf4" };
1758
+ } else {
1759
+ rowStyle = { ...rowStyle, borderColor: "#ef4444", backgroundColor: "#fef2f2" };
1760
+ }
1761
+ } else if (isDragOver) {
1762
+ rowStyle = { ...rowStyle, borderColor: "#6366f1", backgroundColor: "#eef2ff", borderStyle: "solid" };
1763
+ } else if (matchedRight) {
1764
+ rowStyle = { ...rowStyle, borderStyle: "solid", borderColor: "#22c55e" };
1765
+ }
1766
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1767
+ "div",
1768
+ {
1769
+ style: rowStyle,
1770
+ "data-testid": `matrix-row-${idx}`,
1771
+ onDragOver: (e) => handleDragOver(e, leftItem),
1772
+ onDragLeave: handleDragLeave,
1773
+ onDrop: (e) => handleDrop(e, leftItem),
1774
+ children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, fontWeight: "500" }, children: leftItem }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "#6b7280" }, children: "\u2192" }),
1777
+ matchedRight ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1778
+ display: "flex",
1779
+ alignItems: "center",
1780
+ gap: "8px",
1781
+ padding: "6px 12px",
1782
+ backgroundColor: showFeedback ? isCorrect ? "#dcfce7" : "#fee2e2" : "#e0e7ff",
1783
+ borderRadius: "6px",
1784
+ fontSize: "14px"
1785
+ }, children: [
1786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: matchedRight }),
1787
+ !showFeedback && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1788
+ "button",
1789
+ {
1790
+ onClick: () => handleClearMatch(leftItem),
1791
+ style: {
1792
+ background: "none",
1793
+ border: "none",
1794
+ cursor: "pointer",
1795
+ padding: "2px",
1796
+ display: "flex",
1797
+ color: "#6b7280"
1798
+ },
1799
+ "aria-label": "Remove match",
1800
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1801
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1802
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1803
+ ] })
1804
+ }
1805
+ ),
1806
+ showFeedback && (isCorrect ? /* @__PURE__ */ (0, import_jsx_runtime4.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_runtime4.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#ef4444", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1807
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1808
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1809
+ ] }))
1810
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "#9ca3af", fontSize: "14px", fontStyle: "italic" }, children: "Drop here" }),
1811
+ showFeedback && !isCorrect && correctMatch && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { color: "#166534", fontSize: "13px", marginLeft: "8px" }, children: [
1812
+ "(Correct: ",
1813
+ correctMatch,
1814
+ ")"
1815
+ ] })
1816
+ ]
1817
+ },
1818
+ idx
1819
+ );
1820
+ })
1821
+ ] }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: "200px", display: "flex", flexDirection: "column", gap: "8px" }, children: [
1823
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "14px", fontWeight: "600", color: "#6b7280", marginBottom: "4px" }, children: "Drag to match:" }),
1824
+ unmatchedRightItems.length > 0 ? unmatchedRightItems.map((rightItem, idx) => {
1825
+ const isDragging = draggedItem === rightItem;
1826
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1827
+ "div",
1828
+ {
1829
+ style: {
1830
+ padding: "12px 16px",
1831
+ border: "2px solid #e5e7eb",
1832
+ borderRadius: "8px",
1833
+ backgroundColor: "#ffffff",
1834
+ cursor: showFeedback ? "default" : "grab",
1835
+ opacity: isDragging ? 0.5 : 1,
1836
+ display: "flex",
1837
+ alignItems: "center",
1838
+ gap: "8px",
1839
+ transition: "all 0.2s ease"
1840
+ },
1841
+ draggable: !showFeedback,
1842
+ onDragStart: (e) => handleDragStart(e, rightItem),
1843
+ onDragEnd: handleDragEnd,
1844
+ "data-testid": `draggable-right-${idx}`,
1845
+ children: [
1846
+ !showFeedback && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#9ca3af", display: "flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1847
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "5", r: "1" }),
1848
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
1849
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "9", cy: "19", r: "1" }),
1850
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "5", r: "1" }),
1851
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
1852
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "15", cy: "19", r: "1" })
1853
+ ] }) }),
1854
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: rightItem })
1855
+ ]
1856
+ },
1857
+ idx
1858
+ );
1859
+ }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1860
+ padding: "16px",
1861
+ textAlign: "center",
1862
+ color: "#22c55e",
1863
+ fontSize: "14px"
1864
+ }, children: "All items matched!" })
1865
+ ] })
1866
+ ] });
1867
+ }
1372
1868
  function Spinner({ size = 16, color = "#ffffff" }) {
1373
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1869
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1374
1870
  "span",
1375
1871
  {
1376
1872
  style: {
@@ -1382,7 +1878,7 @@ function Spinner({ size = 16, color = "#ffffff" }) {
1382
1878
  borderRadius: "50%",
1383
1879
  animation: "spin 0.8s linear infinite"
1384
1880
  },
1385
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1881
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1386
1882
  }
1387
1883
  );
1388
1884
  }
@@ -1400,7 +1896,8 @@ function QuizPlayer({
1400
1896
  onProgress,
1401
1897
  onGenerateMoreQuestions,
1402
1898
  className,
1403
- forceNewAttempt = true
1899
+ forceNewAttempt = true,
1900
+ hideScore = false
1404
1901
  }) {
1405
1902
  const [quiz, setQuiz] = (0, import_react3.useState)(null);
1406
1903
  const [attempt, setAttempt] = (0, import_react3.useState)(null);
@@ -1411,10 +1908,14 @@ function QuizPlayer({
1411
1908
  const [isNavigating, setIsNavigating] = (0, import_react3.useState)(false);
1412
1909
  const [isCompleted, setIsCompleted] = (0, import_react3.useState)(false);
1413
1910
  const [result, setResult] = (0, import_react3.useState)(null);
1414
- const [error, setError] = (0, import_react3.useState)(null);
1911
+ const [errorCode, setErrorCode] = (0, import_react3.useState)(null);
1415
1912
  const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
1416
1913
  const [elapsedSeconds, setElapsedSeconds] = (0, import_react3.useState)(0);
1417
1914
  const [showIntro, setShowIntro] = (0, import_react3.useState)(true);
1915
+ const [showResumeChoice, setShowResumeChoice] = (0, import_react3.useState)(false);
1916
+ const [hasExistingProgress, setHasExistingProgress] = (0, import_react3.useState)(false);
1917
+ const [existingProgressCount, setExistingProgressCount] = (0, import_react3.useState)(0);
1918
+ const [isStartingFresh, setIsStartingFresh] = (0, import_react3.useState)(false);
1418
1919
  const [timerStarted, setTimerStarted] = (0, import_react3.useState)(false);
1419
1920
  const [showFeedback, setShowFeedback] = (0, import_react3.useState)(false);
1420
1921
  const [currentAnswerDetail, setCurrentAnswerDetail] = (0, import_react3.useState)(null);
@@ -1449,7 +1950,7 @@ function QuizPlayer({
1449
1950
  if (!apiClient.current) return;
1450
1951
  try {
1451
1952
  setIsLoading(true);
1452
- setError(null);
1953
+ setErrorCode(null);
1453
1954
  const quizData = await apiClient.current.getQuiz(quizId);
1454
1955
  setQuiz(quizData);
1455
1956
  const attemptData = await apiClient.current.createAttempt({
@@ -1462,7 +1963,10 @@ function QuizPlayer({
1462
1963
  forceNew: forceNewAttempt
1463
1964
  });
1464
1965
  setAttempt(attemptData);
1465
- if (!forceNewAttempt && attemptData.answers && attemptData.answers.length > 0) {
1966
+ if (!forceNewAttempt && attemptData.answers && attemptData.answers.length > 0 && attemptData.status === "in_progress") {
1967
+ setHasExistingProgress(true);
1968
+ setExistingProgressCount(attemptData.answers.length);
1969
+ setShowResumeChoice(true);
1466
1970
  setAnswersDetail(attemptData.answers);
1467
1971
  const answersMap = /* @__PURE__ */ new Map();
1468
1972
  attemptData.answers.forEach((a) => {
@@ -1485,7 +1989,15 @@ function QuizPlayer({
1485
1989
  setIsLoading(false);
1486
1990
  } catch (err) {
1487
1991
  const message = err instanceof Error ? err.message : "Failed to load quiz";
1488
- setError(message);
1992
+ const code = getErrorFromMessage(message, "quiz");
1993
+ setErrorCode(code);
1994
+ apiClient.current?.logError({
1995
+ errorCode: code,
1996
+ context: "quiz",
1997
+ resourceId: quizId,
1998
+ component: "QuizPlayer",
1999
+ message
2000
+ });
1489
2001
  setIsLoading(false);
1490
2002
  onErrorRef.current?.(err instanceof Error ? err : new Error(message));
1491
2003
  }
@@ -1493,7 +2005,7 @@ function QuizPlayer({
1493
2005
  initialize();
1494
2006
  }, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt]);
1495
2007
  (0, import_react3.useEffect)(() => {
1496
- if (timerStarted && !isCompleted && !error) {
2008
+ if (timerStarted && !isCompleted && !errorCode) {
1497
2009
  startTimeRef.current = Date.now();
1498
2010
  timerRef.current = setInterval(() => {
1499
2011
  setElapsedSeconds(Math.floor((Date.now() - startTimeRef.current) / 1e3));
@@ -1504,11 +2016,62 @@ function QuizPlayer({
1504
2016
  clearInterval(timerRef.current);
1505
2017
  }
1506
2018
  };
1507
- }, [timerStarted, isCompleted, error]);
2019
+ }, [timerStarted, isCompleted, errorCode]);
1508
2020
  const handleStart = (0, import_react3.useCallback)(() => {
1509
2021
  setShowIntro(false);
2022
+ setShowResumeChoice(false);
1510
2023
  setTimerStarted(true);
1511
2024
  }, []);
2025
+ const handleResumePrevious = (0, import_react3.useCallback)(() => {
2026
+ if (quiz && answers.size > 0) {
2027
+ const answeredIds = new Set(answers.keys());
2028
+ const allQs = [...quiz.questions, ...extraQuestions].filter((q) => !skippedQuestionIds.has(q.id));
2029
+ let resumeIndex = 0;
2030
+ for (let i = 0; i < allQs.length; i++) {
2031
+ if (!answeredIds.has(allQs[i].id)) {
2032
+ resumeIndex = i;
2033
+ break;
2034
+ }
2035
+ resumeIndex = allQs.length - 1;
2036
+ }
2037
+ setCurrentQuestionIndex(resumeIndex);
2038
+ }
2039
+ setShowIntro(false);
2040
+ setShowResumeChoice(false);
2041
+ setTimerStarted(true);
2042
+ }, [quiz, answers, extraQuestions, skippedQuestionIds]);
2043
+ const handleStartFresh = (0, import_react3.useCallback)(async () => {
2044
+ if (!apiClient.current || !attempt) return;
2045
+ setIsStartingFresh(true);
2046
+ try {
2047
+ await apiClient.current.updateAttempt(attempt.id, {
2048
+ status: "abandoned"
2049
+ });
2050
+ const newAttemptData = await apiClient.current.createAttempt({
2051
+ quizId,
2052
+ lessonId,
2053
+ assignLessonId,
2054
+ courseId,
2055
+ childId,
2056
+ parentId,
2057
+ forceNew: true
2058
+ });
2059
+ setAttempt(newAttemptData);
2060
+ setAnswers(/* @__PURE__ */ new Map());
2061
+ setAnswersDetail([]);
2062
+ setCurrentQuestionIndex(0);
2063
+ setHasExistingProgress(false);
2064
+ setExistingProgressCount(0);
2065
+ setShowResumeChoice(false);
2066
+ setShowIntro(false);
2067
+ setTimerStarted(true);
2068
+ } catch (err) {
2069
+ const message = err instanceof Error ? err.message : "Failed to start fresh";
2070
+ onErrorRef.current?.(new Error(message));
2071
+ } finally {
2072
+ setIsStartingFresh(false);
2073
+ }
2074
+ }, [attempt, quizId, lessonId, assignLessonId, courseId, childId, parentId]);
1512
2075
  (0, import_react3.useEffect)(() => {
1513
2076
  setShowFeedback(false);
1514
2077
  setCurrentAnswerDetail(null);
@@ -1628,7 +2191,15 @@ function QuizPlayer({
1628
2191
  onCompleteRef.current?.(quizResult);
1629
2192
  } catch (err) {
1630
2193
  const message = err instanceof Error ? err.message : "Failed to submit quiz";
1631
- setError(message);
2194
+ const code = getErrorFromMessage(message, "quiz");
2195
+ setErrorCode(code);
2196
+ apiClient.current?.logError({
2197
+ errorCode: code,
2198
+ context: "quiz",
2199
+ resourceId: quizId,
2200
+ component: "QuizPlayer",
2201
+ message
2202
+ });
1632
2203
  onErrorRef.current?.(err instanceof Error ? err : new Error(message));
1633
2204
  } finally {
1634
2205
  setIsSubmitting(false);
@@ -1707,13 +2278,10 @@ function QuizPlayer({
1707
2278
  }
1708
2279
  }, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId]);
1709
2280
  if (isLoading) {
1710
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.loading, children: "Loading quiz..." }) });
2281
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.loading, children: "Loading quiz..." }) });
1711
2282
  }
1712
- if (error) {
1713
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.error, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1714
- "Error: ",
1715
- error
1716
- ] }) }) });
2283
+ if (errorCode) {
2284
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MaintenanceScreen, { errorCode }) });
1717
2285
  }
1718
2286
  if (isCompleted && result) {
1719
2287
  const percentage = result.totalQuestions > 0 ? Math.round(result.correctAnswers / result.totalQuestions * 100) : 0;
@@ -1771,7 +2339,7 @@ function QuizPlayer({
1771
2339
  rotation: Math.random() * 360,
1772
2340
  size: 6 + Math.random() * 8
1773
2341
  }));
1774
- const StarIcon = ({ filled, delay }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2342
+ const StarIcon = ({ filled, delay }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1775
2343
  "svg",
1776
2344
  {
1777
2345
  width: "36",
@@ -1782,7 +2350,7 @@ function QuizPlayer({
1782
2350
  animationDelay: `${delay}s`,
1783
2351
  opacity: 0
1784
2352
  },
1785
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2353
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1786
2354
  "path",
1787
2355
  {
1788
2356
  d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
@@ -1793,62 +2361,23 @@ function QuizPlayer({
1793
2361
  )
1794
2362
  }
1795
2363
  );
1796
- const MascotOwl = ({ mood }) => {
1797
- const getEyeExpression = () => {
1798
- switch (mood) {
1799
- case "celebrating":
1800
- return { leftEye: ">", rightEye: "<", pupilY: 42 };
1801
- // Squinting happy
1802
- case "happy":
1803
- return { leftEye: null, rightEye: null, pupilY: 42 };
1804
- // Normal happy
1805
- case "encouraging":
1806
- return { leftEye: null, rightEye: null, pupilY: 44 };
1807
- // Looking down warmly
1808
- default:
1809
- return { leftEye: null, rightEye: null, pupilY: 42 };
1810
- }
1811
- };
1812
- const eyeExpr = getEyeExpression();
1813
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1814
- "svg",
2364
+ const Mascot = ({ mood }) => {
2365
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2366
+ "img",
1815
2367
  {
1816
- width: "120",
1817
- height: "120",
1818
- viewBox: "0 0 100 100",
2368
+ src: astronautImage,
2369
+ alt: "Astronaut mascot",
2370
+ width: "180",
2371
+ height: "180",
1819
2372
  style: {
1820
- animation: mood === "celebrating" ? "bounce 0.6s ease-in-out infinite" : "gentleBob 2s ease-in-out infinite"
1821
- },
1822
- children: [
1823
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "50", cy: "60", rx: "35", ry: "30", fill: "#8b5cf6" }),
1824
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "50", cy: "65", rx: "25", ry: "20", fill: "#c4b5fd" }),
1825
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "50", cy: "35", r: "28", fill: "#a78bfa" }),
1826
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "28,15 35,30 22,28", fill: "#7c3aed" }),
1827
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "72,15 65,30 78,28", fill: "#7c3aed" }),
1828
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "38", cy: "38", rx: "10", ry: "12", fill: "white" }),
1829
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "62", cy: "38", rx: "10", ry: "12", fill: "white" }),
1830
- eyeExpr.leftEye ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: "38", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.leftEye }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "38", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
1831
- eyeExpr.rightEye ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: "62", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.rightEye }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "62", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
1832
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "50,45 45,52 55,52", fill: "#fbbf24" }),
1833
- (mood === "celebrating" || mood === "happy") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1834
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "28", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" }),
1835
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "72", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" })
1836
- ] }),
1837
- mood === "celebrating" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1838
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "18", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(-30 18 55)" }),
1839
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "82", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(30 82 55)" })
1840
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1841
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "20", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" }),
1842
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "80", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" })
1843
- ] }),
1844
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "40", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" }),
1845
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: "60", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" })
1846
- ]
2373
+ animation: mood === "celebrating" ? "bounce 0.6s ease-in-out infinite" : "gentleBob 2s ease-in-out infinite",
2374
+ objectFit: "contain"
2375
+ }
1847
2376
  }
1848
2377
  );
1849
2378
  };
1850
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: defaultStyles.container, children: [
1851
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
2379
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style: defaultStyles.container, children: [
2380
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
1852
2381
  @keyframes confettiFall {
1853
2382
  0% {
1854
2383
  transform: translateY(-10px) rotate(0deg);
@@ -1912,8 +2441,8 @@ function QuizPlayer({
1912
2441
  }
1913
2442
  }
1914
2443
  ` }),
1915
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.results, children: [
1916
- percentage >= 60 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.confettiContainer, children: confettiPieces.map((piece) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2444
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.results, children: [
2445
+ percentage >= 60 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.confettiContainer, children: confettiPieces.map((piece) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1917
2446
  "div",
1918
2447
  {
1919
2448
  style: {
@@ -1930,15 +2459,70 @@ function QuizPlayer({
1930
2459
  },
1931
2460
  piece.id
1932
2461
  )) }),
1933
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultStyles.resultsBackground, background: theme.bgGradient } }),
1934
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.resultsContent, children: [
1935
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.resultStars, children: [
1936
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StarIcon, { filled: theme.stars >= 1, delay: 0.3 }),
1937
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StarIcon, { filled: theme.stars >= 2, delay: 0.5 }),
1938
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StarIcon, { filled: theme.stars >= 3, delay: 0.7 })
2462
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultStyles.resultsBackground, background: theme.bgGradient } }),
2463
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.resultsContent, children: hideScore ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2464
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginBottom: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Mascot, { mood: "happy" }) }),
2465
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2466
+ "div",
2467
+ {
2468
+ style: {
2469
+ background: "linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%)",
2470
+ padding: "12px 28px",
2471
+ borderRadius: "50px",
2472
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
2473
+ marginBottom: "20px",
2474
+ animation: "badgePop 0.6s ease-out 0.2s forwards",
2475
+ opacity: 0,
2476
+ border: "3px solid #22c55e"
2477
+ },
2478
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2479
+ "span",
2480
+ {
2481
+ style: {
2482
+ fontSize: "22px",
2483
+ fontWeight: "700",
2484
+ color: "#1f2937",
2485
+ textShadow: "0 1px 2px rgba(255,255,255,0.5)"
2486
+ },
2487
+ children: "All Done!"
2488
+ }
2489
+ )
2490
+ }
2491
+ ),
2492
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2493
+ "div",
2494
+ {
2495
+ style: {
2496
+ animation: "scoreSlideIn 0.5s ease-out 0.4s forwards",
2497
+ opacity: 0
2498
+ },
2499
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2500
+ "div",
2501
+ {
2502
+ style: {
2503
+ fontSize: "24px",
2504
+ fontWeight: "600",
2505
+ color: "#22c55e",
2506
+ lineHeight: "1.4",
2507
+ marginBottom: "12px"
2508
+ },
2509
+ children: "The quiz was submitted successfully!"
2510
+ }
2511
+ )
2512
+ }
2513
+ ),
2514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...defaultStyles.resultDetails, marginTop: "8px" }, children: [
2515
+ "Time: ",
2516
+ formatTime(result.timeSpentSeconds)
2517
+ ] })
2518
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2519
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.resultStars, children: [
2520
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StarIcon, { filled: theme.stars >= 1, delay: 0.3 }),
2521
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StarIcon, { filled: theme.stars >= 2, delay: 0.5 }),
2522
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StarIcon, { filled: theme.stars >= 3, delay: 0.7 })
1939
2523
  ] }),
1940
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MascotOwl, { mood: theme.mascotMood }) }),
1941
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2524
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Mascot, { mood: theme.mascotMood }) }),
2525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1942
2526
  "div",
1943
2527
  {
1944
2528
  style: {
@@ -1951,7 +2535,7 @@ function QuizPlayer({
1951
2535
  opacity: 0,
1952
2536
  border: `3px solid ${theme.badgeColor}`
1953
2537
  },
1954
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2538
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1955
2539
  "span",
1956
2540
  {
1957
2541
  style: {
@@ -1965,7 +2549,7 @@ function QuizPlayer({
1965
2549
  )
1966
2550
  }
1967
2551
  ),
1968
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2552
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1969
2553
  "div",
1970
2554
  {
1971
2555
  style: {
@@ -1973,7 +2557,7 @@ function QuizPlayer({
1973
2557
  opacity: 0
1974
2558
  },
1975
2559
  children: [
1976
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2560
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1977
2561
  "div",
1978
2562
  {
1979
2563
  style: {
@@ -1990,7 +2574,7 @@ function QuizPlayer({
1990
2574
  ]
1991
2575
  }
1992
2576
  ),
1993
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2577
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1994
2578
  "div",
1995
2579
  {
1996
2580
  style: {
@@ -2005,25 +2589,84 @@ function QuizPlayer({
2005
2589
  ]
2006
2590
  }
2007
2591
  ),
2008
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...defaultStyles.resultDetails, marginTop: "8px" }, children: [
2592
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...defaultStyles.resultDetails, marginTop: "8px" }, children: [
2009
2593
  "Time: ",
2010
2594
  formatTime(result.timeSpentSeconds)
2011
2595
  ] })
2012
- ] })
2596
+ ] }) })
2013
2597
  ] })
2014
2598
  ] });
2015
2599
  }
2600
+ if (quiz && showIntro && showResumeChoice && hasExistingProgress) {
2601
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.intro, children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.introTitle, children: "Welcome Back!" }),
2603
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.introSubtitle, children: "You have an unfinished quiz. Would you like to continue or start over?" }),
2604
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.introQuestionCount, children: [
2605
+ existingProgressCount,
2606
+ " of ",
2607
+ quiz.questions.length,
2608
+ " question",
2609
+ quiz.questions.length !== 1 ? "s" : "",
2610
+ " answered"
2611
+ ] }),
2612
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px", marginTop: "8px" }, children: [
2613
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2614
+ "button",
2615
+ {
2616
+ style: defaultStyles.startButton,
2617
+ onClick: handleResumePrevious,
2618
+ onMouseOver: (e) => {
2619
+ e.currentTarget.style.transform = "translateY(-2px)";
2620
+ e.currentTarget.style.boxShadow = "0 6px 20px rgba(124, 58, 237, 0.5)";
2621
+ },
2622
+ onMouseOut: (e) => {
2623
+ e.currentTarget.style.transform = "translateY(0)";
2624
+ e.currentTarget.style.boxShadow = "0 4px 14px rgba(124, 58, 237, 0.4)";
2625
+ },
2626
+ "data-testid": "button-continue-quiz",
2627
+ children: "Continue Where I Left Off"
2628
+ }
2629
+ ),
2630
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2631
+ "button",
2632
+ {
2633
+ style: {
2634
+ ...defaultStyles.startButton,
2635
+ background: "transparent",
2636
+ color: "#7c3aed",
2637
+ border: "2px solid #7c3aed",
2638
+ boxShadow: "none"
2639
+ },
2640
+ onClick: handleStartFresh,
2641
+ disabled: isStartingFresh,
2642
+ onMouseOver: (e) => {
2643
+ if (!isStartingFresh) {
2644
+ e.currentTarget.style.transform = "translateY(-2px)";
2645
+ e.currentTarget.style.background = "rgba(124, 58, 237, 0.1)";
2646
+ }
2647
+ },
2648
+ onMouseOut: (e) => {
2649
+ e.currentTarget.style.transform = "translateY(0)";
2650
+ e.currentTarget.style.background = "transparent";
2651
+ },
2652
+ "data-testid": "button-start-fresh",
2653
+ children: isStartingFresh ? "Starting..." : "Start Fresh"
2654
+ }
2655
+ )
2656
+ ] })
2657
+ ] }) });
2658
+ }
2016
2659
  if (quiz && showIntro) {
2017
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.intro, children: [
2018
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.introTitle, children: quiz.title }),
2019
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.introSubtitle, children: "Ready to test your knowledge?" }),
2020
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.introQuestionCount, children: [
2660
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.intro, children: [
2661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.introTitle, children: quiz.title }),
2662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.introSubtitle, children: "Ready to test your knowledge?" }),
2663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.introQuestionCount, children: [
2021
2664
  quiz.questions.length,
2022
2665
  " question",
2023
2666
  quiz.questions.length !== 1 ? "s" : "",
2024
2667
  " to answer"
2025
2668
  ] }),
2026
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2669
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2027
2670
  "button",
2028
2671
  {
2029
2672
  style: defaultStyles.startButton,
@@ -2043,7 +2686,7 @@ function QuizPlayer({
2043
2686
  ] }) });
2044
2687
  }
2045
2688
  if (!quiz || !currentQuestion) {
2046
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.error, children: "No quiz data available" }) });
2689
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.error, children: "No quiz data available" }) });
2047
2690
  }
2048
2691
  const selectedAnswer = answers.get(currentQuestion.id);
2049
2692
  const isLastQuestion = currentQuestionIndex === totalQuestions - 1;
@@ -2051,21 +2694,21 @@ function QuizPlayer({
2051
2694
  const remainingSlots = maxQuestions - totalQuestions;
2052
2695
  const questionsToAdd = Math.min(5, remainingSlots);
2053
2696
  const canAddMore = onGenerateMoreQuestions && remainingSlots > 0;
2054
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.mainLayout, children: [
2055
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.quizContent, children: [
2056
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.header, children: [
2057
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.title, children: quiz.title }),
2058
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.progress, children: [
2697
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.mainLayout, children: [
2698
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.quizContent, children: [
2699
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.header, children: [
2700
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.title, children: quiz.title }),
2701
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.progress, children: [
2059
2702
  "Question ",
2060
2703
  currentQuestionIndex + 1,
2061
2704
  " of ",
2062
2705
  totalQuestions
2063
2706
  ] }),
2064
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
2707
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
2065
2708
  ] }),
2066
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
2067
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
2068
- isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2709
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
2710
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
2711
+ isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2069
2712
  "button",
2070
2713
  {
2071
2714
  onClick: () => setShowSkipModal(true),
@@ -2098,15 +2741,15 @@ function QuizPlayer({
2098
2741
  },
2099
2742
  "data-testid": "button-skip-question",
2100
2743
  children: [
2101
- /* @__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: [
2102
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "5 4 15 12 5 20 5 4" }),
2103
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
2744
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2745
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: "5 4 15 12 5 20 5 4" }),
2746
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
2104
2747
  ] }),
2105
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Skip" })
2748
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Skip" })
2106
2749
  ]
2107
2750
  }
2108
2751
  ),
2109
- !isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2752
+ !isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2110
2753
  "button",
2111
2754
  {
2112
2755
  onClick: () => setShowReportModal(true),
@@ -2139,15 +2782,15 @@ function QuizPlayer({
2139
2782
  },
2140
2783
  "data-testid": "button-report-question",
2141
2784
  children: [
2142
- /* @__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: [
2143
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
2144
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
2785
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
2787
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
2145
2788
  ] }),
2146
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Report" })
2789
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Report" })
2147
2790
  ]
2148
2791
  }
2149
2792
  ),
2150
- (currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
2793
+ (currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
2151
2794
  const isSelected = selectedAnswer === option;
2152
2795
  const isCorrectOption = currentQuestion.correctAnswer === option;
2153
2796
  let optionStyle = { ...defaultStyles.option };
@@ -2160,7 +2803,7 @@ function QuizPlayer({
2160
2803
  } else if (isSelected) {
2161
2804
  optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
2162
2805
  }
2163
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2806
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2164
2807
  "div",
2165
2808
  {
2166
2809
  style: {
@@ -2172,14 +2815,14 @@ function QuizPlayer({
2172
2815
  },
2173
2816
  onClick: () => !showFeedback && handleAnswerChange(option),
2174
2817
  children: [
2175
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
2176
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
2818
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
2819
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: option })
2177
2820
  ]
2178
2821
  },
2179
2822
  idx
2180
2823
  );
2181
2824
  }) }),
2182
- currentQuestion.type === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
2825
+ currentQuestion.type === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
2183
2826
  const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
2184
2827
  const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
2185
2828
  const isCorrectOption = correctAnswers.includes(option);
@@ -2193,7 +2836,7 @@ function QuizPlayer({
2193
2836
  } else if (selected) {
2194
2837
  optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
2195
2838
  }
2196
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2839
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2197
2840
  "div",
2198
2841
  {
2199
2842
  style: {
@@ -2213,14 +2856,14 @@ function QuizPlayer({
2213
2856
  }
2214
2857
  },
2215
2858
  children: [
2216
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
2217
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
2859
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
2860
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: option })
2218
2861
  ]
2219
2862
  },
2220
2863
  idx
2221
2864
  );
2222
2865
  }) }),
2223
- (currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2866
+ (currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2224
2867
  "textarea",
2225
2868
  {
2226
2869
  style: { ...defaultStyles.input, minHeight: currentQuestion.type === "essay" ? "150px" : "60px" },
@@ -2230,7 +2873,7 @@ function QuizPlayer({
2230
2873
  disabled: showFeedback
2231
2874
  }
2232
2875
  ),
2233
- currentQuestion.type === "fill" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2876
+ currentQuestion.type === "fill" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2234
2877
  "input",
2235
2878
  {
2236
2879
  style: defaultStyles.input,
@@ -2245,18 +2888,127 @@ function QuizPlayer({
2245
2888
  },
2246
2889
  idx
2247
2890
  )) }),
2248
- showFeedback && currentAnswerDetail && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
2891
+ currentQuestion.type === "sorting" && currentQuestion.items && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2892
+ SortingDragDrop,
2893
+ {
2894
+ items: currentQuestion.items,
2895
+ currentOrder: Array.isArray(selectedAnswer) ? selectedAnswer : currentQuestion.items.map((_, i) => i),
2896
+ correctOrder: currentQuestion.correctOrder,
2897
+ showFeedback,
2898
+ onOrderChange: handleAnswerChange
2899
+ }
2900
+ ),
2901
+ currentQuestion.type === "matrix" && currentQuestion.leftItems && currentQuestion.rightItems && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2902
+ MatchingDragDrop,
2903
+ {
2904
+ leftItems: currentQuestion.leftItems,
2905
+ rightItems: currentQuestion.rightItems,
2906
+ currentMatches: typeof selectedAnswer === "object" && selectedAnswer !== null && !Array.isArray(selectedAnswer) ? selectedAnswer : {},
2907
+ correctMatches: currentQuestion.correctMatches,
2908
+ showFeedback,
2909
+ onMatchChange: handleAnswerChange
2910
+ }
2911
+ ),
2912
+ currentQuestion.type === "assessment" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.options, children: (() => {
2913
+ const scaleType = currentQuestion.scaleType || "likert";
2914
+ if (scaleType === "yes-no") {
2915
+ const options = ["Yes", "No"];
2916
+ return options.map((option, idx) => {
2917
+ const isSelected = selectedAnswer === option;
2918
+ let optionStyle = { ...defaultStyles.option };
2919
+ if (isSelected) {
2920
+ optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
2921
+ }
2922
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2923
+ "div",
2924
+ {
2925
+ style: {
2926
+ ...optionStyle,
2927
+ cursor: showFeedback ? "default" : "pointer",
2928
+ display: "flex",
2929
+ alignItems: "center",
2930
+ gap: "8px"
2931
+ },
2932
+ onClick: () => !showFeedback && handleAnswerChange(option),
2933
+ "data-testid": `assessment-option-${option.toLowerCase()}`,
2934
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: option })
2935
+ },
2936
+ idx
2937
+ );
2938
+ });
2939
+ }
2940
+ if (scaleType === "rating") {
2941
+ const min = currentQuestion.scaleMin || 1;
2942
+ const max = currentQuestion.scaleMax || 5;
2943
+ const ratings = Array.from({ length: max - min + 1 }, (_, i) => min + i);
2944
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", justifyContent: "center" }, children: ratings.map((rating) => {
2945
+ const isSelected = selectedAnswer === rating;
2946
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2947
+ "button",
2948
+ {
2949
+ onClick: () => !showFeedback && handleAnswerChange(rating),
2950
+ disabled: showFeedback,
2951
+ style: {
2952
+ width: "48px",
2953
+ height: "48px",
2954
+ borderRadius: "50%",
2955
+ border: isSelected ? "2px solid #6721b0" : "2px solid #e5e7eb",
2956
+ backgroundColor: isSelected ? "#f3e8ff" : "#ffffff",
2957
+ cursor: showFeedback ? "not-allowed" : "pointer",
2958
+ fontSize: "18px",
2959
+ fontWeight: "600",
2960
+ color: isSelected ? "#6721b0" : "#374151"
2961
+ },
2962
+ "data-testid": `assessment-rating-${rating}`,
2963
+ children: rating
2964
+ },
2965
+ rating
2966
+ );
2967
+ }) });
2968
+ }
2969
+ const likertOptions = [
2970
+ "Strongly Disagree",
2971
+ "Disagree",
2972
+ "Neutral",
2973
+ "Agree",
2974
+ "Strongly Agree"
2975
+ ];
2976
+ return likertOptions.map((option, idx) => {
2977
+ const isSelected = selectedAnswer === option;
2978
+ let optionStyle = { ...defaultStyles.option };
2979
+ if (isSelected) {
2980
+ optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
2981
+ }
2982
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2983
+ "div",
2984
+ {
2985
+ style: {
2986
+ ...optionStyle,
2987
+ cursor: showFeedback ? "default" : "pointer",
2988
+ display: "flex",
2989
+ alignItems: "center",
2990
+ gap: "8px"
2991
+ },
2992
+ onClick: () => !showFeedback && handleAnswerChange(option),
2993
+ "data-testid": `assessment-likert-${idx}`,
2994
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 }, children: option })
2995
+ },
2996
+ idx
2997
+ );
2998
+ });
2999
+ })() }),
3000
+ showFeedback && currentAnswerDetail && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
2249
3001
  ...defaultStyles.feedback,
2250
- ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
3002
+ ...currentQuestion.type === "assessment" ? defaultStyles.feedbackNeutral : currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
2251
3003
  }, children: [
2252
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
3004
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
2253
3005
  ...defaultStyles.feedbackTitle,
2254
- ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
2255
- }, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
2256
- currentQuestion.explanation && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
3006
+ ...currentQuestion.type === "assessment" ? defaultStyles.feedbackTitleNeutral : currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
3007
+ }, children: currentQuestion.type === "assessment" ? "\u2713 Response recorded" : currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
3008
+ currentQuestion.explanation && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
2257
3009
  ] })
2258
3010
  ] }),
2259
- showSkipModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
3011
+ showSkipModal && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
2260
3012
  position: "fixed",
2261
3013
  top: 0,
2262
3014
  left: 0,
@@ -2267,7 +3019,7 @@ function QuizPlayer({
2267
3019
  alignItems: "center",
2268
3020
  justifyContent: "center",
2269
3021
  zIndex: 1e3
2270
- }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
3022
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
2271
3023
  backgroundColor: "#ffffff",
2272
3024
  borderRadius: "12px",
2273
3025
  padding: "24px",
@@ -2275,10 +3027,10 @@ function QuizPlayer({
2275
3027
  width: "90%",
2276
3028
  boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
2277
3029
  }, children: [
2278
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
2279
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "Help us improve by telling us why you're skipping this question." }),
2280
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
2281
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3030
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
3031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "Help us improve by telling us why you're skipping this question." }),
3032
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
3033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2282
3034
  "button",
2283
3035
  {
2284
3036
  onClick: () => setSelectedSkipReason("question_issue"),
@@ -2299,7 +3051,7 @@ function QuizPlayer({
2299
3051
  children: "Question has an issue"
2300
3052
  }
2301
3053
  ),
2302
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3054
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2303
3055
  "button",
2304
3056
  {
2305
3057
  onClick: () => setSelectedSkipReason("dont_know"),
@@ -2321,9 +3073,9 @@ function QuizPlayer({
2321
3073
  }
2322
3074
  )
2323
3075
  ] }),
2324
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
2325
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
2326
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3076
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "16px" }, children: [
3077
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
3078
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2327
3079
  "textarea",
2328
3080
  {
2329
3081
  value: skipComment,
@@ -2344,13 +3096,13 @@ function QuizPlayer({
2344
3096
  "data-testid": "input-skip-comment"
2345
3097
  }
2346
3098
  ),
2347
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
3099
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
2348
3100
  skipComment.length,
2349
3101
  "/200"
2350
3102
  ] })
2351
3103
  ] }),
2352
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
2353
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3104
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
3105
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2354
3106
  "button",
2355
3107
  {
2356
3108
  onClick: () => {
@@ -2373,7 +3125,7 @@ function QuizPlayer({
2373
3125
  children: "Cancel"
2374
3126
  }
2375
3127
  ),
2376
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3128
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2377
3129
  "button",
2378
3130
  {
2379
3131
  onClick: () => selectedSkipReason && handleSkipQuestion(selectedSkipReason, skipComment),
@@ -2396,7 +3148,7 @@ function QuizPlayer({
2396
3148
  )
2397
3149
  ] })
2398
3150
  ] }) }),
2399
- showReportModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
3151
+ showReportModal && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
2400
3152
  position: "fixed",
2401
3153
  top: 0,
2402
3154
  left: 0,
@@ -2407,7 +3159,7 @@ function QuizPlayer({
2407
3159
  alignItems: "center",
2408
3160
  justifyContent: "center",
2409
3161
  zIndex: 1e3
2410
- }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
3162
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
2411
3163
  backgroundColor: "#ffffff",
2412
3164
  borderRadius: "12px",
2413
3165
  padding: "24px",
@@ -2415,10 +3167,10 @@ function QuizPlayer({
2415
3167
  width: "90%",
2416
3168
  boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
2417
3169
  }, children: [
2418
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
2419
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
2420
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3170
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
3171
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
3172
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "16px" }, children: [
3173
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2422
3174
  "textarea",
2423
3175
  {
2424
3176
  value: reportComment,
@@ -2439,13 +3191,13 @@ function QuizPlayer({
2439
3191
  "data-testid": "input-report-comment"
2440
3192
  }
2441
3193
  ),
2442
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
3194
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
2443
3195
  reportComment.length,
2444
3196
  "/300"
2445
3197
  ] })
2446
3198
  ] }),
2447
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
2448
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3199
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
3200
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2449
3201
  "button",
2450
3202
  {
2451
3203
  onClick: () => {
@@ -2467,7 +3219,7 @@ function QuizPlayer({
2467
3219
  children: "Cancel"
2468
3220
  }
2469
3221
  ),
2470
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3222
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2471
3223
  "button",
2472
3224
  {
2473
3225
  onClick: () => handleReportQuestion(reportComment),
@@ -2490,8 +3242,8 @@ function QuizPlayer({
2490
3242
  )
2491
3243
  ] })
2492
3244
  ] }) }),
2493
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.buttonsColumn, children: [
2494
- showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3245
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles.buttonsColumn, children: [
3246
+ showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2495
3247
  "button",
2496
3248
  {
2497
3249
  style: {
@@ -2501,10 +3253,10 @@ function QuizPlayer({
2501
3253
  onClick: handleAddMoreQuestions,
2502
3254
  disabled: isGeneratingExtra,
2503
3255
  "data-testid": "button-add-more-questions",
2504
- children: isGeneratingExtra ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2505
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }),
3256
+ children: isGeneratingExtra ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
3257
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Spinner, { size: 16, color: "#9ca3af" }),
2506
3258
  "Generating Questions..."
2507
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3259
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2508
3260
  "+ Add ",
2509
3261
  questionsToAdd,
2510
3262
  " More Question",
@@ -2512,9 +3264,9 @@ function QuizPlayer({
2512
3264
  ] })
2513
3265
  }
2514
3266
  ),
2515
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
3267
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
2516
3268
  // After viewing feedback
2517
- isLastQuestion ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3269
+ isLastQuestion ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2518
3270
  "button",
2519
3271
  {
2520
3272
  style: {
@@ -2524,9 +3276,9 @@ function QuizPlayer({
2524
3276
  onClick: handleSubmit,
2525
3277
  disabled: isSubmitting || isGeneratingExtra,
2526
3278
  "data-testid": "button-submit-quiz",
2527
- children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
3279
+ children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
2528
3280
  }
2529
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3281
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2530
3282
  "button",
2531
3283
  {
2532
3284
  style: {
@@ -2540,7 +3292,7 @@ function QuizPlayer({
2540
3292
  )
2541
3293
  ) : (
2542
3294
  // Before checking answer
2543
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3295
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2544
3296
  "button",
2545
3297
  {
2546
3298
  style: {
@@ -2550,13 +3302,13 @@ function QuizPlayer({
2550
3302
  onClick: handleCheckAnswer,
2551
3303
  disabled: isNavigating || selectedAnswer === void 0,
2552
3304
  "data-testid": "button-check-answer",
2553
- children: isNavigating ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
3305
+ children: isNavigating ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
2554
3306
  }
2555
3307
  )
2556
3308
  ) })
2557
3309
  ] })
2558
3310
  ] }),
2559
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.chatPanel, children: apiClient.current && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3311
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles.chatPanel, children: apiClient.current && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2560
3312
  QuestionChatPanel,
2561
3313
  {
2562
3314
  apiClient: apiClient.current,
@@ -2574,7 +3326,7 @@ function QuizPlayer({
2574
3326
  lessonId,
2575
3327
  courseId,
2576
3328
  answerResult: showFeedback && currentAnswerDetail ? {
2577
- wasIncorrect: !currentAnswerDetail.isCorrect,
3329
+ wasIncorrect: currentQuestion.type !== "assessment" && !currentAnswerDetail.isCorrect,
2578
3330
  selectedAnswer: typeof selectedAnswer === "string" ? selectedAnswer : Array.isArray(selectedAnswer) ? selectedAnswer.join(", ") : void 0,
2579
3331
  correctAnswer: typeof currentQuestion.correctAnswer === "string" ? currentQuestion.correctAnswer : Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer.join(", ") : void 0,
2580
3332
  explanation: currentQuestion.explanation
@@ -2586,7 +3338,7 @@ function QuizPlayer({
2586
3338
 
2587
3339
  // src/AttemptViewer.tsx
2588
3340
  var import_react4 = require("react");
2589
- var import_jsx_runtime4 = require("react/jsx-runtime");
3341
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2590
3342
  var defaultStyles2 = {
2591
3343
  container: {
2592
3344
  fontFamily: "system-ui, -apple-system, sans-serif",
@@ -2782,10 +3534,31 @@ var spinnerKeyframes = `
2782
3534
  to { transform: rotate(360deg); }
2783
3535
  }
2784
3536
  `;
2785
- function formatAnswer(answer) {
3537
+ function formatAnswer(answer, questionType, items, leftItems) {
2786
3538
  if (answer === null || answer === void 0) {
2787
3539
  return "No answer";
2788
3540
  }
3541
+ if (questionType === "sorting" && Array.isArray(answer)) {
3542
+ const indices = answer;
3543
+ if (items && items.length > 0) {
3544
+ return indices.map((idx, pos) => `${pos + 1}. ${items[idx] ?? `Item ${idx + 1}`}`).join(", ");
3545
+ }
3546
+ return indices.map((idx, pos) => `Position ${pos + 1}: Item ${idx + 1}`).join(", ");
3547
+ }
3548
+ if (questionType === "matrix" && typeof answer === "object" && !Array.isArray(answer)) {
3549
+ const matches = answer;
3550
+ if (leftItems && leftItems.length > 0) {
3551
+ return leftItems.map((left) => {
3552
+ const right = matches[left];
3553
+ return `${left} \u2192 ${right || "No answer"}`;
3554
+ }).join(", ");
3555
+ }
3556
+ const entries = Object.entries(matches);
3557
+ if (entries.length === 0) {
3558
+ return "No answer";
3559
+ }
3560
+ return entries.map(([left, right]) => `${left} \u2192 ${right || "No answer"}`).join(", ");
3561
+ }
2789
3562
  if (typeof answer === "string") {
2790
3563
  return answer;
2791
3564
  }
@@ -2797,6 +3570,15 @@ function formatAnswer(answer) {
2797
3570
  }
2798
3571
  return String(answer);
2799
3572
  }
3573
+ function formatCorrectSortingAnswer(items, correctOrder) {
3574
+ return correctOrder.map((idx, pos) => `${pos + 1}. ${items[idx] ?? `Item ${idx + 1}`}`).join(", ");
3575
+ }
3576
+ function formatCorrectMatrixAnswer(correctMatches, leftItems) {
3577
+ if (leftItems && leftItems.length > 0) {
3578
+ return leftItems.map((left) => `${left} \u2192 ${correctMatches[left] || "N/A"}`).join(", ");
3579
+ }
3580
+ return Object.entries(correctMatches).map(([left, right]) => `${left} \u2192 ${right}`).join(", ");
3581
+ }
2800
3582
  function AttemptViewer({
2801
3583
  attemptId,
2802
3584
  apiBaseUrl,
@@ -2809,23 +3591,39 @@ function AttemptViewer({
2809
3591
  }) {
2810
3592
  const [attempt, setAttempt] = (0, import_react4.useState)(null);
2811
3593
  const [loading, setLoading] = (0, import_react4.useState)(true);
2812
- const [error, setError] = (0, import_react4.useState)(null);
3594
+ const [errorCode, setErrorCode] = (0, import_react4.useState)(null);
2813
3595
  const [chatHistories, setChatHistories] = (0, import_react4.useState)({});
2814
3596
  const [expandedChats, setExpandedChats] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
3597
+ const [fetchedAttemptId, setFetchedAttemptId] = (0, import_react4.useState)(null);
3598
+ const onErrorRef = (0, import_react4.useRef)(onError);
3599
+ onErrorRef.current = onError;
2815
3600
  (0, import_react4.useEffect)(() => {
3601
+ if (fetchedAttemptId === attemptId) return;
2816
3602
  const apiClient = new QuizApiClient({
2817
3603
  baseUrl: apiBaseUrl,
2818
3604
  authToken
2819
3605
  });
2820
3606
  async function fetchAttempt() {
2821
3607
  setLoading(true);
2822
- setError(null);
3608
+ setErrorCode(null);
2823
3609
  try {
2824
3610
  const response = await fetch(`${apiBaseUrl}/api/external/quiz-attempts/${attemptId}`, {
2825
3611
  headers: authToken ? { Authorization: `Bearer ${authToken}` } : {}
2826
3612
  });
2827
3613
  if (!response.ok) {
2828
- throw new Error(`Failed to fetch attempt: ${response.statusText}`);
3614
+ const code = getErrorFromHttpStatus(response.status, "attempt");
3615
+ setErrorCode(code);
3616
+ apiClient.logError({
3617
+ errorCode: code,
3618
+ context: "attempt",
3619
+ resourceId: attemptId,
3620
+ component: "AttemptViewer",
3621
+ message: `HTTP ${response.status}: ${response.statusText}`
3622
+ });
3623
+ onErrorRef.current?.(new Error(`Failed to fetch attempt: ${response.statusText}`));
3624
+ setLoading(false);
3625
+ setFetchedAttemptId(attemptId);
3626
+ return;
2829
3627
  }
2830
3628
  const data = await response.json();
2831
3629
  setAttempt(data);
@@ -2839,14 +3637,23 @@ function AttemptViewer({
2839
3637
  }
2840
3638
  } catch (err) {
2841
3639
  const errorMessage = err instanceof Error ? err.message : "Failed to load attempt";
2842
- setError(errorMessage);
2843
- onError?.(err instanceof Error ? err : new Error(errorMessage));
3640
+ const code = getErrorFromMessage(errorMessage, "attempt");
3641
+ setErrorCode(code);
3642
+ apiClient.logError({
3643
+ errorCode: code,
3644
+ context: "attempt",
3645
+ resourceId: attemptId,
3646
+ component: "AttemptViewer",
3647
+ message: errorMessage
3648
+ });
3649
+ onErrorRef.current?.(err instanceof Error ? err : new Error(errorMessage));
2844
3650
  } finally {
2845
3651
  setLoading(false);
3652
+ setFetchedAttemptId(attemptId);
2846
3653
  }
2847
3654
  }
2848
3655
  fetchAttempt();
2849
- }, [attemptId, apiBaseUrl, authToken, onError, showConversation]);
3656
+ }, [attemptId, apiBaseUrl, authToken, showConversation, fetchedAttemptId]);
2850
3657
  const toggleChatExpanded = (questionId) => {
2851
3658
  setExpandedChats((prev) => {
2852
3659
  const newSet = new Set(prev);
@@ -2860,53 +3667,49 @@ function AttemptViewer({
2860
3667
  };
2861
3668
  const handleRetry = () => {
2862
3669
  setLoading(true);
2863
- setError(null);
3670
+ setErrorCode(null);
2864
3671
  window.location.reload();
2865
3672
  };
2866
3673
  if (loading) {
2867
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.container, className, children: [
2868
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: spinnerKeyframes }),
2869
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.loading, children: [
2870
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.spinner }),
2871
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { marginTop: "16px", color: "#6b7280" }, children: "Loading attempt..." })
3674
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.container, className, children: [
3675
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: spinnerKeyframes }),
3676
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.loading, children: [
3677
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.spinner }),
3678
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { marginTop: "16px", color: "#6b7280" }, children: "Loading attempt..." })
2872
3679
  ] })
2873
3680
  ] });
2874
3681
  }
2875
- if (error || !attempt) {
2876
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.container, className, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.error, children: [
2877
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { fontSize: "18px", fontWeight: "500" }, children: "Failed to load attempt" }),
2878
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { marginTop: "8px", color: "#6b7280" }, children: error }),
2879
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { style: defaultStyles2.retryButton, onClick: handleRetry, children: "Try Again" })
2880
- ] }) });
3682
+ if (errorCode || !attempt) {
3683
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.container, className, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MaintenanceScreen, { errorCode: errorCode || "ATTEMPT_NOT_FOUND" }) });
2881
3684
  }
2882
3685
  const scorePercentage = attempt.score ?? 0;
2883
3686
  const correctCount = attempt.correctAnswers ?? 0;
2884
3687
  const totalQuestions = attempt.totalQuestions;
2885
3688
  const timeSpent = attempt.timeSpentSeconds ?? 0;
2886
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.container, className, children: [
2887
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: spinnerKeyframes }),
2888
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.header, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryGrid, children: [
2889
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
2890
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryValue, children: [
3689
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.container, className, children: [
3690
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: spinnerKeyframes }),
3691
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.header, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryGrid, children: [
3692
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
3693
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryValue, children: [
2891
3694
  scorePercentage,
2892
3695
  "%"
2893
3696
  ] }),
2894
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Score" })
3697
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Score" })
2895
3698
  ] }),
2896
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
2897
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryValue, children: [
3699
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
3700
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryValue, children: [
2898
3701
  correctCount,
2899
3702
  "/",
2900
3703
  totalQuestions
2901
3704
  ] }),
2902
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Correct" })
3705
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Correct" })
2903
3706
  ] }),
2904
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
2905
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.summaryValue, children: formatTime(timeSpent) }),
2906
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Time" })
3707
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.summaryCard, children: [
3708
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.summaryValue, children: formatTime(timeSpent) }),
3709
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.summaryLabel, children: "Time" })
2907
3710
  ] })
2908
3711
  ] }) }),
2909
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.questionsList, children: attempt.answers.map((answer, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3712
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.questionsList, children: attempt.answers.map((answer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2910
3713
  "div",
2911
3714
  {
2912
3715
  style: {
@@ -2914,12 +3717,12 @@ function AttemptViewer({
2914
3717
  ...answer.isCorrect ? defaultStyles2.questionCardCorrect : defaultStyles2.questionCardIncorrect
2915
3718
  },
2916
3719
  children: [
2917
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.questionHeader, children: [
2918
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: defaultStyles2.questionNumber, children: [
3720
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.questionHeader, children: [
3721
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: defaultStyles2.questionNumber, children: [
2919
3722
  "Question ",
2920
3723
  index + 1
2921
3724
  ] }),
2922
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3725
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2923
3726
  "span",
2924
3727
  {
2925
3728
  style: {
@@ -2930,35 +3733,35 @@ function AttemptViewer({
2930
3733
  }
2931
3734
  )
2932
3735
  ] }),
2933
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.questionText, children: answer.questionText }),
2934
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.answerSection, children: [
2935
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.answerLabel, children: "Your answer:" }),
2936
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer) })
3736
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.questionText, children: answer.questionText }),
3737
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.answerSection, children: [
3738
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: defaultStyles2.answerLabel, children: "Your answer:" }),
3739
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer, answer.questionType, answer.items, answer.leftItems) })
2937
3740
  ] }),
2938
- !answer.isCorrect && answer.correctAnswer && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.answerSection, children: [
2939
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.answerLabel, children: "Correct answer:" }),
2940
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: defaultStyles2.correctAnswer, children: formatAnswer(answer.correctAnswer) })
3741
+ !answer.isCorrect && answer.correctAnswer && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.answerSection, children: [
3742
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: defaultStyles2.answerLabel, children: "Correct answer:" }),
3743
+ /* @__PURE__ */ (0, import_jsx_runtime5.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) })
2941
3744
  ] }),
2942
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.points, children: [
3745
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.points, children: [
2943
3746
  answer.pointsEarned,
2944
3747
  " / ",
2945
3748
  answer.points,
2946
3749
  " points"
2947
3750
  ] }),
2948
- showExplanations && answer.explanation && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.explanation, children: [
2949
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "Explanation:" }),
3751
+ showExplanations && answer.explanation && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.explanation, children: [
3752
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Explanation:" }),
2950
3753
  " ",
2951
3754
  answer.explanation
2952
3755
  ] }),
2953
- showConversation && chatHistories[answer.questionId] && chatHistories[answer.questionId].messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.chatHistorySection, children: [
2954
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3756
+ showConversation && chatHistories[answer.questionId] && chatHistories[answer.questionId].messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: defaultStyles2.chatHistorySection, children: [
3757
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2955
3758
  "button",
2956
3759
  {
2957
3760
  style: defaultStyles2.chatToggleButton,
2958
3761
  onClick: () => toggleChatExpanded(answer.questionId),
2959
3762
  "data-testid": `button-toggle-chat-${answer.questionId}`,
2960
3763
  children: [
2961
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }),
3764
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }),
2962
3765
  expandedChats.has(answer.questionId) ? "Hide" : "View",
2963
3766
  " Chat History (",
2964
3767
  chatHistories[answer.questionId].messages.length,
@@ -2966,7 +3769,7 @@ function AttemptViewer({
2966
3769
  ]
2967
3770
  }
2968
3771
  ),
2969
- expandedChats.has(answer.questionId) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.chatMessages, children: chatHistories[answer.questionId].messages.map((msg, msgIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3772
+ expandedChats.has(answer.questionId) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: defaultStyles2.chatMessages, children: chatHistories[answer.questionId].messages.map((msg, msgIndex) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2970
3773
  "div",
2971
3774
  {
2972
3775
  style: {
@@ -2984,15 +3787,621 @@ function AttemptViewer({
2984
3787
  )) })
2985
3788
  ] });
2986
3789
  }
3790
+
3791
+ // src/ErrorTypesPanel.tsx
3792
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3793
+ var panelStyles2 = {
3794
+ container: {
3795
+ fontFamily: "system-ui, -apple-system, sans-serif",
3796
+ padding: "24px",
3797
+ backgroundColor: "#ffffff",
3798
+ borderRadius: "12px",
3799
+ maxWidth: "800px"
3800
+ },
3801
+ header: {
3802
+ marginBottom: "24px"
3803
+ },
3804
+ title: {
3805
+ fontSize: "20px",
3806
+ fontWeight: "600",
3807
+ color: "#111827",
3808
+ marginBottom: "8px"
3809
+ },
3810
+ subtitle: {
3811
+ fontSize: "14px",
3812
+ color: "#6b7280"
3813
+ },
3814
+ section: {
3815
+ marginBottom: "24px"
3816
+ },
3817
+ sectionTitle: {
3818
+ fontSize: "14px",
3819
+ fontWeight: "600",
3820
+ color: "#374151",
3821
+ marginBottom: "12px",
3822
+ textTransform: "uppercase",
3823
+ letterSpacing: "0.05em"
3824
+ },
3825
+ errorList: {
3826
+ display: "flex",
3827
+ flexDirection: "column",
3828
+ gap: "12px"
3829
+ },
3830
+ errorCard: {
3831
+ padding: "16px",
3832
+ backgroundColor: "#f9fafb",
3833
+ borderRadius: "8px",
3834
+ border: "1px solid #e5e7eb"
3835
+ },
3836
+ errorCardBlocking: {
3837
+ borderLeft: "4px solid #ef4444"
3838
+ },
3839
+ errorCardNonBlocking: {
3840
+ borderLeft: "4px solid #f59e0b"
3841
+ },
3842
+ errorHeader: {
3843
+ display: "flex",
3844
+ justifyContent: "space-between",
3845
+ alignItems: "flex-start",
3846
+ marginBottom: "8px"
3847
+ },
3848
+ errorCode: {
3849
+ fontSize: "13px",
3850
+ fontWeight: "600",
3851
+ color: "#1f2937",
3852
+ fontFamily: "monospace",
3853
+ backgroundColor: "#e5e7eb",
3854
+ padding: "2px 8px",
3855
+ borderRadius: "4px"
3856
+ },
3857
+ errorBadge: {
3858
+ fontSize: "11px",
3859
+ fontWeight: "500",
3860
+ padding: "2px 8px",
3861
+ borderRadius: "12px"
3862
+ },
3863
+ blockingBadge: {
3864
+ backgroundColor: "#fee2e2",
3865
+ color: "#dc2626"
3866
+ },
3867
+ nonBlockingBadge: {
3868
+ backgroundColor: "#fef3c7",
3869
+ color: "#d97706"
3870
+ },
3871
+ userMessage: {
3872
+ fontSize: "15px",
3873
+ fontWeight: "500",
3874
+ color: "#111827",
3875
+ marginBottom: "4px"
3876
+ },
3877
+ subMessage: {
3878
+ fontSize: "13px",
3879
+ color: "#6b7280",
3880
+ marginBottom: "8px"
3881
+ },
3882
+ causeLabel: {
3883
+ fontSize: "11px",
3884
+ fontWeight: "600",
3885
+ color: "#9ca3af",
3886
+ textTransform: "uppercase",
3887
+ marginBottom: "4px"
3888
+ },
3889
+ causeText: {
3890
+ fontSize: "13px",
3891
+ color: "#4b5563",
3892
+ fontStyle: "italic"
3893
+ }
3894
+ };
3895
+ function ErrorTypesPanel({ className }) {
3896
+ const blockingErrors = Object.values(ERROR_DEFINITIONS).filter((e) => e.isBlocking);
3897
+ const nonBlockingErrors = Object.values(ERROR_DEFINITIONS).filter((e) => !e.isBlocking);
3898
+ const renderErrorCard = (error) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3899
+ "div",
3900
+ {
3901
+ style: {
3902
+ ...panelStyles2.errorCard,
3903
+ ...error.isBlocking ? panelStyles2.errorCardBlocking : panelStyles2.errorCardNonBlocking
3904
+ },
3905
+ "data-testid": `error-card-${error.code}`,
3906
+ children: [
3907
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: panelStyles2.errorHeader, children: [
3908
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { style: panelStyles2.errorCode, children: error.code }),
3909
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3910
+ "span",
3911
+ {
3912
+ style: {
3913
+ ...panelStyles2.errorBadge,
3914
+ ...error.isBlocking ? panelStyles2.blockingBadge : panelStyles2.nonBlockingBadge
3915
+ },
3916
+ children: error.isBlocking ? "Blocking" : "Non-Blocking"
3917
+ }
3918
+ )
3919
+ ] }),
3920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.userMessage, children: error.userMessage }),
3921
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.subMessage, children: error.subMessage }),
3922
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.causeLabel, children: "Why this happens:" }),
3923
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.causeText, children: error.cause })
3924
+ ]
3925
+ },
3926
+ error.code
3927
+ );
3928
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: panelStyles2.container, className, "data-testid": "error-types-panel", children: [
3929
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: panelStyles2.header, children: [
3930
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { style: panelStyles2.title, children: "Error Types Reference" }),
3931
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: panelStyles2.subtitle, children: "List of all error types that can occur in the quiz components, with user-facing messages and technical causes." })
3932
+ ] }),
3933
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: panelStyles2.section, children: [
3934
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h3", { style: panelStyles2.sectionTitle, children: [
3935
+ "Blocking Errors (",
3936
+ blockingErrors.length,
3937
+ ")"
3938
+ ] }),
3939
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { ...panelStyles2.subtitle, marginBottom: "12px" }, children: "These errors prevent the quiz from loading or continuing. Users see a full-screen error message." }),
3940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.errorList, children: blockingErrors.map(renderErrorCard) })
3941
+ ] }),
3942
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: panelStyles2.section, children: [
3943
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h3", { style: panelStyles2.sectionTitle, children: [
3944
+ "Non-Blocking Errors (",
3945
+ nonBlockingErrors.length,
3946
+ ")"
3947
+ ] }),
3948
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { ...panelStyles2.subtitle, marginBottom: "12px" }, children: "These errors affect specific features but allow the quiz to continue. Users may see a toast notification." }),
3949
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: panelStyles2.errorList, children: nonBlockingErrors.map(renderErrorCard) })
3950
+ ] })
3951
+ ] });
3952
+ }
3953
+
3954
+ // src/ErrorLogsPanel.tsx
3955
+ var import_react5 = require("react");
3956
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3957
+ var panelStyles3 = {
3958
+ container: {
3959
+ fontFamily: "system-ui, -apple-system, sans-serif",
3960
+ padding: "24px",
3961
+ backgroundColor: "#ffffff",
3962
+ borderRadius: "12px",
3963
+ maxWidth: "1000px"
3964
+ },
3965
+ header: {
3966
+ marginBottom: "24px"
3967
+ },
3968
+ title: {
3969
+ fontSize: "20px",
3970
+ fontWeight: "600",
3971
+ color: "#111827",
3972
+ marginBottom: "8px"
3973
+ },
3974
+ subtitle: {
3975
+ fontSize: "14px",
3976
+ color: "#6b7280"
3977
+ },
3978
+ tabs: {
3979
+ display: "flex",
3980
+ gap: "8px",
3981
+ marginBottom: "16px",
3982
+ borderBottom: "1px solid #e5e7eb",
3983
+ paddingBottom: "12px"
3984
+ },
3985
+ tab: {
3986
+ padding: "8px 16px",
3987
+ fontSize: "14px",
3988
+ fontWeight: "500",
3989
+ borderRadius: "6px",
3990
+ cursor: "pointer",
3991
+ border: "none",
3992
+ backgroundColor: "transparent",
3993
+ color: "#6b7280"
3994
+ },
3995
+ tabActive: {
3996
+ backgroundColor: "#6721b0",
3997
+ color: "#ffffff"
3998
+ },
3999
+ stats: {
4000
+ display: "flex",
4001
+ gap: "16px",
4002
+ marginBottom: "20px"
4003
+ },
4004
+ statCard: {
4005
+ padding: "12px 16px",
4006
+ backgroundColor: "#f9fafb",
4007
+ borderRadius: "8px",
4008
+ flex: 1
4009
+ },
4010
+ statValue: {
4011
+ fontSize: "24px",
4012
+ fontWeight: "700",
4013
+ color: "#111827"
4014
+ },
4015
+ statLabel: {
4016
+ fontSize: "12px",
4017
+ color: "#6b7280",
4018
+ textTransform: "uppercase",
4019
+ letterSpacing: "0.05em"
4020
+ },
4021
+ errorList: {
4022
+ display: "flex",
4023
+ flexDirection: "column",
4024
+ gap: "12px"
4025
+ },
4026
+ errorCard: {
4027
+ padding: "16px",
4028
+ backgroundColor: "#f9fafb",
4029
+ borderRadius: "8px",
4030
+ border: "1px solid #e5e7eb"
4031
+ },
4032
+ errorCardRecent: {
4033
+ borderLeft: "4px solid #ef4444",
4034
+ backgroundColor: "#fef2f2"
4035
+ },
4036
+ errorCardOld: {
4037
+ borderLeft: "4px solid #9ca3af",
4038
+ opacity: 0.7
4039
+ },
4040
+ errorHeader: {
4041
+ display: "flex",
4042
+ justifyContent: "space-between",
4043
+ alignItems: "flex-start",
4044
+ marginBottom: "8px"
4045
+ },
4046
+ errorLeft: {
4047
+ flex: 1
4048
+ },
4049
+ errorCode: {
4050
+ fontSize: "13px",
4051
+ fontWeight: "600",
4052
+ color: "#1f2937",
4053
+ fontFamily: "monospace",
4054
+ backgroundColor: "#e5e7eb",
4055
+ padding: "2px 8px",
4056
+ borderRadius: "4px",
4057
+ display: "inline-block"
4058
+ },
4059
+ countBadge: {
4060
+ fontSize: "14px",
4061
+ fontWeight: "700",
4062
+ padding: "4px 12px",
4063
+ borderRadius: "16px",
4064
+ backgroundColor: "#ef4444",
4065
+ color: "#ffffff"
4066
+ },
4067
+ countBadgeLow: {
4068
+ backgroundColor: "#f59e0b"
4069
+ },
4070
+ contextBadge: {
4071
+ fontSize: "11px",
4072
+ fontWeight: "500",
4073
+ padding: "2px 8px",
4074
+ borderRadius: "4px",
4075
+ marginLeft: "8px",
4076
+ backgroundColor: "#dbeafe",
4077
+ color: "#1d4ed8"
4078
+ },
4079
+ userMessage: {
4080
+ fontSize: "15px",
4081
+ fontWeight: "500",
4082
+ color: "#111827",
4083
+ marginTop: "8px",
4084
+ marginBottom: "4px"
4085
+ },
4086
+ resourceId: {
4087
+ fontSize: "12px",
4088
+ color: "#6b7280",
4089
+ fontFamily: "monospace",
4090
+ marginTop: "4px"
4091
+ },
4092
+ resourceLink: {
4093
+ color: "#6721b0",
4094
+ textDecoration: "underline",
4095
+ cursor: "pointer"
4096
+ },
4097
+ timestamps: {
4098
+ display: "flex",
4099
+ gap: "16px",
4100
+ marginTop: "8px",
4101
+ fontSize: "12px",
4102
+ color: "#9ca3af"
4103
+ },
4104
+ dismissButton: {
4105
+ padding: "6px 12px",
4106
+ fontSize: "12px",
4107
+ fontWeight: "500",
4108
+ borderRadius: "4px",
4109
+ cursor: "pointer",
4110
+ border: "1px solid #e5e7eb",
4111
+ backgroundColor: "#ffffff",
4112
+ color: "#6b7280",
4113
+ marginTop: "8px"
4114
+ },
4115
+ dismissButtonHover: {
4116
+ backgroundColor: "#f3f4f6"
4117
+ },
4118
+ emptyState: {
4119
+ textAlign: "center",
4120
+ padding: "40px",
4121
+ color: "#9ca3af",
4122
+ fontSize: "14px"
4123
+ },
4124
+ loading: {
4125
+ textAlign: "center",
4126
+ padding: "40px",
4127
+ color: "#6b7280",
4128
+ fontSize: "14px"
4129
+ },
4130
+ pagination: {
4131
+ display: "flex",
4132
+ justifyContent: "center",
4133
+ gap: "8px",
4134
+ marginTop: "20px"
4135
+ },
4136
+ pageButton: {
4137
+ padding: "8px 12px",
4138
+ fontSize: "14px",
4139
+ borderRadius: "4px",
4140
+ cursor: "pointer",
4141
+ border: "1px solid #e5e7eb",
4142
+ backgroundColor: "#ffffff",
4143
+ color: "#374151"
4144
+ },
4145
+ pageButtonDisabled: {
4146
+ opacity: 0.5,
4147
+ cursor: "not-allowed"
4148
+ }
4149
+ };
4150
+ function formatRelativeTime(dateStr) {
4151
+ const date = new Date(dateStr);
4152
+ const now = /* @__PURE__ */ new Date();
4153
+ const diffMs = now.getTime() - date.getTime();
4154
+ const diffMins = Math.floor(diffMs / 6e4);
4155
+ const diffHours = Math.floor(diffMs / 36e5);
4156
+ const diffDays = Math.floor(diffMs / 864e5);
4157
+ if (diffMins < 1) return "Just now";
4158
+ if (diffMins < 60) return `${diffMins}m ago`;
4159
+ if (diffHours < 24) return `${diffHours}h ago`;
4160
+ if (diffDays < 7) return `${diffDays}d ago`;
4161
+ return date.toLocaleDateString();
4162
+ }
4163
+ function isRecent(dateStr) {
4164
+ const date = new Date(dateStr);
4165
+ const now = /* @__PURE__ */ new Date();
4166
+ const diffMs = now.getTime() - date.getTime();
4167
+ const diffHours = diffMs / 36e5;
4168
+ return diffHours < 24;
4169
+ }
4170
+ function ErrorLogsPanel({ apiBaseUrl, authToken, onResourceClick }) {
4171
+ const [logs, setLogs] = (0, import_react5.useState)([]);
4172
+ const [loading, setLoading] = (0, import_react5.useState)(true);
4173
+ const [showDismissed, setShowDismissed] = (0, import_react5.useState)(false);
4174
+ const [page, setPage] = (0, import_react5.useState)(1);
4175
+ const [totalPages, setTotalPages] = (0, import_react5.useState)(1);
4176
+ const [total, setTotal] = (0, import_react5.useState)(0);
4177
+ (0, import_react5.useEffect)(() => {
4178
+ fetchLogs();
4179
+ }, [showDismissed, page]);
4180
+ async function fetchLogs() {
4181
+ setLoading(true);
4182
+ try {
4183
+ const params = new URLSearchParams();
4184
+ params.set("dismissed", showDismissed ? "true" : "false");
4185
+ params.set("page", page.toString());
4186
+ params.set("limit", "20");
4187
+ const response = await fetch(`${apiBaseUrl}/api/admin/error-logs?${params}`, {
4188
+ headers: authToken ? { Authorization: `Bearer ${authToken}` } : {}
4189
+ });
4190
+ if (response.ok) {
4191
+ const data = await response.json();
4192
+ setLogs(data.items || []);
4193
+ setTotalPages(data.totalPages || 1);
4194
+ setTotal(data.total || 0);
4195
+ }
4196
+ } catch (err) {
4197
+ console.error("Failed to fetch error logs:", err);
4198
+ } finally {
4199
+ setLoading(false);
4200
+ }
4201
+ }
4202
+ async function handleDismiss(id) {
4203
+ try {
4204
+ const response = await fetch(`${apiBaseUrl}/api/admin/error-logs/${id}/dismiss`, {
4205
+ method: "PATCH",
4206
+ headers: authToken ? { Authorization: `Bearer ${authToken}` } : {}
4207
+ });
4208
+ if (response.ok) {
4209
+ fetchLogs();
4210
+ }
4211
+ } catch (err) {
4212
+ console.error("Failed to dismiss error:", err);
4213
+ }
4214
+ }
4215
+ async function handleUndismiss(id) {
4216
+ try {
4217
+ const response = await fetch(`${apiBaseUrl}/api/admin/error-logs/${id}/undismiss`, {
4218
+ method: "PATCH",
4219
+ headers: authToken ? { Authorization: `Bearer ${authToken}` } : {}
4220
+ });
4221
+ if (response.ok) {
4222
+ fetchLogs();
4223
+ }
4224
+ } catch (err) {
4225
+ console.error("Failed to undismiss error:", err);
4226
+ }
4227
+ }
4228
+ function getErrorDefinition(code) {
4229
+ return ERROR_DEFINITIONS[code];
4230
+ }
4231
+ const totalErrors = logs.reduce((sum, log) => sum + log.count, 0);
4232
+ const recentCount = logs.filter((log) => isRecent(log.lastSeenAt)).length;
4233
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.container, "data-testid": "error-logs-panel", children: [
4234
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.header, children: [
4235
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: panelStyles3.title, children: "Error Tracking" }),
4236
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: panelStyles3.subtitle, children: "Aggregated errors from QuizPlayer and AttemptViewer components, sorted by occurrence count" })
4237
+ ] }),
4238
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.tabs, children: [
4239
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4240
+ "button",
4241
+ {
4242
+ style: {
4243
+ ...panelStyles3.tab,
4244
+ ...showDismissed ? {} : panelStyles3.tabActive
4245
+ },
4246
+ onClick: () => {
4247
+ setShowDismissed(false);
4248
+ setPage(1);
4249
+ },
4250
+ "data-testid": "tab-active-errors",
4251
+ children: "Active Errors"
4252
+ }
4253
+ ),
4254
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4255
+ "button",
4256
+ {
4257
+ style: {
4258
+ ...panelStyles3.tab,
4259
+ ...showDismissed ? panelStyles3.tabActive : {}
4260
+ },
4261
+ onClick: () => {
4262
+ setShowDismissed(true);
4263
+ setPage(1);
4264
+ },
4265
+ "data-testid": "tab-dismissed-errors",
4266
+ children: "Dismissed"
4267
+ }
4268
+ )
4269
+ ] }),
4270
+ !showDismissed && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.stats, children: [
4271
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.statCard, children: [
4272
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.statValue, children: total }),
4273
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.statLabel, children: "Unique Errors" })
4274
+ ] }),
4275
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.statCard, children: [
4276
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.statValue, children: totalErrors }),
4277
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.statLabel, children: "Total Occurrences" })
4278
+ ] }),
4279
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.statCard, children: [
4280
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { ...panelStyles3.statValue, color: recentCount > 0 ? "#ef4444" : "#22c55e" }, children: recentCount }),
4281
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.statLabel, children: "Last 24h" })
4282
+ ] })
4283
+ ] }),
4284
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.loading, children: "Loading..." }) : logs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.emptyState, children: showDismissed ? "No dismissed errors" : "No active errors" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.errorList, children: logs.map((log) => {
4285
+ const def = getErrorDefinition(log.errorCode);
4286
+ const recent = isRecent(log.lastSeenAt);
4287
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4288
+ "div",
4289
+ {
4290
+ style: {
4291
+ ...panelStyles3.errorCard,
4292
+ ...recent ? panelStyles3.errorCardRecent : panelStyles3.errorCardOld
4293
+ },
4294
+ "data-testid": `error-log-${log.id}`,
4295
+ children: [
4296
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.errorHeader, children: [
4297
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.errorLeft, children: [
4298
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: panelStyles3.errorCode, children: log.errorCode }),
4299
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: panelStyles3.contextBadge, children: log.context }),
4300
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...panelStyles3.contextBadge, backgroundColor: "#f3e8ff", color: "#7c3aed" }, children: log.component })
4301
+ ] }),
4302
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4303
+ "span",
4304
+ {
4305
+ style: {
4306
+ ...panelStyles3.countBadge,
4307
+ ...log.count < 10 ? panelStyles3.countBadgeLow : {}
4308
+ },
4309
+ children: [
4310
+ log.count,
4311
+ "x"
4312
+ ]
4313
+ }
4314
+ )
4315
+ ] }),
4316
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: panelStyles3.userMessage, children: def?.userMessage || log.lastMessage || "Unknown error" }),
4317
+ log.resourceId && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.resourceId, children: [
4318
+ "Resource:",
4319
+ " ",
4320
+ onResourceClick ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4321
+ "span",
4322
+ {
4323
+ style: panelStyles3.resourceLink,
4324
+ onClick: () => onResourceClick(log.resourceId, log.context),
4325
+ "data-testid": `link-resource-${log.id}`,
4326
+ children: log.resourceId
4327
+ }
4328
+ ) : log.resourceId
4329
+ ] }),
4330
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.timestamps, children: [
4331
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
4332
+ "First: ",
4333
+ formatRelativeTime(log.firstSeenAt)
4334
+ ] }),
4335
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
4336
+ "Last: ",
4337
+ formatRelativeTime(log.lastSeenAt)
4338
+ ] })
4339
+ ] }),
4340
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4341
+ "button",
4342
+ {
4343
+ style: panelStyles3.dismissButton,
4344
+ onClick: () => showDismissed ? handleUndismiss(log.id) : handleDismiss(log.id),
4345
+ "data-testid": `button-dismiss-${log.id}`,
4346
+ children: showDismissed ? "Restore" : "Dismiss"
4347
+ }
4348
+ )
4349
+ ]
4350
+ },
4351
+ log.id
4352
+ );
4353
+ }) }),
4354
+ totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: panelStyles3.pagination, children: [
4355
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4356
+ "button",
4357
+ {
4358
+ style: {
4359
+ ...panelStyles3.pageButton,
4360
+ ...page <= 1 ? panelStyles3.pageButtonDisabled : {}
4361
+ },
4362
+ onClick: () => setPage((p) => Math.max(1, p - 1)),
4363
+ disabled: page <= 1,
4364
+ "data-testid": "button-prev-page",
4365
+ children: "Previous"
4366
+ }
4367
+ ),
4368
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { padding: "8px 12px", color: "#6b7280" }, children: [
4369
+ "Page ",
4370
+ page,
4371
+ " of ",
4372
+ totalPages
4373
+ ] }),
4374
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4375
+ "button",
4376
+ {
4377
+ style: {
4378
+ ...panelStyles3.pageButton,
4379
+ ...page >= totalPages ? panelStyles3.pageButtonDisabled : {}
4380
+ },
4381
+ onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
4382
+ disabled: page >= totalPages,
4383
+ "data-testid": "button-next-page",
4384
+ children: "Next"
4385
+ }
4386
+ )
4387
+ ] })
4388
+ ] });
4389
+ }
2987
4390
  // Annotate the CommonJS export names for ESM import in node:
2988
4391
  0 && (module.exports = {
2989
4392
  AttemptViewer,
4393
+ ERROR_DEFINITIONS,
4394
+ ErrorLogsPanel,
4395
+ ErrorTypesPanel,
4396
+ MaintenanceScreen,
2990
4397
  QuizApiClient,
2991
4398
  QuizPlayer,
2992
4399
  TextToSpeech,
2993
4400
  calculateScore,
2994
4401
  checkAnswer,
2995
4402
  createAnswerDetail,
2996
- formatTime
4403
+ formatTime,
4404
+ getErrorFromHttpStatus,
4405
+ getErrorFromMessage
2997
4406
  });
2998
4407
  //# sourceMappingURL=index.js.map