@schoolio/player 1.3.0 → 1.4.0

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/QuizPlayer.tsx
2
- import { useState, useEffect, useCallback, useRef } from "react";
2
+ import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useRef as useRef3 } from "react";
3
3
 
4
4
  // src/api.ts
5
5
  var QuizApiClient = class {
@@ -51,6 +51,35 @@ var QuizApiClient = class {
51
51
  `/api/external/quiz-attempts?${queryParams.toString()}`
52
52
  );
53
53
  }
54
+ async skipQuestion(payload) {
55
+ return this.request(
56
+ "POST",
57
+ "/api/external/skip-question",
58
+ payload
59
+ );
60
+ }
61
+ async reportQuestion(payload) {
62
+ return this.request(
63
+ "POST",
64
+ "/api/external/report-question",
65
+ payload
66
+ );
67
+ }
68
+ async getStarterPrompts() {
69
+ return this.request("GET", "/api/external/question-chat/prompts");
70
+ }
71
+ async getOrCreateChatSession(params) {
72
+ return this.request("POST", "/api/external/question-chat/session", params);
73
+ }
74
+ async sendChatMessage(params) {
75
+ return this.request("POST", "/api/external/question-chat/message", params);
76
+ }
77
+ async getChatHistory(questionId, childId) {
78
+ return this.request(
79
+ "GET",
80
+ `/api/external/question-chat/${questionId}/${childId}`
81
+ );
82
+ }
54
83
  };
55
84
 
56
85
  // src/utils.ts
@@ -140,8 +169,597 @@ function formatTime(seconds) {
140
169
  return `${mins}:${secs.toString().padStart(2, "0")}`;
141
170
  }
142
171
 
172
+ // src/TextToSpeech.tsx
173
+ import { useState, useEffect, useRef } from "react";
174
+ import { jsx, jsxs } from "react/jsx-runtime";
175
+ var styles = {
176
+ container: {
177
+ display: "flex",
178
+ alignItems: "flex-start",
179
+ gap: "8px"
180
+ },
181
+ button: {
182
+ display: "inline-flex",
183
+ alignItems: "center",
184
+ justifyContent: "center",
185
+ border: "none",
186
+ borderRadius: "6px",
187
+ cursor: "pointer",
188
+ transition: "all 0.2s ease",
189
+ flexShrink: 0
190
+ },
191
+ buttonSm: {
192
+ width: "28px",
193
+ height: "28px",
194
+ padding: "4px",
195
+ backgroundColor: "transparent"
196
+ },
197
+ buttonMd: {
198
+ width: "32px",
199
+ height: "32px",
200
+ padding: "6px",
201
+ backgroundColor: "rgba(103, 33, 176, 0.1)"
202
+ },
203
+ buttonPlaying: {
204
+ backgroundColor: "rgba(103, 33, 176, 0.2)"
205
+ },
206
+ buttonDisabled: {
207
+ opacity: 0.4,
208
+ cursor: "not-allowed"
209
+ },
210
+ icon: {
211
+ width: "16px",
212
+ height: "16px",
213
+ color: "#6721b0"
214
+ },
215
+ textContainer: {
216
+ flex: 1
217
+ },
218
+ highlightedWord: {
219
+ backgroundColor: "rgba(103, 33, 176, 0.25)",
220
+ borderRadius: "3px",
221
+ padding: "0 2px",
222
+ transition: "background-color 0.15s ease",
223
+ fontWeight: 500
224
+ }
225
+ };
226
+ function VolumeIcon() {
227
+ return /* @__PURE__ */ jsxs(
228
+ "svg",
229
+ {
230
+ style: styles.icon,
231
+ viewBox: "0 0 24 24",
232
+ fill: "none",
233
+ stroke: "currentColor",
234
+ strokeWidth: "2",
235
+ strokeLinecap: "round",
236
+ strokeLinejoin: "round",
237
+ children: [
238
+ /* @__PURE__ */ jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
239
+ /* @__PURE__ */ jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
240
+ /* @__PURE__ */ jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
241
+ ]
242
+ }
243
+ );
244
+ }
245
+ function StopIcon() {
246
+ return /* @__PURE__ */ jsxs(
247
+ "svg",
248
+ {
249
+ style: styles.icon,
250
+ viewBox: "0 0 24 24",
251
+ fill: "none",
252
+ stroke: "currentColor",
253
+ strokeWidth: "2",
254
+ strokeLinecap: "round",
255
+ strokeLinejoin: "round",
256
+ children: [
257
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
258
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "6", height: "6" })
259
+ ]
260
+ }
261
+ );
262
+ }
263
+ function TextToSpeech({ text, inline = false, size = "sm" }) {
264
+ const [isPlaying, setIsPlaying] = useState(false);
265
+ const [currentWordIndex, setCurrentWordIndex] = useState(-1);
266
+ const [isSupported, setIsSupported] = useState(true);
267
+ const [bestVoice, setBestVoice] = useState(null);
268
+ const utteranceRef = useRef(null);
269
+ const wordsRef = useRef([]);
270
+ useEffect(() => {
271
+ wordsRef.current = text.split(/\s+/).filter((word) => word.length > 0);
272
+ }, [text]);
273
+ useEffect(() => {
274
+ if (typeof window === "undefined" || !("speechSynthesis" in window)) {
275
+ setIsSupported(false);
276
+ }
277
+ }, []);
278
+ useEffect(() => {
279
+ if (!isSupported || typeof window === "undefined") return;
280
+ const loadVoices = () => {
281
+ const voices = window.speechSynthesis.getVoices();
282
+ if (voices.length === 0) return;
283
+ const premiumVoices = [
284
+ "samantha",
285
+ "victoria",
286
+ "karen",
287
+ "serena",
288
+ "jenny",
289
+ "aria",
290
+ "emma",
291
+ "moira",
292
+ "fiona",
293
+ "alice"
294
+ ];
295
+ const englishVoices = voices.filter(
296
+ (voice) => voice.lang.startsWith("en-")
297
+ );
298
+ const scoredVoices = englishVoices.map((voice) => {
299
+ let score = 0;
300
+ const nameLower = voice.name.toLowerCase();
301
+ premiumVoices.forEach((premiumName, index) => {
302
+ if (nameLower.includes(premiumName)) {
303
+ score += (premiumVoices.length - index) * 10;
304
+ }
305
+ });
306
+ if (voice.localService) score += 8;
307
+ if (!nameLower.includes("male")) score += 6;
308
+ if (voice.lang === "en-US") score += 3;
309
+ if (nameLower.includes("google us english") || nameLower.includes("microsoft david")) {
310
+ score -= 20;
311
+ }
312
+ return { voice, score };
313
+ });
314
+ scoredVoices.sort((a, b) => b.score - a.score);
315
+ if (scoredVoices.length > 0) {
316
+ setBestVoice(scoredVoices[0].voice);
317
+ }
318
+ };
319
+ loadVoices();
320
+ if (window.speechSynthesis.onvoiceschanged !== void 0) {
321
+ window.speechSynthesis.onvoiceschanged = loadVoices;
322
+ }
323
+ return () => {
324
+ if (window.speechSynthesis.onvoiceschanged !== void 0) {
325
+ window.speechSynthesis.onvoiceschanged = null;
326
+ }
327
+ };
328
+ }, [isSupported]);
329
+ const handlePlay = () => {
330
+ if (!isSupported || typeof window === "undefined") return;
331
+ try {
332
+ if (isPlaying) {
333
+ window.speechSynthesis.cancel();
334
+ setIsPlaying(false);
335
+ setCurrentWordIndex(-1);
336
+ return;
337
+ }
338
+ const utterance = new SpeechSynthesisUtterance(text);
339
+ utteranceRef.current = utterance;
340
+ if (bestVoice) {
341
+ utterance.voice = bestVoice;
342
+ }
343
+ utterance.rate = 1;
344
+ utterance.pitch = 1;
345
+ utterance.volume = 1;
346
+ let wordIndex = 0;
347
+ utterance.onboundary = (event) => {
348
+ if (event.name === "word") {
349
+ setCurrentWordIndex(wordIndex);
350
+ wordIndex++;
351
+ }
352
+ };
353
+ utterance.onstart = () => {
354
+ setIsPlaying(true);
355
+ setCurrentWordIndex(0);
356
+ };
357
+ utterance.onend = () => {
358
+ setIsPlaying(false);
359
+ setCurrentWordIndex(-1);
360
+ };
361
+ utterance.onerror = (event) => {
362
+ console.error("Speech error:", event);
363
+ setIsPlaying(false);
364
+ setCurrentWordIndex(-1);
365
+ };
366
+ window.speechSynthesis.speak(utterance);
367
+ } catch (error) {
368
+ console.error("TTS error:", error);
369
+ setIsPlaying(false);
370
+ }
371
+ };
372
+ useEffect(() => {
373
+ return () => {
374
+ if (typeof window !== "undefined" && "speechSynthesis" in window) {
375
+ try {
376
+ window.speechSynthesis.cancel();
377
+ } catch (error) {
378
+ }
379
+ }
380
+ };
381
+ }, []);
382
+ const buttonStyle = {
383
+ ...styles.button,
384
+ ...size === "sm" ? styles.buttonSm : styles.buttonMd,
385
+ ...isPlaying ? styles.buttonPlaying : {},
386
+ ...!isSupported ? styles.buttonDisabled : {}
387
+ };
388
+ if (inline) {
389
+ const words = text.split(/\s+/);
390
+ return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
391
+ /* @__PURE__ */ jsx(
392
+ "button",
393
+ {
394
+ style: buttonStyle,
395
+ onClick: handlePlay,
396
+ disabled: !isSupported,
397
+ "aria-label": isPlaying ? "Stop reading" : "Read aloud",
398
+ title: isPlaying ? "Stop" : "Read aloud",
399
+ "data-testid": "button-tts",
400
+ children: isPlaying ? /* @__PURE__ */ jsx(StopIcon, {}) : /* @__PURE__ */ jsx(VolumeIcon, {})
401
+ }
402
+ ),
403
+ /* @__PURE__ */ jsx("span", { style: styles.textContainer, children: words.map((word, index) => /* @__PURE__ */ jsxs(
404
+ "span",
405
+ {
406
+ style: index === currentWordIndex ? styles.highlightedWord : void 0,
407
+ children: [
408
+ word,
409
+ index < words.length - 1 ? " " : ""
410
+ ]
411
+ },
412
+ index
413
+ )) })
414
+ ] });
415
+ }
416
+ return /* @__PURE__ */ jsx(
417
+ "button",
418
+ {
419
+ style: buttonStyle,
420
+ onClick: handlePlay,
421
+ disabled: !isSupported,
422
+ "aria-label": isPlaying ? "Stop reading" : "Read aloud",
423
+ title: isPlaying ? "Stop" : "Read aloud",
424
+ "data-testid": "button-tts",
425
+ children: isPlaying ? /* @__PURE__ */ jsx(StopIcon, {}) : /* @__PURE__ */ jsx(VolumeIcon, {})
426
+ }
427
+ );
428
+ }
429
+
430
+ // src/QuestionChatPanel.tsx
431
+ import { useState as useState2, useEffect as useEffect2, useRef as useRef2, useCallback } from "react";
432
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
433
+ var panelStyles = {
434
+ container: {
435
+ display: "flex",
436
+ flexDirection: "column",
437
+ height: "100%",
438
+ backgroundColor: "#f8fafc",
439
+ borderRadius: "12px",
440
+ border: "1px solid #e2e8f0",
441
+ overflow: "hidden"
442
+ },
443
+ header: {
444
+ padding: "12px 16px",
445
+ backgroundColor: "#6721b0",
446
+ color: "#ffffff",
447
+ fontWeight: "600",
448
+ fontSize: "14px",
449
+ display: "flex",
450
+ alignItems: "center",
451
+ gap: "8px"
452
+ },
453
+ messagesContainer: {
454
+ flex: 1,
455
+ overflowY: "auto",
456
+ padding: "12px",
457
+ display: "flex",
458
+ flexDirection: "column",
459
+ gap: "8px"
460
+ },
461
+ starterPrompts: {
462
+ display: "flex",
463
+ flexDirection: "column",
464
+ gap: "8px",
465
+ padding: "16px"
466
+ },
467
+ starterButton: {
468
+ padding: "10px 14px",
469
+ borderRadius: "20px",
470
+ border: "1px solid #e2e8f0",
471
+ backgroundColor: "#ffffff",
472
+ color: "#374151",
473
+ fontSize: "13px",
474
+ cursor: "pointer",
475
+ textAlign: "left",
476
+ transition: "all 0.2s ease"
477
+ },
478
+ starterButtonHover: {
479
+ backgroundColor: "#f3e8ff",
480
+ borderColor: "#6721b0"
481
+ },
482
+ messageRow: {
483
+ display: "flex"
484
+ },
485
+ userMessage: {
486
+ maxWidth: "85%",
487
+ marginLeft: "auto",
488
+ padding: "10px 14px",
489
+ borderRadius: "16px 16px 4px 16px",
490
+ backgroundColor: "#6721b0",
491
+ color: "#ffffff",
492
+ fontSize: "14px",
493
+ lineHeight: 1.4
494
+ },
495
+ assistantMessage: {
496
+ maxWidth: "85%",
497
+ marginRight: "auto",
498
+ padding: "10px 14px",
499
+ borderRadius: "16px 16px 16px 4px",
500
+ backgroundColor: "#ffffff",
501
+ color: "#1f2937",
502
+ fontSize: "14px",
503
+ lineHeight: 1.4,
504
+ border: "1px solid #e2e8f0"
505
+ },
506
+ inputContainer: {
507
+ padding: "12px",
508
+ borderTop: "1px solid #e2e8f0",
509
+ backgroundColor: "#ffffff",
510
+ display: "flex",
511
+ gap: "8px"
512
+ },
513
+ input: {
514
+ flex: 1,
515
+ padding: "10px 14px",
516
+ borderRadius: "20px",
517
+ border: "1px solid #e2e8f0",
518
+ fontSize: "14px",
519
+ outline: "none"
520
+ },
521
+ sendButton: {
522
+ width: "40px",
523
+ height: "40px",
524
+ borderRadius: "50%",
525
+ border: "none",
526
+ backgroundColor: "#6721b0",
527
+ color: "#ffffff",
528
+ cursor: "pointer",
529
+ display: "flex",
530
+ alignItems: "center",
531
+ justifyContent: "center",
532
+ fontSize: "16px",
533
+ transition: "all 0.2s ease"
534
+ },
535
+ sendButtonDisabled: {
536
+ backgroundColor: "#d1d5db",
537
+ cursor: "not-allowed"
538
+ },
539
+ loadingDots: {
540
+ display: "flex",
541
+ alignItems: "center",
542
+ gap: "4px",
543
+ padding: "10px 14px",
544
+ maxWidth: "85%",
545
+ marginRight: "auto"
546
+ },
547
+ dot: {
548
+ width: "8px",
549
+ height: "8px",
550
+ backgroundColor: "#9ca3af",
551
+ borderRadius: "50%",
552
+ animation: "bounce 1.4s ease-in-out infinite"
553
+ },
554
+ emptyState: {
555
+ flex: 1,
556
+ display: "flex",
557
+ flexDirection: "column",
558
+ alignItems: "center",
559
+ justifyContent: "center",
560
+ padding: "20px",
561
+ color: "#6b7280",
562
+ textAlign: "center"
563
+ },
564
+ helperIcon: {
565
+ width: "48px",
566
+ height: "48px",
567
+ marginBottom: "12px",
568
+ fontSize: "32px"
569
+ }
570
+ };
571
+ var STARTER_PROMPTS = [
572
+ { id: "dont_understand", label: "I don't understand this question", message: "I don't understand this question. Can you help me?" },
573
+ { id: "word_help", label: "I don't understand a word", message: "I don't understand a word in this question. Can you help?" },
574
+ { id: "explain_again", label: "Explain this again", message: "Can you explain this question to me again in a different way?" }
575
+ ];
576
+ function QuestionChatPanel({
577
+ apiClient,
578
+ question,
579
+ quizId,
580
+ childId,
581
+ parentId,
582
+ lessonId,
583
+ courseId
584
+ }) {
585
+ const [messages, setMessages] = useState2([]);
586
+ const [inputValue, setInputValue] = useState2("");
587
+ const [isLoading, setIsLoading] = useState2(false);
588
+ const [chatId, setChatId] = useState2(null);
589
+ const [hoveredButton, setHoveredButton] = useState2(null);
590
+ const messagesContainerRef = useRef2(null);
591
+ const messagesEndRef = useRef2(null);
592
+ const scrollToBottom = useCallback(() => {
593
+ if (messagesContainerRef.current) {
594
+ messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
595
+ }
596
+ }, []);
597
+ useEffect2(() => {
598
+ scrollToBottom();
599
+ }, [messages, scrollToBottom]);
600
+ useEffect2(() => {
601
+ setMessages([]);
602
+ setChatId(null);
603
+ setInputValue("");
604
+ const loadHistory = async () => {
605
+ try {
606
+ const history = await apiClient.getChatHistory(question.id, childId);
607
+ if (history.chatId && history.messages.length > 0) {
608
+ setChatId(history.chatId);
609
+ setMessages(history.messages);
610
+ }
611
+ } catch (err) {
612
+ console.error("Failed to load chat history:", err);
613
+ }
614
+ };
615
+ loadHistory();
616
+ }, [question.id, childId, apiClient]);
617
+ const initializeChat = async () => {
618
+ if (chatId) return chatId;
619
+ try {
620
+ const session = await apiClient.getOrCreateChatSession({
621
+ questionId: question.id,
622
+ questionContent: question,
623
+ quizId,
624
+ childId,
625
+ parentId,
626
+ lessonId,
627
+ courseId
628
+ });
629
+ setChatId(session.chatId);
630
+ return session.chatId;
631
+ } catch (err) {
632
+ console.error("Failed to create chat session:", err);
633
+ return null;
634
+ }
635
+ };
636
+ const sendMessage = async (messageText) => {
637
+ if (!messageText.trim() || isLoading) return;
638
+ setIsLoading(true);
639
+ const userMsg = {
640
+ role: "user",
641
+ content: messageText,
642
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
643
+ };
644
+ setMessages((prev) => [...prev, userMsg]);
645
+ setInputValue("");
646
+ try {
647
+ const currentChatId = await initializeChat();
648
+ if (!currentChatId) {
649
+ throw new Error("Failed to initialize chat");
650
+ }
651
+ const response = await apiClient.sendChatMessage({
652
+ chatId: currentChatId,
653
+ message: messageText,
654
+ questionContext: question,
655
+ childId
656
+ });
657
+ setMessages((prev) => [...prev, response.assistantMessage]);
658
+ } catch (err) {
659
+ console.error("Failed to send message:", err);
660
+ const errorMsg = {
661
+ role: "assistant",
662
+ content: "Sorry, I'm having trouble right now. Please try again!",
663
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
664
+ };
665
+ setMessages((prev) => [...prev, errorMsg]);
666
+ } finally {
667
+ setIsLoading(false);
668
+ }
669
+ };
670
+ const handleKeyPress = (e) => {
671
+ if (e.key === "Enter" && !e.shiftKey) {
672
+ e.preventDefault();
673
+ sendMessage(inputValue);
674
+ }
675
+ };
676
+ return /* @__PURE__ */ jsxs2("div", { style: panelStyles.container, children: [
677
+ /* @__PURE__ */ jsx2("style", { children: `
678
+ @keyframes bounce {
679
+ 0%, 60%, 100% { transform: translateY(0); }
680
+ 30% { transform: translateY(-4px); }
681
+ }
682
+ ` }),
683
+ /* @__PURE__ */ jsx2("div", { style: panelStyles.header, children: /* @__PURE__ */ jsx2("span", { children: "Need Help?" }) }),
684
+ /* @__PURE__ */ jsx2("div", { ref: messagesContainerRef, style: panelStyles.messagesContainer, children: messages.length === 0 ? /* @__PURE__ */ jsxs2("div", { style: panelStyles.emptyState, children: [
685
+ /* @__PURE__ */ jsx2("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "#6721b0", strokeWidth: "2", children: [
686
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
687
+ /* @__PURE__ */ jsx2("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
688
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
689
+ ] }) }),
690
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: "14px", fontWeight: "500", marginBottom: "8px" }, children: "Hi! I'm your question helper" }),
691
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: "13px", color: "#9ca3af" }, children: "Ask me if you need help understanding this question" }),
692
+ /* @__PURE__ */ jsx2("div", { style: { ...panelStyles.starterPrompts, marginTop: "16px" }, children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ jsx2(
693
+ "button",
694
+ {
695
+ style: {
696
+ ...panelStyles.starterButton,
697
+ ...hoveredButton === prompt.id ? panelStyles.starterButtonHover : {}
698
+ },
699
+ onMouseEnter: () => setHoveredButton(prompt.id),
700
+ onMouseLeave: () => setHoveredButton(null),
701
+ onClick: () => sendMessage(prompt.message),
702
+ disabled: isLoading,
703
+ "data-testid": `button-chat-starter-${prompt.id}`,
704
+ children: prompt.label
705
+ },
706
+ prompt.id
707
+ )) })
708
+ ] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
709
+ messages.map((msg, idx) => /* @__PURE__ */ jsx2(
710
+ "div",
711
+ {
712
+ style: {
713
+ ...panelStyles.messageRow,
714
+ justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
715
+ },
716
+ children: /* @__PURE__ */ jsx2("div", { style: msg.role === "user" ? panelStyles.userMessage : panelStyles.assistantMessage, children: msg.content })
717
+ },
718
+ idx
719
+ )),
720
+ isLoading && /* @__PURE__ */ jsxs2("div", { style: panelStyles.loadingDots, children: [
721
+ /* @__PURE__ */ jsx2("div", { style: { ...panelStyles.dot, animationDelay: "0s" } }),
722
+ /* @__PURE__ */ jsx2("div", { style: { ...panelStyles.dot, animationDelay: "0.2s" } }),
723
+ /* @__PURE__ */ jsx2("div", { style: { ...panelStyles.dot, animationDelay: "0.4s" } })
724
+ ] }),
725
+ /* @__PURE__ */ jsx2("div", { ref: messagesEndRef })
726
+ ] }) }),
727
+ /* @__PURE__ */ jsxs2("div", { style: panelStyles.inputContainer, children: [
728
+ /* @__PURE__ */ jsx2(
729
+ "input",
730
+ {
731
+ type: "text",
732
+ value: inputValue,
733
+ onChange: (e) => setInputValue(e.target.value),
734
+ onKeyPress: handleKeyPress,
735
+ placeholder: "Ask about this question...",
736
+ style: panelStyles.input,
737
+ disabled: isLoading,
738
+ "data-testid": "input-chat-message"
739
+ }
740
+ ),
741
+ /* @__PURE__ */ jsx2(
742
+ "button",
743
+ {
744
+ onClick: () => sendMessage(inputValue),
745
+ disabled: isLoading || !inputValue.trim(),
746
+ style: {
747
+ ...panelStyles.sendButton,
748
+ ...isLoading || !inputValue.trim() ? panelStyles.sendButtonDisabled : {}
749
+ },
750
+ "data-testid": "button-send-chat",
751
+ children: /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
752
+ /* @__PURE__ */ jsx2("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
753
+ /* @__PURE__ */ jsx2("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
754
+ ] })
755
+ }
756
+ )
757
+ ] })
758
+ ] });
759
+ }
760
+
143
761
  // src/QuizPlayer.tsx
144
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
762
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
145
763
  var defaultStyles = {
146
764
  container: {
147
765
  fontFamily: "system-ui, -apple-system, sans-serif",
@@ -199,7 +817,10 @@ var defaultStyles = {
199
817
  cursor: "pointer",
200
818
  transition: "all 0.2s ease",
201
819
  outline: "none",
202
- backgroundColor: "#ffffff"
820
+ boxShadow: "none",
821
+ backgroundColor: "#ffffff",
822
+ WebkitTapHighlightColor: "transparent",
823
+ userSelect: "none"
203
824
  },
204
825
  optionSelected: {
205
826
  borderColor: "#6721b0",
@@ -276,6 +897,19 @@ var defaultStyles = {
276
897
  gap: "12px",
277
898
  marginTop: "24px"
278
899
  },
900
+ mainLayout: {
901
+ display: "flex",
902
+ gap: "24px"
903
+ },
904
+ quizContent: {
905
+ flex: 1,
906
+ minWidth: 0
907
+ },
908
+ chatPanel: {
909
+ width: "320px",
910
+ flexShrink: 0,
911
+ height: "460px"
912
+ },
279
913
  timer: {
280
914
  fontSize: "14px",
281
915
  color: "#6b7280",
@@ -421,7 +1055,7 @@ var defaultStyles = {
421
1055
  }
422
1056
  };
423
1057
  function Spinner({ size = 16, color = "#ffffff" }) {
424
- return /* @__PURE__ */ jsx(
1058
+ return /* @__PURE__ */ jsx3(
425
1059
  "span",
426
1060
  {
427
1061
  style: {
@@ -433,7 +1067,7 @@ function Spinner({ size = 16, color = "#ffffff" }) {
433
1067
  borderRadius: "50%",
434
1068
  animation: "spin 0.8s linear infinite"
435
1069
  },
436
- children: /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1070
+ children: /* @__PURE__ */ jsx3("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
437
1071
  }
438
1072
  );
439
1073
  }
@@ -450,33 +1084,42 @@ function QuizPlayer({
450
1084
  onError,
451
1085
  onProgress,
452
1086
  onGenerateMoreQuestions,
453
- className
1087
+ className,
1088
+ forceNewAttempt = true
454
1089
  }) {
455
- const [quiz, setQuiz] = useState(null);
456
- const [attempt, setAttempt] = useState(null);
457
- const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
458
- const [answers, setAnswers] = useState(/* @__PURE__ */ new Map());
459
- const [answersDetail, setAnswersDetail] = useState([]);
460
- const [isSubmitting, setIsSubmitting] = useState(false);
461
- const [isNavigating, setIsNavigating] = useState(false);
462
- const [isCompleted, setIsCompleted] = useState(false);
463
- const [result, setResult] = useState(null);
464
- const [error, setError] = useState(null);
465
- const [isLoading, setIsLoading] = useState(true);
466
- const [elapsedSeconds, setElapsedSeconds] = useState(0);
467
- const [showIntro, setShowIntro] = useState(true);
468
- const [timerStarted, setTimerStarted] = useState(false);
469
- const [showFeedback, setShowFeedback] = useState(false);
470
- const [currentAnswerDetail, setCurrentAnswerDetail] = useState(null);
471
- const [extraQuestions, setExtraQuestions] = useState([]);
472
- const [isGeneratingExtra, setIsGeneratingExtra] = useState(false);
473
- const apiClient = useRef(null);
474
- const timerRef = useRef(null);
475
- const startTimeRef = useRef(0);
476
- useEffect(() => {
1090
+ const [quiz, setQuiz] = useState3(null);
1091
+ const [attempt, setAttempt] = useState3(null);
1092
+ const [currentQuestionIndex, setCurrentQuestionIndex] = useState3(0);
1093
+ const [answers, setAnswers] = useState3(/* @__PURE__ */ new Map());
1094
+ const [answersDetail, setAnswersDetail] = useState3([]);
1095
+ const [isSubmitting, setIsSubmitting] = useState3(false);
1096
+ const [isNavigating, setIsNavigating] = useState3(false);
1097
+ const [isCompleted, setIsCompleted] = useState3(false);
1098
+ const [result, setResult] = useState3(null);
1099
+ const [error, setError] = useState3(null);
1100
+ const [isLoading, setIsLoading] = useState3(true);
1101
+ const [elapsedSeconds, setElapsedSeconds] = useState3(0);
1102
+ const [showIntro, setShowIntro] = useState3(true);
1103
+ const [timerStarted, setTimerStarted] = useState3(false);
1104
+ const [showFeedback, setShowFeedback] = useState3(false);
1105
+ const [currentAnswerDetail, setCurrentAnswerDetail] = useState3(null);
1106
+ const [extraQuestions, setExtraQuestions] = useState3([]);
1107
+ const [isGeneratingExtra, setIsGeneratingExtra] = useState3(false);
1108
+ const [showSkipModal, setShowSkipModal] = useState3(false);
1109
+ const [skippedQuestionIds, setSkippedQuestionIds] = useState3(/* @__PURE__ */ new Set());
1110
+ const [isSkipping, setIsSkipping] = useState3(false);
1111
+ const [skipComment, setSkipComment] = useState3("");
1112
+ const [selectedSkipReason, setSelectedSkipReason] = useState3(null);
1113
+ const [showReportModal, setShowReportModal] = useState3(false);
1114
+ const [isReporting, setIsReporting] = useState3(false);
1115
+ const [reportComment, setReportComment] = useState3("");
1116
+ const apiClient = useRef3(null);
1117
+ const timerRef = useRef3(null);
1118
+ const startTimeRef = useRef3(0);
1119
+ useEffect3(() => {
477
1120
  apiClient.current = new QuizApiClient({ baseUrl: apiBaseUrl, authToken });
478
1121
  }, [apiBaseUrl, authToken]);
479
- useEffect(() => {
1122
+ useEffect3(() => {
480
1123
  async function initialize() {
481
1124
  if (!apiClient.current) return;
482
1125
  try {
@@ -490,10 +1133,11 @@ function QuizPlayer({
490
1133
  assignLessonId,
491
1134
  courseId,
492
1135
  childId,
493
- parentId
1136
+ parentId,
1137
+ forceNew: forceNewAttempt
494
1138
  });
495
1139
  setAttempt(attemptData);
496
- if (attemptData.answers && attemptData.answers.length > 0) {
1140
+ if (!forceNewAttempt && attemptData.answers && attemptData.answers.length > 0) {
497
1141
  setAnswersDetail(attemptData.answers);
498
1142
  const answersMap = /* @__PURE__ */ new Map();
499
1143
  attemptData.answers.forEach((a) => {
@@ -522,8 +1166,8 @@ function QuizPlayer({
522
1166
  }
523
1167
  }
524
1168
  initialize();
525
- }, [quizId, lessonId, assignLessonId, courseId, childId, parentId, onError]);
526
- useEffect(() => {
1169
+ }, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt, onError]);
1170
+ useEffect3(() => {
527
1171
  if (timerStarted && !isCompleted && !error) {
528
1172
  startTimeRef.current = Date.now();
529
1173
  timerRef.current = setInterval(() => {
@@ -536,18 +1180,18 @@ function QuizPlayer({
536
1180
  }
537
1181
  };
538
1182
  }, [timerStarted, isCompleted, error]);
539
- const handleStart = useCallback(() => {
1183
+ const handleStart = useCallback2(() => {
540
1184
  setShowIntro(false);
541
1185
  setTimerStarted(true);
542
1186
  }, []);
543
- useEffect(() => {
1187
+ useEffect3(() => {
544
1188
  setShowFeedback(false);
545
1189
  setCurrentAnswerDetail(null);
546
1190
  }, [currentQuestionIndex]);
547
- const allQuestions = quiz ? [...quiz.questions, ...extraQuestions] : [];
1191
+ const allQuestions = quiz ? [...quiz.questions, ...extraQuestions].filter((q) => !skippedQuestionIds.has(q.id)) : [];
548
1192
  const totalQuestions = allQuestions.length;
549
1193
  const maxQuestions = 50;
550
- useEffect(() => {
1194
+ useEffect3(() => {
551
1195
  if (quiz && onProgress) {
552
1196
  onProgress({
553
1197
  currentQuestion: currentQuestionIndex + 1,
@@ -557,11 +1201,11 @@ function QuizPlayer({
557
1201
  }
558
1202
  }, [currentQuestionIndex, answers.size, quiz, onProgress, totalQuestions]);
559
1203
  const currentQuestion = allQuestions[currentQuestionIndex];
560
- const handleAnswerChange = useCallback((value) => {
1204
+ const handleAnswerChange = useCallback2((value) => {
561
1205
  if (!currentQuestion) return;
562
1206
  setAnswers((prev) => new Map(prev).set(currentQuestion.id, value));
563
1207
  }, [currentQuestion]);
564
- const handleCheckAnswer = useCallback(async () => {
1208
+ const handleCheckAnswer = useCallback2(async () => {
565
1209
  if (!quiz || !attempt || !currentQuestion || !apiClient.current) return;
566
1210
  const selectedAnswer2 = answers.get(currentQuestion.id);
567
1211
  if (selectedAnswer2 === void 0) return;
@@ -587,7 +1231,7 @@ function QuizPlayer({
587
1231
  }
588
1232
  setShowFeedback(true);
589
1233
  }, [quiz, attempt, currentQuestion, answers, answersDetail]);
590
- const handleContinue = useCallback(() => {
1234
+ const handleContinue = useCallback2(() => {
591
1235
  if (!quiz) return;
592
1236
  setShowFeedback(false);
593
1237
  setCurrentAnswerDetail(null);
@@ -595,7 +1239,7 @@ function QuizPlayer({
595
1239
  setCurrentQuestionIndex((prev) => prev + 1);
596
1240
  }
597
1241
  }, [quiz, currentQuestionIndex, totalQuestions]);
598
- const handleAddMoreQuestions = useCallback(async () => {
1242
+ const handleAddMoreQuestions = useCallback2(async () => {
599
1243
  if (!attempt || !onGenerateMoreQuestions || isGeneratingExtra) return;
600
1244
  if (totalQuestions >= maxQuestions) return;
601
1245
  setIsGeneratingExtra(true);
@@ -618,7 +1262,7 @@ function QuizPlayer({
618
1262
  setIsGeneratingExtra(false);
619
1263
  }
620
1264
  }, [attempt, onGenerateMoreQuestions, isGeneratingExtra, totalQuestions, maxQuestions, onError]);
621
- const handleSubmit = useCallback(async () => {
1265
+ const handleSubmit = useCallback2(async () => {
622
1266
  if (!quiz || !attempt || !apiClient.current) return;
623
1267
  setIsSubmitting(true);
624
1268
  try {
@@ -640,6 +1284,7 @@ function QuizPlayer({
640
1284
  status: "completed",
641
1285
  score: scoreData.score,
642
1286
  correctAnswers: scoreData.correctAnswers,
1287
+ totalQuestions,
643
1288
  timeSpentSeconds: timeSpent
644
1289
  });
645
1290
  setIsCompleted(true);
@@ -664,17 +1309,89 @@ function QuizPlayer({
664
1309
  setIsSubmitting(false);
665
1310
  }
666
1311
  }, [quiz, attempt, currentQuestion, answers, answersDetail, onComplete, onError, totalQuestions, timerStarted, elapsedSeconds]);
1312
+ const isExtraQuestion = currentQuestion && extraQuestions.some((q) => q.id === currentQuestion.id);
1313
+ const handleSkipQuestion = useCallback2(async (reason, comment) => {
1314
+ if (!currentQuestion || !apiClient.current || !attempt) return;
1315
+ setIsSkipping(true);
1316
+ try {
1317
+ await apiClient.current.skipQuestion({
1318
+ questionId: currentQuestion.id,
1319
+ questionContent: currentQuestion,
1320
+ skipReason: reason,
1321
+ skipComment: comment?.trim() || void 0,
1322
+ quizId: quiz?.id,
1323
+ attemptId: attempt.id,
1324
+ childId,
1325
+ parentId,
1326
+ lessonId,
1327
+ courseId,
1328
+ assignLessonId
1329
+ });
1330
+ const newSkippedIds = new Set(skippedQuestionIds).add(currentQuestion.id);
1331
+ setSkippedQuestionIds(newSkippedIds);
1332
+ const newExtraQuestions = extraQuestions.filter((q) => q.id !== currentQuestion.id);
1333
+ setExtraQuestions(newExtraQuestions);
1334
+ const newAllQuestions = quiz ? [...quiz.questions, ...newExtraQuestions].filter((q) => !newSkippedIds.has(q.id)) : [];
1335
+ const newTotalQuestions = newAllQuestions.length;
1336
+ if (newTotalQuestions === 0) {
1337
+ setIsCompleted(true);
1338
+ const quizResult = {
1339
+ attemptId: attempt.id,
1340
+ score: 0,
1341
+ correctAnswers: 0,
1342
+ totalQuestions: 0,
1343
+ answers: [],
1344
+ timeSpentSeconds: elapsedSeconds
1345
+ };
1346
+ setResult(quizResult);
1347
+ onComplete?.(quizResult);
1348
+ } else if (currentQuestionIndex >= newTotalQuestions) {
1349
+ setCurrentQuestionIndex(newTotalQuestions - 1);
1350
+ }
1351
+ setShowSkipModal(false);
1352
+ setSkipComment("");
1353
+ setSelectedSkipReason(null);
1354
+ } catch (err) {
1355
+ console.error("Failed to skip question:", err);
1356
+ } finally {
1357
+ setIsSkipping(false);
1358
+ }
1359
+ }, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId, skippedQuestionIds, extraQuestions, currentQuestionIndex, elapsedSeconds, onComplete]);
1360
+ const handleReportQuestion = useCallback2(async (comment) => {
1361
+ if (!currentQuestion || !apiClient.current || !attempt || !comment.trim()) return;
1362
+ setIsReporting(true);
1363
+ try {
1364
+ await apiClient.current.reportQuestion({
1365
+ questionId: currentQuestion.id,
1366
+ questionContent: currentQuestion,
1367
+ reportComment: comment.trim(),
1368
+ quizId: quiz?.id,
1369
+ attemptId: attempt.id,
1370
+ childId,
1371
+ parentId,
1372
+ lessonId,
1373
+ courseId,
1374
+ assignLessonId
1375
+ });
1376
+ setShowReportModal(false);
1377
+ setReportComment("");
1378
+ } catch (err) {
1379
+ console.error("Failed to report question:", err);
1380
+ } finally {
1381
+ setIsReporting(false);
1382
+ }
1383
+ }, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId]);
667
1384
  if (isLoading) {
668
- return /* @__PURE__ */ jsx("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx("div", { style: defaultStyles.loading, children: "Loading quiz..." }) });
1385
+ return /* @__PURE__ */ jsx3("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx3("div", { style: defaultStyles.loading, children: "Loading quiz..." }) });
669
1386
  }
670
1387
  if (error) {
671
- return /* @__PURE__ */ jsx("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx("div", { style: defaultStyles.error, children: /* @__PURE__ */ jsxs("p", { children: [
1388
+ return /* @__PURE__ */ jsx3("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx3("div", { style: defaultStyles.error, children: /* @__PURE__ */ jsxs3("p", { children: [
672
1389
  "Error: ",
673
1390
  error
674
1391
  ] }) }) });
675
1392
  }
676
1393
  if (isCompleted && result) {
677
- const percentage = Math.round(result.correctAnswers / result.totalQuestions * 100);
1394
+ const percentage = result.totalQuestions > 0 ? Math.round(result.correctAnswers / result.totalQuestions * 100) : 0;
678
1395
  const getScoreTheme = (pct) => {
679
1396
  if (pct >= 80) {
680
1397
  return {
@@ -729,7 +1446,7 @@ function QuizPlayer({
729
1446
  rotation: Math.random() * 360,
730
1447
  size: 6 + Math.random() * 8
731
1448
  }));
732
- const StarIcon = ({ filled, delay }) => /* @__PURE__ */ jsx(
1449
+ const StarIcon = ({ filled, delay }) => /* @__PURE__ */ jsx3(
733
1450
  "svg",
734
1451
  {
735
1452
  width: "36",
@@ -740,7 +1457,7 @@ function QuizPlayer({
740
1457
  animationDelay: `${delay}s`,
741
1458
  opacity: 0
742
1459
  },
743
- children: /* @__PURE__ */ jsx(
1460
+ children: /* @__PURE__ */ jsx3(
744
1461
  "path",
745
1462
  {
746
1463
  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",
@@ -768,7 +1485,7 @@ function QuizPlayer({
768
1485
  }
769
1486
  };
770
1487
  const eyeExpr = getEyeExpression();
771
- return /* @__PURE__ */ jsxs(
1488
+ return /* @__PURE__ */ jsxs3(
772
1489
  "svg",
773
1490
  {
774
1491
  width: "120",
@@ -778,35 +1495,35 @@ function QuizPlayer({
778
1495
  animation: mood === "celebrating" ? "bounce 0.6s ease-in-out infinite" : "gentleBob 2s ease-in-out infinite"
779
1496
  },
780
1497
  children: [
781
- /* @__PURE__ */ jsx("ellipse", { cx: "50", cy: "60", rx: "35", ry: "30", fill: "#8b5cf6" }),
782
- /* @__PURE__ */ jsx("ellipse", { cx: "50", cy: "65", rx: "25", ry: "20", fill: "#c4b5fd" }),
783
- /* @__PURE__ */ jsx("circle", { cx: "50", cy: "35", r: "28", fill: "#a78bfa" }),
784
- /* @__PURE__ */ jsx("polygon", { points: "28,15 35,30 22,28", fill: "#7c3aed" }),
785
- /* @__PURE__ */ jsx("polygon", { points: "72,15 65,30 78,28", fill: "#7c3aed" }),
786
- /* @__PURE__ */ jsx("ellipse", { cx: "38", cy: "38", rx: "10", ry: "12", fill: "white" }),
787
- /* @__PURE__ */ jsx("ellipse", { cx: "62", cy: "38", rx: "10", ry: "12", fill: "white" }),
788
- eyeExpr.leftEye ? /* @__PURE__ */ jsx("text", { x: "38", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.leftEye }) : /* @__PURE__ */ jsx("circle", { cx: "38", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
789
- eyeExpr.rightEye ? /* @__PURE__ */ jsx("text", { x: "62", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.rightEye }) : /* @__PURE__ */ jsx("circle", { cx: "62", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
790
- /* @__PURE__ */ jsx("polygon", { points: "50,45 45,52 55,52", fill: "#fbbf24" }),
791
- (mood === "celebrating" || mood === "happy") && /* @__PURE__ */ jsxs(Fragment, { children: [
792
- /* @__PURE__ */ jsx("ellipse", { cx: "28", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" }),
793
- /* @__PURE__ */ jsx("ellipse", { cx: "72", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" })
1498
+ /* @__PURE__ */ jsx3("ellipse", { cx: "50", cy: "60", rx: "35", ry: "30", fill: "#8b5cf6" }),
1499
+ /* @__PURE__ */ jsx3("ellipse", { cx: "50", cy: "65", rx: "25", ry: "20", fill: "#c4b5fd" }),
1500
+ /* @__PURE__ */ jsx3("circle", { cx: "50", cy: "35", r: "28", fill: "#a78bfa" }),
1501
+ /* @__PURE__ */ jsx3("polygon", { points: "28,15 35,30 22,28", fill: "#7c3aed" }),
1502
+ /* @__PURE__ */ jsx3("polygon", { points: "72,15 65,30 78,28", fill: "#7c3aed" }),
1503
+ /* @__PURE__ */ jsx3("ellipse", { cx: "38", cy: "38", rx: "10", ry: "12", fill: "white" }),
1504
+ /* @__PURE__ */ jsx3("ellipse", { cx: "62", cy: "38", rx: "10", ry: "12", fill: "white" }),
1505
+ eyeExpr.leftEye ? /* @__PURE__ */ jsx3("text", { x: "38", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.leftEye }) : /* @__PURE__ */ jsx3("circle", { cx: "38", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
1506
+ eyeExpr.rightEye ? /* @__PURE__ */ jsx3("text", { x: "62", y: "44", textAnchor: "middle", fontSize: "16", fill: "#1f2937", children: eyeExpr.rightEye }) : /* @__PURE__ */ jsx3("circle", { cx: "62", cy: eyeExpr.pupilY, r: "5", fill: "#1f2937" }),
1507
+ /* @__PURE__ */ jsx3("polygon", { points: "50,45 45,52 55,52", fill: "#fbbf24" }),
1508
+ (mood === "celebrating" || mood === "happy") && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1509
+ /* @__PURE__ */ jsx3("ellipse", { cx: "28", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" }),
1510
+ /* @__PURE__ */ jsx3("ellipse", { cx: "72", cy: "45", rx: "5", ry: "3", fill: "#fda4af", opacity: "0.6" })
794
1511
  ] }),
795
- mood === "celebrating" ? /* @__PURE__ */ jsxs(Fragment, { children: [
796
- /* @__PURE__ */ jsx("ellipse", { cx: "18", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(-30 18 55)" }),
797
- /* @__PURE__ */ jsx("ellipse", { cx: "82", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(30 82 55)" })
798
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
799
- /* @__PURE__ */ jsx("ellipse", { cx: "20", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" }),
800
- /* @__PURE__ */ jsx("ellipse", { cx: "80", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" })
1512
+ mood === "celebrating" ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
1513
+ /* @__PURE__ */ jsx3("ellipse", { cx: "18", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(-30 18 55)" }),
1514
+ /* @__PURE__ */ jsx3("ellipse", { cx: "82", cy: "55", rx: "8", ry: "15", fill: "#7c3aed", transform: "rotate(30 82 55)" })
1515
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
1516
+ /* @__PURE__ */ jsx3("ellipse", { cx: "20", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" }),
1517
+ /* @__PURE__ */ jsx3("ellipse", { cx: "80", cy: "60", rx: "8", ry: "15", fill: "#7c3aed" })
801
1518
  ] }),
802
- /* @__PURE__ */ jsx("ellipse", { cx: "40", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" }),
803
- /* @__PURE__ */ jsx("ellipse", { cx: "60", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" })
1519
+ /* @__PURE__ */ jsx3("ellipse", { cx: "40", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" }),
1520
+ /* @__PURE__ */ jsx3("ellipse", { cx: "60", cy: "88", rx: "8", ry: "4", fill: "#fbbf24" })
804
1521
  ]
805
1522
  }
806
1523
  );
807
1524
  };
808
- return /* @__PURE__ */ jsxs("div", { className, style: defaultStyles.container, children: [
809
- /* @__PURE__ */ jsx("style", { children: `
1525
+ return /* @__PURE__ */ jsxs3("div", { className, style: defaultStyles.container, children: [
1526
+ /* @__PURE__ */ jsx3("style", { children: `
810
1527
  @keyframes confettiFall {
811
1528
  0% {
812
1529
  transform: translateY(-10px) rotate(0deg);
@@ -870,8 +1587,8 @@ function QuizPlayer({
870
1587
  }
871
1588
  }
872
1589
  ` }),
873
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.results, children: [
874
- percentage >= 60 && /* @__PURE__ */ jsx("div", { style: defaultStyles.confettiContainer, children: confettiPieces.map((piece) => /* @__PURE__ */ jsx(
1590
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.results, children: [
1591
+ percentage >= 60 && /* @__PURE__ */ jsx3("div", { style: defaultStyles.confettiContainer, children: confettiPieces.map((piece) => /* @__PURE__ */ jsx3(
875
1592
  "div",
876
1593
  {
877
1594
  style: {
@@ -888,15 +1605,15 @@ function QuizPlayer({
888
1605
  },
889
1606
  piece.id
890
1607
  )) }),
891
- /* @__PURE__ */ jsx("div", { style: { ...defaultStyles.resultsBackground, background: theme.bgGradient } }),
892
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.resultsContent, children: [
893
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.resultStars, children: [
894
- /* @__PURE__ */ jsx(StarIcon, { filled: theme.stars >= 1, delay: 0.3 }),
895
- /* @__PURE__ */ jsx(StarIcon, { filled: theme.stars >= 2, delay: 0.5 }),
896
- /* @__PURE__ */ jsx(StarIcon, { filled: theme.stars >= 3, delay: 0.7 })
1608
+ /* @__PURE__ */ jsx3("div", { style: { ...defaultStyles.resultsBackground, background: theme.bgGradient } }),
1609
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.resultsContent, children: [
1610
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.resultStars, children: [
1611
+ /* @__PURE__ */ jsx3(StarIcon, { filled: theme.stars >= 1, delay: 0.3 }),
1612
+ /* @__PURE__ */ jsx3(StarIcon, { filled: theme.stars >= 2, delay: 0.5 }),
1613
+ /* @__PURE__ */ jsx3(StarIcon, { filled: theme.stars >= 3, delay: 0.7 })
897
1614
  ] }),
898
- /* @__PURE__ */ jsx("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ jsx(MascotOwl, { mood: theme.mascotMood }) }),
899
- /* @__PURE__ */ jsx(
1615
+ /* @__PURE__ */ jsx3("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ jsx3(MascotOwl, { mood: theme.mascotMood }) }),
1616
+ /* @__PURE__ */ jsx3(
900
1617
  "div",
901
1618
  {
902
1619
  style: {
@@ -909,7 +1626,7 @@ function QuizPlayer({
909
1626
  opacity: 0,
910
1627
  border: `3px solid ${theme.badgeColor}`
911
1628
  },
912
- children: /* @__PURE__ */ jsx(
1629
+ children: /* @__PURE__ */ jsx3(
913
1630
  "span",
914
1631
  {
915
1632
  style: {
@@ -923,7 +1640,7 @@ function QuizPlayer({
923
1640
  )
924
1641
  }
925
1642
  ),
926
- /* @__PURE__ */ jsxs(
1643
+ /* @__PURE__ */ jsxs3(
927
1644
  "div",
928
1645
  {
929
1646
  style: {
@@ -931,7 +1648,7 @@ function QuizPlayer({
931
1648
  opacity: 0
932
1649
  },
933
1650
  children: [
934
- /* @__PURE__ */ jsxs(
1651
+ /* @__PURE__ */ jsxs3(
935
1652
  "div",
936
1653
  {
937
1654
  style: {
@@ -948,7 +1665,7 @@ function QuizPlayer({
948
1665
  ]
949
1666
  }
950
1667
  ),
951
- /* @__PURE__ */ jsx(
1668
+ /* @__PURE__ */ jsx3(
952
1669
  "div",
953
1670
  {
954
1671
  style: {
@@ -963,7 +1680,7 @@ function QuizPlayer({
963
1680
  ]
964
1681
  }
965
1682
  ),
966
- /* @__PURE__ */ jsxs("div", { style: { ...defaultStyles.resultDetails, marginTop: "8px" }, children: [
1683
+ /* @__PURE__ */ jsxs3("div", { style: { ...defaultStyles.resultDetails, marginTop: "8px" }, children: [
967
1684
  "Time: ",
968
1685
  formatTime(result.timeSpentSeconds)
969
1686
  ] })
@@ -972,16 +1689,16 @@ function QuizPlayer({
972
1689
  ] });
973
1690
  }
974
1691
  if (quiz && showIntro) {
975
- return /* @__PURE__ */ jsx("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsxs("div", { style: defaultStyles.intro, children: [
976
- /* @__PURE__ */ jsx("div", { style: defaultStyles.introTitle, children: quiz.title }),
977
- /* @__PURE__ */ jsx("div", { style: defaultStyles.introSubtitle, children: "Ready to test your knowledge?" }),
978
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.introQuestionCount, children: [
1692
+ return /* @__PURE__ */ jsx3("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsxs3("div", { style: defaultStyles.intro, children: [
1693
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.introTitle, children: quiz.title }),
1694
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.introSubtitle, children: "Ready to test your knowledge?" }),
1695
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.introQuestionCount, children: [
979
1696
  quiz.questions.length,
980
1697
  " question",
981
1698
  quiz.questions.length !== 1 ? "s" : "",
982
1699
  " to answer"
983
1700
  ] }),
984
- /* @__PURE__ */ jsx(
1701
+ /* @__PURE__ */ jsx3(
985
1702
  "button",
986
1703
  {
987
1704
  style: defaultStyles.startButton,
@@ -1001,7 +1718,7 @@ function QuizPlayer({
1001
1718
  ] }) });
1002
1719
  }
1003
1720
  if (!quiz || !currentQuestion) {
1004
- return /* @__PURE__ */ jsx("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx("div", { style: defaultStyles.error, children: "No quiz data available" }) });
1721
+ return /* @__PURE__ */ jsx3("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsx3("div", { style: defaultStyles.error, children: "No quiz data available" }) });
1005
1722
  }
1006
1723
  const selectedAnswer = answers.get(currentQuestion.id);
1007
1724
  const isLastQuestion = currentQuestionIndex === totalQuestions - 1;
@@ -1009,187 +1726,538 @@ function QuizPlayer({
1009
1726
  const remainingSlots = maxQuestions - totalQuestions;
1010
1727
  const questionsToAdd = Math.min(5, remainingSlots);
1011
1728
  const canAddMore = onGenerateMoreQuestions && remainingSlots > 0;
1012
- return /* @__PURE__ */ jsxs("div", { className, style: defaultStyles.container, children: [
1013
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.header, children: [
1014
- /* @__PURE__ */ jsx("div", { style: defaultStyles.title, children: quiz.title }),
1015
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.progress, children: [
1729
+ return /* @__PURE__ */ jsxs3("div", { className, style: defaultStyles.container, children: [
1730
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.header, children: [
1731
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.title, children: quiz.title }),
1732
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.progress, children: [
1016
1733
  "Question ",
1017
1734
  currentQuestionIndex + 1,
1018
1735
  " of ",
1019
1736
  totalQuestions
1020
1737
  ] }),
1021
- /* @__PURE__ */ jsx("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ jsx("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
1738
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ jsx3("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
1022
1739
  ] }),
1023
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.question, children: [
1024
- /* @__PURE__ */ jsx("div", { style: defaultStyles.questionText, children: currentQuestion.question }),
1025
- (currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ jsx("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
1026
- const isSelected = selectedAnswer === option;
1027
- const isCorrectOption = currentQuestion.correctAnswer === option;
1028
- let optionStyle = { ...defaultStyles.option };
1029
- if (showFeedback) {
1030
- if (isCorrectOption) {
1031
- optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
1032
- } else if (isSelected && !isCorrectOption) {
1033
- optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
1034
- }
1035
- } else if (isSelected) {
1036
- optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
1037
- }
1038
- return /* @__PURE__ */ jsx(
1039
- "div",
1040
- {
1041
- style: {
1042
- ...optionStyle,
1043
- cursor: showFeedback ? "default" : "pointer"
1044
- },
1045
- onClick: () => !showFeedback && handleAnswerChange(option),
1046
- children: option
1047
- },
1048
- idx
1049
- );
1050
- }) }),
1051
- currentQuestion.type === "multiple" && /* @__PURE__ */ jsx("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
1052
- const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
1053
- const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
1054
- const isCorrectOption = correctAnswers.includes(option);
1055
- let optionStyle = { ...defaultStyles.option };
1056
- if (showFeedback) {
1057
- if (isCorrectOption) {
1058
- optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
1059
- } else if (selected && !isCorrectOption) {
1060
- optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
1061
- }
1062
- } else if (selected) {
1063
- optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
1064
- }
1065
- return /* @__PURE__ */ jsx(
1066
- "div",
1067
- {
1068
- style: {
1069
- ...optionStyle,
1070
- cursor: showFeedback ? "default" : "pointer"
1071
- },
1072
- onClick: () => {
1073
- if (showFeedback) return;
1074
- const current = Array.isArray(selectedAnswer) ? selectedAnswer : [];
1075
- if (selected) {
1076
- handleAnswerChange(current.filter((o) => o !== option));
1077
- } else {
1078
- handleAnswerChange([...current, option]);
1740
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.mainLayout, children: [
1741
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.quizContent, children: [
1742
+ /* @__PURE__ */ jsxs3("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
1743
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ jsx3(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
1744
+ isExtraQuestion && !showFeedback && /* @__PURE__ */ jsxs3(
1745
+ "button",
1746
+ {
1747
+ onClick: () => setShowSkipModal(true),
1748
+ title: "Skip question",
1749
+ style: {
1750
+ position: "absolute",
1751
+ bottom: "8px",
1752
+ left: "0",
1753
+ background: "transparent",
1754
+ border: "none",
1755
+ cursor: "pointer",
1756
+ padding: "6px 10px",
1757
+ borderRadius: "6px",
1758
+ color: "#9ca3af",
1759
+ display: "flex",
1760
+ alignItems: "center",
1761
+ justifyContent: "center",
1762
+ gap: "4px",
1763
+ fontSize: "12px",
1764
+ opacity: 0.6,
1765
+ transition: "opacity 0.2s, color 0.2s"
1766
+ },
1767
+ onMouseEnter: (e) => {
1768
+ e.currentTarget.style.opacity = "1";
1769
+ e.currentTarget.style.color = "#6b7280";
1770
+ },
1771
+ onMouseLeave: (e) => {
1772
+ e.currentTarget.style.opacity = "0.6";
1773
+ e.currentTarget.style.color = "#9ca3af";
1774
+ },
1775
+ "data-testid": "button-skip-question",
1776
+ children: [
1777
+ /* @__PURE__ */ jsxs3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1778
+ /* @__PURE__ */ jsx3("polygon", { points: "5 4 15 12 5 20 5 4" }),
1779
+ /* @__PURE__ */ jsx3("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
1780
+ ] }),
1781
+ /* @__PURE__ */ jsx3("span", { children: "Skip" })
1782
+ ]
1783
+ }
1784
+ ),
1785
+ !isExtraQuestion && !showFeedback && /* @__PURE__ */ jsxs3(
1786
+ "button",
1787
+ {
1788
+ onClick: () => setShowReportModal(true),
1789
+ title: "Report an issue with this question",
1790
+ style: {
1791
+ position: "absolute",
1792
+ bottom: "8px",
1793
+ left: "0",
1794
+ background: "transparent",
1795
+ border: "none",
1796
+ cursor: "pointer",
1797
+ padding: "6px 10px",
1798
+ borderRadius: "6px",
1799
+ color: "#9ca3af",
1800
+ display: "flex",
1801
+ alignItems: "center",
1802
+ justifyContent: "center",
1803
+ gap: "4px",
1804
+ fontSize: "12px",
1805
+ opacity: 0.6,
1806
+ transition: "opacity 0.2s, color 0.2s"
1807
+ },
1808
+ onMouseEnter: (e) => {
1809
+ e.currentTarget.style.opacity = "1";
1810
+ e.currentTarget.style.color = "#ef4444";
1811
+ },
1812
+ onMouseLeave: (e) => {
1813
+ e.currentTarget.style.opacity = "0.6";
1814
+ e.currentTarget.style.color = "#9ca3af";
1815
+ },
1816
+ "data-testid": "button-report-question",
1817
+ children: [
1818
+ /* @__PURE__ */ jsxs3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1819
+ /* @__PURE__ */ jsx3("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" }),
1820
+ /* @__PURE__ */ jsx3("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
1821
+ ] }),
1822
+ /* @__PURE__ */ jsx3("span", { children: "Report" })
1823
+ ]
1824
+ }
1825
+ ),
1826
+ (currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
1827
+ const isSelected = selectedAnswer === option;
1828
+ const isCorrectOption = currentQuestion.correctAnswer === option;
1829
+ let optionStyle = { ...defaultStyles.option };
1830
+ if (showFeedback) {
1831
+ if (isCorrectOption) {
1832
+ optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
1833
+ } else if (isSelected && !isCorrectOption) {
1834
+ optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
1079
1835
  }
1836
+ } else if (isSelected) {
1837
+ optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
1838
+ }
1839
+ return /* @__PURE__ */ jsxs3(
1840
+ "div",
1841
+ {
1842
+ style: {
1843
+ ...optionStyle,
1844
+ cursor: showFeedback ? "default" : "pointer",
1845
+ display: "flex",
1846
+ alignItems: "center",
1847
+ gap: "8px"
1848
+ },
1849
+ onClick: () => !showFeedback && handleAnswerChange(option),
1850
+ children: [
1851
+ /* @__PURE__ */ jsx3("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx3(TextToSpeech, { text: option, size: "sm" }) }),
1852
+ /* @__PURE__ */ jsx3("span", { style: { flex: 1 }, children: option })
1853
+ ]
1854
+ },
1855
+ idx
1856
+ );
1857
+ }) }),
1858
+ currentQuestion.type === "multiple" && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
1859
+ const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
1860
+ const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
1861
+ const isCorrectOption = correctAnswers.includes(option);
1862
+ let optionStyle = { ...defaultStyles.option };
1863
+ if (showFeedback) {
1864
+ if (isCorrectOption) {
1865
+ optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
1866
+ } else if (selected && !isCorrectOption) {
1867
+ optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
1868
+ }
1869
+ } else if (selected) {
1870
+ optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
1871
+ }
1872
+ return /* @__PURE__ */ jsxs3(
1873
+ "div",
1874
+ {
1875
+ style: {
1876
+ ...optionStyle,
1877
+ cursor: showFeedback ? "default" : "pointer",
1878
+ display: "flex",
1879
+ alignItems: "center",
1880
+ gap: "8px"
1881
+ },
1882
+ onClick: () => {
1883
+ if (showFeedback) return;
1884
+ const current = Array.isArray(selectedAnswer) ? selectedAnswer : [];
1885
+ if (selected) {
1886
+ handleAnswerChange(current.filter((o) => o !== option));
1887
+ } else {
1888
+ handleAnswerChange([...current, option]);
1889
+ }
1890
+ },
1891
+ children: [
1892
+ /* @__PURE__ */ jsx3("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx3(TextToSpeech, { text: option, size: "sm" }) }),
1893
+ /* @__PURE__ */ jsx3("span", { style: { flex: 1 }, children: option })
1894
+ ]
1895
+ },
1896
+ idx
1897
+ );
1898
+ }) }),
1899
+ (currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ jsx3(
1900
+ "textarea",
1901
+ {
1902
+ style: { ...defaultStyles.input, minHeight: currentQuestion.type === "essay" ? "150px" : "60px" },
1903
+ value: selectedAnswer || "",
1904
+ onChange: (e) => handleAnswerChange(e.target.value),
1905
+ placeholder: "Type your answer here...",
1906
+ disabled: showFeedback
1907
+ }
1908
+ ),
1909
+ currentQuestion.type === "fill" && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ jsx3(
1910
+ "input",
1911
+ {
1912
+ style: defaultStyles.input,
1913
+ value: (Array.isArray(selectedAnswer) ? selectedAnswer[idx] : "") || "",
1914
+ onChange: (e) => {
1915
+ const current = Array.isArray(selectedAnswer) ? [...selectedAnswer] : [];
1916
+ current[idx] = e.target.value;
1917
+ handleAnswerChange(current);
1918
+ },
1919
+ placeholder: `Blank ${idx + 1}`,
1920
+ disabled: showFeedback
1080
1921
  },
1081
- children: option
1082
- },
1083
- idx
1084
- );
1085
- }) }),
1086
- (currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ jsx(
1087
- "textarea",
1088
- {
1089
- style: { ...defaultStyles.input, minHeight: currentQuestion.type === "essay" ? "150px" : "60px" },
1090
- value: selectedAnswer || "",
1091
- onChange: (e) => handleAnswerChange(e.target.value),
1092
- placeholder: "Type your answer here...",
1093
- disabled: showFeedback
1094
- }
1095
- ),
1096
- currentQuestion.type === "fill" && /* @__PURE__ */ jsx("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ jsx(
1097
- "input",
1098
- {
1099
- style: defaultStyles.input,
1100
- value: (Array.isArray(selectedAnswer) ? selectedAnswer[idx] : "") || "",
1101
- onChange: (e) => {
1102
- const current = Array.isArray(selectedAnswer) ? [...selectedAnswer] : [];
1103
- current[idx] = e.target.value;
1104
- handleAnswerChange(current);
1105
- },
1106
- placeholder: `Blank ${idx + 1}`,
1107
- disabled: showFeedback
1108
- },
1109
- idx
1110
- )) }),
1111
- showFeedback && currentAnswerDetail && /* @__PURE__ */ jsxs("div", { style: {
1112
- ...defaultStyles.feedback,
1113
- ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
1114
- }, children: [
1115
- /* @__PURE__ */ jsx("div", { style: {
1116
- ...defaultStyles.feedbackTitle,
1117
- ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
1118
- }, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
1119
- currentQuestion.explanation && /* @__PURE__ */ jsx("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
1120
- ] })
1121
- ] }),
1122
- /* @__PURE__ */ jsxs("div", { style: defaultStyles.buttonsColumn, children: [
1123
- showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ jsx(
1124
- "button",
1922
+ idx
1923
+ )) }),
1924
+ showFeedback && currentAnswerDetail && /* @__PURE__ */ jsxs3("div", { style: {
1925
+ ...defaultStyles.feedback,
1926
+ ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
1927
+ }, children: [
1928
+ /* @__PURE__ */ jsx3("div", { style: {
1929
+ ...defaultStyles.feedbackTitle,
1930
+ ...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
1931
+ }, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
1932
+ currentQuestion.explanation && /* @__PURE__ */ jsx3("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
1933
+ ] })
1934
+ ] }),
1935
+ showSkipModal && /* @__PURE__ */ jsx3("div", { style: {
1936
+ position: "fixed",
1937
+ top: 0,
1938
+ left: 0,
1939
+ right: 0,
1940
+ bottom: 0,
1941
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
1942
+ display: "flex",
1943
+ alignItems: "center",
1944
+ justifyContent: "center",
1945
+ zIndex: 1e3
1946
+ }, children: /* @__PURE__ */ jsxs3("div", { style: {
1947
+ backgroundColor: "#ffffff",
1948
+ borderRadius: "12px",
1949
+ padding: "24px",
1950
+ maxWidth: "400px",
1951
+ width: "90%",
1952
+ boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
1953
+ }, children: [
1954
+ /* @__PURE__ */ jsx3("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
1955
+ /* @__PURE__ */ jsx3("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "Help us improve by telling us why you're skipping this question." }),
1956
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
1957
+ /* @__PURE__ */ jsx3(
1958
+ "button",
1959
+ {
1960
+ onClick: () => setSelectedSkipReason("question_issue"),
1961
+ disabled: isSkipping,
1962
+ style: {
1963
+ padding: "12px 16px",
1964
+ borderRadius: "8px",
1965
+ border: selectedSkipReason === "question_issue" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
1966
+ backgroundColor: selectedSkipReason === "question_issue" ? "#f5f3ff" : "#f9fafb",
1967
+ cursor: isSkipping ? "not-allowed" : "pointer",
1968
+ fontSize: "14px",
1969
+ fontWeight: "500",
1970
+ color: "#374151",
1971
+ textAlign: "left",
1972
+ opacity: isSkipping ? 0.6 : 1
1973
+ },
1974
+ "data-testid": "button-skip-reason-issue",
1975
+ children: "Question has an issue"
1976
+ }
1977
+ ),
1978
+ /* @__PURE__ */ jsx3(
1979
+ "button",
1980
+ {
1981
+ onClick: () => setSelectedSkipReason("dont_know"),
1982
+ disabled: isSkipping,
1983
+ style: {
1984
+ padding: "12px 16px",
1985
+ borderRadius: "8px",
1986
+ border: selectedSkipReason === "dont_know" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
1987
+ backgroundColor: selectedSkipReason === "dont_know" ? "#f5f3ff" : "#f9fafb",
1988
+ cursor: isSkipping ? "not-allowed" : "pointer",
1989
+ fontSize: "14px",
1990
+ fontWeight: "500",
1991
+ color: "#374151",
1992
+ textAlign: "left",
1993
+ opacity: isSkipping ? 0.6 : 1
1994
+ },
1995
+ "data-testid": "button-skip-reason-dont-know",
1996
+ children: "I don't know the answer"
1997
+ }
1998
+ )
1999
+ ] }),
2000
+ /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "16px" }, children: [
2001
+ /* @__PURE__ */ jsx3("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
2002
+ /* @__PURE__ */ jsx3(
2003
+ "textarea",
2004
+ {
2005
+ value: skipComment,
2006
+ onChange: (e) => setSkipComment(e.target.value.slice(0, 200)),
2007
+ placeholder: "Tell us more about the issue...",
2008
+ disabled: isSkipping,
2009
+ style: {
2010
+ width: "100%",
2011
+ minHeight: "80px",
2012
+ padding: "10px 12px",
2013
+ borderRadius: "8px",
2014
+ border: "1px solid #e5e7eb",
2015
+ fontSize: "14px",
2016
+ resize: "vertical",
2017
+ fontFamily: "inherit",
2018
+ boxSizing: "border-box"
2019
+ },
2020
+ "data-testid": "input-skip-comment"
2021
+ }
2022
+ ),
2023
+ /* @__PURE__ */ jsxs3("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
2024
+ skipComment.length,
2025
+ "/200"
2026
+ ] })
2027
+ ] }),
2028
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "10px" }, children: [
2029
+ /* @__PURE__ */ jsx3(
2030
+ "button",
2031
+ {
2032
+ onClick: () => {
2033
+ setShowSkipModal(false);
2034
+ setSkipComment("");
2035
+ setSelectedSkipReason(null);
2036
+ },
2037
+ style: {
2038
+ flex: 1,
2039
+ padding: "10px 16px",
2040
+ borderRadius: "8px",
2041
+ border: "1px solid #e5e7eb",
2042
+ backgroundColor: "#ffffff",
2043
+ cursor: "pointer",
2044
+ fontSize: "14px",
2045
+ fontWeight: "500",
2046
+ color: "#6b7280"
2047
+ },
2048
+ "data-testid": "button-skip-cancel",
2049
+ children: "Cancel"
2050
+ }
2051
+ ),
2052
+ /* @__PURE__ */ jsx3(
2053
+ "button",
2054
+ {
2055
+ onClick: () => selectedSkipReason && handleSkipQuestion(selectedSkipReason, skipComment),
2056
+ disabled: isSkipping || !selectedSkipReason,
2057
+ style: {
2058
+ flex: 1,
2059
+ padding: "10px 16px",
2060
+ borderRadius: "8px",
2061
+ border: "none",
2062
+ backgroundColor: selectedSkipReason ? "#8b5cf6" : "#d1d5db",
2063
+ cursor: isSkipping || !selectedSkipReason ? "not-allowed" : "pointer",
2064
+ fontSize: "14px",
2065
+ fontWeight: "500",
2066
+ color: "#ffffff",
2067
+ opacity: isSkipping ? 0.6 : 1
2068
+ },
2069
+ "data-testid": "button-skip-submit",
2070
+ children: isSkipping ? "Skipping..." : "Skip Question"
2071
+ }
2072
+ )
2073
+ ] })
2074
+ ] }) }),
2075
+ showReportModal && /* @__PURE__ */ jsx3("div", { style: {
2076
+ position: "fixed",
2077
+ top: 0,
2078
+ left: 0,
2079
+ right: 0,
2080
+ bottom: 0,
2081
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
2082
+ display: "flex",
2083
+ alignItems: "center",
2084
+ justifyContent: "center",
2085
+ zIndex: 1e3
2086
+ }, children: /* @__PURE__ */ jsxs3("div", { style: {
2087
+ backgroundColor: "#ffffff",
2088
+ borderRadius: "12px",
2089
+ padding: "24px",
2090
+ maxWidth: "400px",
2091
+ width: "90%",
2092
+ boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
2093
+ }, children: [
2094
+ /* @__PURE__ */ jsx3("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
2095
+ /* @__PURE__ */ jsx3("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
2096
+ /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "16px" }, children: [
2097
+ /* @__PURE__ */ jsx3(
2098
+ "textarea",
2099
+ {
2100
+ value: reportComment,
2101
+ onChange: (e) => setReportComment(e.target.value.slice(0, 300)),
2102
+ placeholder: "Describe the issue with this question...",
2103
+ disabled: isReporting,
2104
+ style: {
2105
+ width: "100%",
2106
+ minHeight: "120px",
2107
+ padding: "10px 12px",
2108
+ borderRadius: "8px",
2109
+ border: "1px solid #e5e7eb",
2110
+ fontSize: "14px",
2111
+ resize: "vertical",
2112
+ fontFamily: "inherit",
2113
+ boxSizing: "border-box"
2114
+ },
2115
+ "data-testid": "input-report-comment"
2116
+ }
2117
+ ),
2118
+ /* @__PURE__ */ jsxs3("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
2119
+ reportComment.length,
2120
+ "/300"
2121
+ ] })
2122
+ ] }),
2123
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "10px" }, children: [
2124
+ /* @__PURE__ */ jsx3(
2125
+ "button",
2126
+ {
2127
+ onClick: () => {
2128
+ setShowReportModal(false);
2129
+ setReportComment("");
2130
+ },
2131
+ style: {
2132
+ flex: 1,
2133
+ padding: "10px 16px",
2134
+ borderRadius: "8px",
2135
+ border: "1px solid #e5e7eb",
2136
+ backgroundColor: "#ffffff",
2137
+ cursor: "pointer",
2138
+ fontSize: "14px",
2139
+ fontWeight: "500",
2140
+ color: "#6b7280"
2141
+ },
2142
+ "data-testid": "button-report-cancel",
2143
+ children: "Cancel"
2144
+ }
2145
+ ),
2146
+ /* @__PURE__ */ jsx3(
2147
+ "button",
2148
+ {
2149
+ onClick: () => handleReportQuestion(reportComment),
2150
+ disabled: isReporting || !reportComment.trim(),
2151
+ style: {
2152
+ flex: 1,
2153
+ padding: "10px 16px",
2154
+ borderRadius: "8px",
2155
+ border: "none",
2156
+ backgroundColor: reportComment.trim() ? "#ef4444" : "#d1d5db",
2157
+ cursor: isReporting || !reportComment.trim() ? "not-allowed" : "pointer",
2158
+ fontSize: "14px",
2159
+ fontWeight: "500",
2160
+ color: "#ffffff",
2161
+ opacity: isReporting ? 0.6 : 1
2162
+ },
2163
+ "data-testid": "button-report-submit",
2164
+ children: isReporting ? "Reporting..." : "Report"
2165
+ }
2166
+ )
2167
+ ] })
2168
+ ] }) }),
2169
+ /* @__PURE__ */ jsxs3("div", { style: defaultStyles.buttonsColumn, children: [
2170
+ showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ jsx3(
2171
+ "button",
2172
+ {
2173
+ style: {
2174
+ ...defaultStyles.buttonAddMore,
2175
+ ...isGeneratingExtra ? defaultStyles.buttonAddMoreDisabled : {}
2176
+ },
2177
+ onClick: handleAddMoreQuestions,
2178
+ disabled: isGeneratingExtra,
2179
+ "data-testid": "button-add-more-questions",
2180
+ children: isGeneratingExtra ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2181
+ /* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }),
2182
+ "Generating Questions..."
2183
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2184
+ "+ Add ",
2185
+ questionsToAdd,
2186
+ " More Question",
2187
+ questionsToAdd !== 1 ? "s" : ""
2188
+ ] })
2189
+ }
2190
+ ),
2191
+ /* @__PURE__ */ jsx3("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
2192
+ // After viewing feedback
2193
+ isLastQuestion ? /* @__PURE__ */ jsx3(
2194
+ "button",
2195
+ {
2196
+ style: {
2197
+ ...defaultStyles.button,
2198
+ ...isSubmitting || isGeneratingExtra ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
2199
+ },
2200
+ onClick: handleSubmit,
2201
+ disabled: isSubmitting || isGeneratingExtra,
2202
+ "data-testid": "button-submit-quiz",
2203
+ children: isSubmitting ? /* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
2204
+ }
2205
+ ) : /* @__PURE__ */ jsx3(
2206
+ "button",
2207
+ {
2208
+ style: {
2209
+ ...defaultStyles.button,
2210
+ ...defaultStyles.buttonPrimary
2211
+ },
2212
+ onClick: handleContinue,
2213
+ "data-testid": "button-continue",
2214
+ children: "Continue"
2215
+ }
2216
+ )
2217
+ ) : (
2218
+ // Before checking answer
2219
+ /* @__PURE__ */ jsx3(
2220
+ "button",
2221
+ {
2222
+ style: {
2223
+ ...defaultStyles.button,
2224
+ ...isNavigating || selectedAnswer === void 0 ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
2225
+ },
2226
+ onClick: handleCheckAnswer,
2227
+ disabled: isNavigating || selectedAnswer === void 0,
2228
+ "data-testid": "button-check-answer",
2229
+ children: isNavigating ? /* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
2230
+ }
2231
+ )
2232
+ ) })
2233
+ ] })
2234
+ ] }),
2235
+ /* @__PURE__ */ jsx3("div", { style: defaultStyles.chatPanel, children: apiClient.current && /* @__PURE__ */ jsx3(
2236
+ QuestionChatPanel,
1125
2237
  {
1126
- style: {
1127
- ...defaultStyles.buttonAddMore,
1128
- ...isGeneratingExtra ? defaultStyles.buttonAddMoreDisabled : {}
2238
+ apiClient: apiClient.current,
2239
+ question: {
2240
+ id: currentQuestion.id,
2241
+ question: currentQuestion.question,
2242
+ type: currentQuestion.type,
2243
+ options: currentQuestion.options,
2244
+ correctAnswer: currentQuestion.correctAnswer,
2245
+ explanation: currentQuestion.explanation
1129
2246
  },
1130
- onClick: handleAddMoreQuestions,
1131
- disabled: isGeneratingExtra,
1132
- "data-testid": "button-add-more-questions",
1133
- children: isGeneratingExtra ? /* @__PURE__ */ jsxs(Fragment, { children: [
1134
- /* @__PURE__ */ jsx(Spinner, { size: 16, color: "#9ca3af" }),
1135
- "Generating Questions..."
1136
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1137
- "+ Add ",
1138
- questionsToAdd,
1139
- " More Question",
1140
- questionsToAdd !== 1 ? "s" : ""
1141
- ] })
2247
+ quizId: quiz.id,
2248
+ childId,
2249
+ parentId,
2250
+ lessonId,
2251
+ courseId
1142
2252
  }
1143
- ),
1144
- /* @__PURE__ */ jsx("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
1145
- // After viewing feedback
1146
- isLastQuestion ? /* @__PURE__ */ jsx(
1147
- "button",
1148
- {
1149
- style: {
1150
- ...defaultStyles.button,
1151
- ...isSubmitting || isGeneratingExtra ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
1152
- },
1153
- onClick: handleSubmit,
1154
- disabled: isSubmitting || isGeneratingExtra,
1155
- "data-testid": "button-submit-quiz",
1156
- children: isSubmitting ? /* @__PURE__ */ jsx(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
1157
- }
1158
- ) : /* @__PURE__ */ jsx(
1159
- "button",
1160
- {
1161
- style: {
1162
- ...defaultStyles.button,
1163
- ...defaultStyles.buttonPrimary
1164
- },
1165
- onClick: handleContinue,
1166
- "data-testid": "button-continue",
1167
- children: "Continue"
1168
- }
1169
- )
1170
- ) : (
1171
- // Before checking answer
1172
- /* @__PURE__ */ jsx(
1173
- "button",
1174
- {
1175
- style: {
1176
- ...defaultStyles.button,
1177
- ...isNavigating || selectedAnswer === void 0 ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
1178
- },
1179
- onClick: handleCheckAnswer,
1180
- disabled: isNavigating || selectedAnswer === void 0,
1181
- "data-testid": "button-check-answer",
1182
- children: isNavigating ? /* @__PURE__ */ jsx(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
1183
- }
1184
- )
1185
2253
  ) })
1186
2254
  ] })
1187
2255
  ] });
1188
2256
  }
1189
2257
 
1190
2258
  // src/AttemptViewer.tsx
1191
- import { useState as useState2, useEffect as useEffect2 } from "react";
1192
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2259
+ import { useState as useState4, useEffect as useEffect4 } from "react";
2260
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1193
2261
  var defaultStyles2 = {
1194
2262
  container: {
1195
2263
  fontFamily: "system-ui, -apple-system, sans-serif",
@@ -1369,10 +2437,10 @@ function AttemptViewer({
1369
2437
  showExplanations = true,
1370
2438
  title
1371
2439
  }) {
1372
- const [attempt, setAttempt] = useState2(null);
1373
- const [loading, setLoading] = useState2(true);
1374
- const [error, setError] = useState2(null);
1375
- useEffect2(() => {
2440
+ const [attempt, setAttempt] = useState4(null);
2441
+ const [loading, setLoading] = useState4(true);
2442
+ const [error, setError] = useState4(null);
2443
+ useEffect4(() => {
1376
2444
  const apiClient = new QuizApiClient({
1377
2445
  baseUrl: apiBaseUrl,
1378
2446
  authToken
@@ -1405,49 +2473,49 @@ function AttemptViewer({
1405
2473
  window.location.reload();
1406
2474
  };
1407
2475
  if (loading) {
1408
- return /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.container, className, children: [
1409
- /* @__PURE__ */ jsx2("style", { children: spinnerKeyframes }),
1410
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.loading, children: [
1411
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.spinner }),
1412
- /* @__PURE__ */ jsx2("p", { style: { marginTop: "16px", color: "#6b7280" }, children: "Loading attempt..." })
2476
+ return /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.container, className, children: [
2477
+ /* @__PURE__ */ jsx4("style", { children: spinnerKeyframes }),
2478
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.loading, children: [
2479
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.spinner }),
2480
+ /* @__PURE__ */ jsx4("p", { style: { marginTop: "16px", color: "#6b7280" }, children: "Loading attempt..." })
1413
2481
  ] })
1414
2482
  ] });
1415
2483
  }
1416
2484
  if (error || !attempt) {
1417
- return /* @__PURE__ */ jsx2("div", { style: defaultStyles2.container, className, children: /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.error, children: [
1418
- /* @__PURE__ */ jsx2("p", { style: { fontSize: "18px", fontWeight: "500" }, children: "Failed to load attempt" }),
1419
- /* @__PURE__ */ jsx2("p", { style: { marginTop: "8px", color: "#6b7280" }, children: error }),
1420
- /* @__PURE__ */ jsx2("button", { style: defaultStyles2.retryButton, onClick: handleRetry, children: "Try Again" })
2485
+ return /* @__PURE__ */ jsx4("div", { style: defaultStyles2.container, className, children: /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.error, children: [
2486
+ /* @__PURE__ */ jsx4("p", { style: { fontSize: "18px", fontWeight: "500" }, children: "Failed to load attempt" }),
2487
+ /* @__PURE__ */ jsx4("p", { style: { marginTop: "8px", color: "#6b7280" }, children: error }),
2488
+ /* @__PURE__ */ jsx4("button", { style: defaultStyles2.retryButton, onClick: handleRetry, children: "Try Again" })
1421
2489
  ] }) });
1422
2490
  }
1423
2491
  const scorePercentage = attempt.score ?? 0;
1424
2492
  const correctCount = attempt.correctAnswers ?? 0;
1425
2493
  const totalQuestions = attempt.totalQuestions;
1426
2494
  const timeSpent = attempt.timeSpentSeconds ?? 0;
1427
- return /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.container, className, children: [
1428
- /* @__PURE__ */ jsx2("style", { children: spinnerKeyframes }),
1429
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.header, children: /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryGrid, children: [
1430
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryCard, children: [
1431
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryValue, children: [
2495
+ return /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.container, className, children: [
2496
+ /* @__PURE__ */ jsx4("style", { children: spinnerKeyframes }),
2497
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.header, children: /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryGrid, children: [
2498
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryCard, children: [
2499
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryValue, children: [
1432
2500
  scorePercentage,
1433
2501
  "%"
1434
2502
  ] }),
1435
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.summaryLabel, children: "Score" })
2503
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.summaryLabel, children: "Score" })
1436
2504
  ] }),
1437
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryCard, children: [
1438
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryValue, children: [
2505
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryCard, children: [
2506
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryValue, children: [
1439
2507
  correctCount,
1440
2508
  "/",
1441
2509
  totalQuestions
1442
2510
  ] }),
1443
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.summaryLabel, children: "Correct" })
2511
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.summaryLabel, children: "Correct" })
1444
2512
  ] }),
1445
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.summaryCard, children: [
1446
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.summaryValue, children: formatTime(timeSpent) }),
1447
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.summaryLabel, children: "Time" })
2513
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.summaryCard, children: [
2514
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.summaryValue, children: formatTime(timeSpent) }),
2515
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.summaryLabel, children: "Time" })
1448
2516
  ] })
1449
2517
  ] }) }),
1450
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.questionsList, children: attempt.answers.map((answer, index) => /* @__PURE__ */ jsxs2(
2518
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.questionsList, children: attempt.answers.map((answer, index) => /* @__PURE__ */ jsxs4(
1451
2519
  "div",
1452
2520
  {
1453
2521
  style: {
@@ -1455,12 +2523,12 @@ function AttemptViewer({
1455
2523
  ...answer.isCorrect ? defaultStyles2.questionCardCorrect : defaultStyles2.questionCardIncorrect
1456
2524
  },
1457
2525
  children: [
1458
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.questionHeader, children: [
1459
- /* @__PURE__ */ jsxs2("span", { style: defaultStyles2.questionNumber, children: [
2526
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.questionHeader, children: [
2527
+ /* @__PURE__ */ jsxs4("span", { style: defaultStyles2.questionNumber, children: [
1460
2528
  "Question ",
1461
2529
  index + 1
1462
2530
  ] }),
1463
- /* @__PURE__ */ jsx2(
2531
+ /* @__PURE__ */ jsx4(
1464
2532
  "span",
1465
2533
  {
1466
2534
  style: {
@@ -1471,23 +2539,23 @@ function AttemptViewer({
1471
2539
  }
1472
2540
  )
1473
2541
  ] }),
1474
- /* @__PURE__ */ jsx2("div", { style: defaultStyles2.questionText, children: answer.questionText }),
1475
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.answerSection, children: [
1476
- /* @__PURE__ */ jsx2("span", { style: defaultStyles2.answerLabel, children: "Your answer:" }),
1477
- /* @__PURE__ */ jsx2("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer) })
2542
+ /* @__PURE__ */ jsx4("div", { style: defaultStyles2.questionText, children: answer.questionText }),
2543
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.answerSection, children: [
2544
+ /* @__PURE__ */ jsx4("span", { style: defaultStyles2.answerLabel, children: "Your answer:" }),
2545
+ /* @__PURE__ */ jsx4("span", { style: defaultStyles2.studentAnswer, children: formatAnswer(answer.selectedAnswer) })
1478
2546
  ] }),
1479
- !answer.isCorrect && answer.correctAnswer && /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.answerSection, children: [
1480
- /* @__PURE__ */ jsx2("span", { style: defaultStyles2.answerLabel, children: "Correct answer:" }),
1481
- /* @__PURE__ */ jsx2("span", { style: defaultStyles2.correctAnswer, children: formatAnswer(answer.correctAnswer) })
2547
+ !answer.isCorrect && answer.correctAnswer && /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.answerSection, children: [
2548
+ /* @__PURE__ */ jsx4("span", { style: defaultStyles2.answerLabel, children: "Correct answer:" }),
2549
+ /* @__PURE__ */ jsx4("span", { style: defaultStyles2.correctAnswer, children: formatAnswer(answer.correctAnswer) })
1482
2550
  ] }),
1483
- /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.points, children: [
2551
+ /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.points, children: [
1484
2552
  answer.pointsEarned,
1485
2553
  " / ",
1486
2554
  answer.points,
1487
2555
  " points"
1488
2556
  ] }),
1489
- showExplanations && answer.explanation && /* @__PURE__ */ jsxs2("div", { style: defaultStyles2.explanation, children: [
1490
- /* @__PURE__ */ jsx2("strong", { children: "Explanation:" }),
2557
+ showExplanations && answer.explanation && /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.explanation, children: [
2558
+ /* @__PURE__ */ jsx4("strong", { children: "Explanation:" }),
1491
2559
  " ",
1492
2560
  answer.explanation
1493
2561
  ] })
@@ -1501,6 +2569,7 @@ export {
1501
2569
  AttemptViewer,
1502
2570
  QuizApiClient,
1503
2571
  QuizPlayer,
2572
+ TextToSpeech,
1504
2573
  calculateScore,
1505
2574
  checkAnswer,
1506
2575
  createAnswerDetail,