@schoolio/player 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +914 -535
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +914 -535
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -80,6 +80,29 @@ var QuizApiClient = class {
|
|
|
80
80
|
`/api/external/question-chat/${questionId}/${childId}`
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
|
+
async getChatsByAttempt(attemptId) {
|
|
84
|
+
return this.request(
|
|
85
|
+
"GET",
|
|
86
|
+
`/api/external/quiz-attempts/${attemptId}/chats`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
async getTextToSpeech(text, voice = "nova") {
|
|
90
|
+
const headers = {
|
|
91
|
+
"Content-Type": "application/json"
|
|
92
|
+
};
|
|
93
|
+
if (this.authToken) {
|
|
94
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
95
|
+
}
|
|
96
|
+
const response = await fetch(`${this.baseUrl}/api/external/tts`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers,
|
|
99
|
+
body: JSON.stringify({ text, voice })
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(`TTS request failed: HTTP ${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
return response.blob();
|
|
105
|
+
}
|
|
83
106
|
};
|
|
84
107
|
|
|
85
108
|
// src/utils.ts
|
|
@@ -482,9 +505,13 @@ var panelStyles = {
|
|
|
482
505
|
messageRow: {
|
|
483
506
|
display: "flex"
|
|
484
507
|
},
|
|
508
|
+
messageContent: {
|
|
509
|
+
display: "flex",
|
|
510
|
+
alignItems: "flex-end",
|
|
511
|
+
gap: "4px",
|
|
512
|
+
maxWidth: "85%"
|
|
513
|
+
},
|
|
485
514
|
userMessage: {
|
|
486
|
-
maxWidth: "85%",
|
|
487
|
-
marginLeft: "auto",
|
|
488
515
|
padding: "10px 14px",
|
|
489
516
|
borderRadius: "16px 16px 4px 16px",
|
|
490
517
|
backgroundColor: "#6721b0",
|
|
@@ -493,8 +520,6 @@ var panelStyles = {
|
|
|
493
520
|
lineHeight: 1.4
|
|
494
521
|
},
|
|
495
522
|
assistantMessage: {
|
|
496
|
-
maxWidth: "85%",
|
|
497
|
-
marginRight: "auto",
|
|
498
523
|
padding: "10px 14px",
|
|
499
524
|
borderRadius: "16px 16px 16px 4px",
|
|
500
525
|
backgroundColor: "#ffffff",
|
|
@@ -508,7 +533,8 @@ var panelStyles = {
|
|
|
508
533
|
borderTop: "1px solid #e2e8f0",
|
|
509
534
|
backgroundColor: "#ffffff",
|
|
510
535
|
display: "flex",
|
|
511
|
-
gap: "8px"
|
|
536
|
+
gap: "8px",
|
|
537
|
+
alignItems: "center"
|
|
512
538
|
},
|
|
513
539
|
input: {
|
|
514
540
|
flex: 1,
|
|
@@ -518,13 +544,11 @@ var panelStyles = {
|
|
|
518
544
|
fontSize: "14px",
|
|
519
545
|
outline: "none"
|
|
520
546
|
},
|
|
521
|
-
|
|
547
|
+
buttonBase: {
|
|
522
548
|
width: "40px",
|
|
523
549
|
height: "40px",
|
|
524
550
|
borderRadius: "50%",
|
|
525
551
|
border: "none",
|
|
526
|
-
backgroundColor: "#6721b0",
|
|
527
|
-
color: "#ffffff",
|
|
528
552
|
cursor: "pointer",
|
|
529
553
|
display: "flex",
|
|
530
554
|
alignItems: "center",
|
|
@@ -532,10 +556,47 @@ var panelStyles = {
|
|
|
532
556
|
fontSize: "16px",
|
|
533
557
|
transition: "all 0.2s ease"
|
|
534
558
|
},
|
|
559
|
+
sendButton: {
|
|
560
|
+
backgroundColor: "#6721b0",
|
|
561
|
+
color: "#ffffff"
|
|
562
|
+
},
|
|
535
563
|
sendButtonDisabled: {
|
|
536
564
|
backgroundColor: "#d1d5db",
|
|
537
565
|
cursor: "not-allowed"
|
|
538
566
|
},
|
|
567
|
+
micButton: {
|
|
568
|
+
backgroundColor: "#ffffff",
|
|
569
|
+
border: "1px solid #e2e8f0",
|
|
570
|
+
color: "#374151"
|
|
571
|
+
},
|
|
572
|
+
micButtonActive: {
|
|
573
|
+
backgroundColor: "#ef4444",
|
|
574
|
+
color: "#ffffff",
|
|
575
|
+
border: "none"
|
|
576
|
+
},
|
|
577
|
+
speakButton: {
|
|
578
|
+
width: "28px",
|
|
579
|
+
height: "28px",
|
|
580
|
+
borderRadius: "50%",
|
|
581
|
+
border: "none",
|
|
582
|
+
backgroundColor: "transparent",
|
|
583
|
+
color: "#9ca3af",
|
|
584
|
+
cursor: "pointer",
|
|
585
|
+
display: "flex",
|
|
586
|
+
alignItems: "center",
|
|
587
|
+
justifyContent: "center",
|
|
588
|
+
transition: "all 0.2s ease",
|
|
589
|
+
flexShrink: 0
|
|
590
|
+
},
|
|
591
|
+
speakButtonReady: {
|
|
592
|
+
color: "#22c55e"
|
|
593
|
+
},
|
|
594
|
+
speakButtonPlaying: {
|
|
595
|
+
color: "#6721b0"
|
|
596
|
+
},
|
|
597
|
+
speakButtonLoading: {
|
|
598
|
+
color: "#f97316"
|
|
599
|
+
},
|
|
539
600
|
loadingDots: {
|
|
540
601
|
display: "flex",
|
|
541
602
|
alignItems: "center",
|
|
@@ -573,6 +634,33 @@ var STARTER_PROMPTS = [
|
|
|
573
634
|
{ id: "word_help", label: "I don't understand a word", message: "I don't understand a word in this question. Can you help?" },
|
|
574
635
|
{ id: "explain_again", label: "Explain this again", message: "Can you explain this question to me again in a different way?" }
|
|
575
636
|
];
|
|
637
|
+
var MicIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
638
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
639
|
+
/* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
640
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
641
|
+
] });
|
|
642
|
+
var MicOffIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
643
|
+
/* @__PURE__ */ jsx2("line", { x1: "2", x2: "22", y1: "2", y2: "22" }),
|
|
644
|
+
/* @__PURE__ */ jsx2("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }),
|
|
645
|
+
/* @__PURE__ */ jsx2("path", { d: "M5 10v2a7 7 0 0 0 12 5" }),
|
|
646
|
+
/* @__PURE__ */ jsx2("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }),
|
|
647
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
|
|
648
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
649
|
+
] });
|
|
650
|
+
var VolumeIcon2 = () => /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
651
|
+
/* @__PURE__ */ jsx2("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
|
|
652
|
+
/* @__PURE__ */ jsx2("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
|
|
653
|
+
/* @__PURE__ */ jsx2("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
|
|
654
|
+
] });
|
|
655
|
+
var SendIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
656
|
+
/* @__PURE__ */ jsx2("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
657
|
+
/* @__PURE__ */ jsx2("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
658
|
+
] });
|
|
659
|
+
var HelpIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "#6721b0", strokeWidth: "2", children: [
|
|
660
|
+
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
|
|
661
|
+
/* @__PURE__ */ jsx2("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
662
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
663
|
+
] });
|
|
576
664
|
function QuestionChatPanel({
|
|
577
665
|
apiClient,
|
|
578
666
|
question,
|
|
@@ -580,20 +668,130 @@ function QuestionChatPanel({
|
|
|
580
668
|
childId,
|
|
581
669
|
parentId,
|
|
582
670
|
lessonId,
|
|
583
|
-
courseId
|
|
671
|
+
courseId,
|
|
672
|
+
answerResult
|
|
584
673
|
}) {
|
|
585
674
|
const [messages, setMessages] = useState2([]);
|
|
586
675
|
const [inputValue, setInputValue] = useState2("");
|
|
587
676
|
const [isLoading, setIsLoading] = useState2(false);
|
|
588
677
|
const [chatId, setChatId] = useState2(null);
|
|
589
678
|
const [hoveredButton, setHoveredButton] = useState2(null);
|
|
679
|
+
const [isListening, setIsListening] = useState2(false);
|
|
680
|
+
const [speakingIndex, setSpeakingIndex] = useState2(null);
|
|
681
|
+
const [audioReadyMap, setAudioReadyMap] = useState2(/* @__PURE__ */ new Map());
|
|
682
|
+
const [hasOfferedHelp, setHasOfferedHelp] = useState2(false);
|
|
590
683
|
const messagesContainerRef = useRef2(null);
|
|
591
684
|
const messagesEndRef = useRef2(null);
|
|
685
|
+
const recognitionRef = useRef2(null);
|
|
686
|
+
const audioRef = useRef2(null);
|
|
687
|
+
const audioCacheRef = useRef2(/* @__PURE__ */ new Map());
|
|
688
|
+
const isSpeechSupported = typeof window !== "undefined" && (window.SpeechRecognition || window.webkitSpeechRecognition);
|
|
592
689
|
const scrollToBottom = useCallback(() => {
|
|
593
690
|
if (messagesContainerRef.current) {
|
|
594
691
|
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
|
|
595
692
|
}
|
|
596
693
|
}, []);
|
|
694
|
+
const preCacheAudio = useCallback(async (text) => {
|
|
695
|
+
if (audioCacheRef.current.has(text)) {
|
|
696
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, false));
|
|
700
|
+
try {
|
|
701
|
+
const audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
702
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
703
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error("Pre-cache TTS error:", error);
|
|
706
|
+
}
|
|
707
|
+
}, [apiClient]);
|
|
708
|
+
const startListening = useCallback(() => {
|
|
709
|
+
const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
710
|
+
if (!SpeechRecognitionAPI) {
|
|
711
|
+
console.log("Speech recognition not supported");
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const recognition = new SpeechRecognitionAPI();
|
|
715
|
+
recognition.continuous = true;
|
|
716
|
+
recognition.interimResults = true;
|
|
717
|
+
recognition.lang = "en-US";
|
|
718
|
+
recognition.onstart = () => {
|
|
719
|
+
console.log("Speech recognition started");
|
|
720
|
+
setIsListening(true);
|
|
721
|
+
};
|
|
722
|
+
recognition.onresult = (event) => {
|
|
723
|
+
let finalTranscript = "";
|
|
724
|
+
for (let i = 0; i < Object.keys(event.results).length; i++) {
|
|
725
|
+
const result = event.results[i];
|
|
726
|
+
if (result && result[0]) {
|
|
727
|
+
finalTranscript += result[0].transcript;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (finalTranscript) {
|
|
731
|
+
setInputValue(finalTranscript);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
recognition.onerror = (event) => {
|
|
735
|
+
console.log("Speech recognition error:", event.error);
|
|
736
|
+
if (event.error === "not-allowed") {
|
|
737
|
+
alert("Microphone access was denied. Please allow microphone access and try again.");
|
|
738
|
+
}
|
|
739
|
+
setIsListening(false);
|
|
740
|
+
};
|
|
741
|
+
recognition.onend = () => {
|
|
742
|
+
console.log("Speech recognition ended");
|
|
743
|
+
setIsListening(false);
|
|
744
|
+
};
|
|
745
|
+
recognitionRef.current = recognition;
|
|
746
|
+
try {
|
|
747
|
+
recognition.start();
|
|
748
|
+
} catch (e) {
|
|
749
|
+
console.log("Failed to start recognition:", e);
|
|
750
|
+
setIsListening(false);
|
|
751
|
+
}
|
|
752
|
+
}, []);
|
|
753
|
+
const stopListening = useCallback(() => {
|
|
754
|
+
if (recognitionRef.current) {
|
|
755
|
+
recognitionRef.current.stop();
|
|
756
|
+
setIsListening(false);
|
|
757
|
+
}
|
|
758
|
+
}, []);
|
|
759
|
+
const speakMessage = useCallback(async (text, index) => {
|
|
760
|
+
if (audioRef.current) {
|
|
761
|
+
audioRef.current.pause();
|
|
762
|
+
audioRef.current = null;
|
|
763
|
+
}
|
|
764
|
+
if (speakingIndex === index) {
|
|
765
|
+
setSpeakingIndex(null);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
setSpeakingIndex(index);
|
|
769
|
+
try {
|
|
770
|
+
let audioBlob;
|
|
771
|
+
const cachedBlob = audioCacheRef.current.get(text);
|
|
772
|
+
if (cachedBlob) {
|
|
773
|
+
audioBlob = cachedBlob;
|
|
774
|
+
} else {
|
|
775
|
+
audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
776
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
777
|
+
}
|
|
778
|
+
const audioUrl = URL.createObjectURL(audioBlob);
|
|
779
|
+
const audio = new Audio(audioUrl);
|
|
780
|
+
audioRef.current = audio;
|
|
781
|
+
audio.onended = () => {
|
|
782
|
+
setSpeakingIndex(null);
|
|
783
|
+
URL.revokeObjectURL(audioUrl);
|
|
784
|
+
};
|
|
785
|
+
audio.onerror = () => {
|
|
786
|
+
setSpeakingIndex(null);
|
|
787
|
+
URL.revokeObjectURL(audioUrl);
|
|
788
|
+
};
|
|
789
|
+
await audio.play();
|
|
790
|
+
} catch (error) {
|
|
791
|
+
console.error("TTS error:", error);
|
|
792
|
+
setSpeakingIndex(null);
|
|
793
|
+
}
|
|
794
|
+
}, [speakingIndex, apiClient]);
|
|
597
795
|
useEffect2(() => {
|
|
598
796
|
scrollToBottom();
|
|
599
797
|
}, [messages, scrollToBottom]);
|
|
@@ -601,19 +799,35 @@ function QuestionChatPanel({
|
|
|
601
799
|
setMessages([]);
|
|
602
800
|
setChatId(null);
|
|
603
801
|
setInputValue("");
|
|
802
|
+
setHasOfferedHelp(false);
|
|
604
803
|
const loadHistory = async () => {
|
|
605
804
|
try {
|
|
606
805
|
const history = await apiClient.getChatHistory(question.id, childId);
|
|
607
806
|
if (history.chatId && history.messages.length > 0) {
|
|
608
807
|
setChatId(history.chatId);
|
|
609
808
|
setMessages(history.messages);
|
|
809
|
+
history.messages.filter((m) => m.role === "assistant").forEach((m) => preCacheAudio(m.content));
|
|
610
810
|
}
|
|
611
811
|
} catch (err) {
|
|
612
812
|
console.error("Failed to load chat history:", err);
|
|
613
813
|
}
|
|
614
814
|
};
|
|
615
815
|
loadHistory();
|
|
616
|
-
}, [question.id, childId, apiClient]);
|
|
816
|
+
}, [question.id, childId, apiClient, preCacheAudio]);
|
|
817
|
+
useEffect2(() => {
|
|
818
|
+
if (answerResult?.wasIncorrect && !hasOfferedHelp) {
|
|
819
|
+
setHasOfferedHelp(true);
|
|
820
|
+
const selectedAnswerText = answerResult.selectedAnswer || "that answer";
|
|
821
|
+
const helpMessage = `Looks like you chose "${selectedAnswerText}" which was incorrect. Would you like me to help explain the correct answer?`;
|
|
822
|
+
const assistantMessage = {
|
|
823
|
+
role: "assistant",
|
|
824
|
+
content: helpMessage,
|
|
825
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
826
|
+
};
|
|
827
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
828
|
+
preCacheAudio(helpMessage);
|
|
829
|
+
}
|
|
830
|
+
}, [answerResult, hasOfferedHelp, preCacheAudio]);
|
|
617
831
|
const initializeChat = async () => {
|
|
618
832
|
if (chatId) return chatId;
|
|
619
833
|
try {
|
|
@@ -635,6 +849,9 @@ function QuestionChatPanel({
|
|
|
635
849
|
};
|
|
636
850
|
const sendMessage = async (messageText) => {
|
|
637
851
|
if (!messageText.trim() || isLoading) return;
|
|
852
|
+
if (isListening) {
|
|
853
|
+
stopListening();
|
|
854
|
+
}
|
|
638
855
|
setIsLoading(true);
|
|
639
856
|
const userMsg = {
|
|
640
857
|
role: "user",
|
|
@@ -654,15 +871,23 @@ function QuestionChatPanel({
|
|
|
654
871
|
questionContext: question,
|
|
655
872
|
childId
|
|
656
873
|
});
|
|
657
|
-
|
|
874
|
+
const assistantMessage = response.assistantMessage || {
|
|
875
|
+
role: "assistant",
|
|
876
|
+
content: "I'm here to help!",
|
|
877
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
878
|
+
};
|
|
879
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
880
|
+
preCacheAudio(assistantMessage.content);
|
|
658
881
|
} catch (err) {
|
|
659
882
|
console.error("Failed to send message:", err);
|
|
883
|
+
const content = "Sorry, I'm having trouble right now. Please try again!";
|
|
660
884
|
const errorMsg = {
|
|
661
885
|
role: "assistant",
|
|
662
|
-
content
|
|
886
|
+
content,
|
|
663
887
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
664
888
|
};
|
|
665
889
|
setMessages((prev) => [...prev, errorMsg]);
|
|
890
|
+
preCacheAudio(content);
|
|
666
891
|
} finally {
|
|
667
892
|
setIsLoading(false);
|
|
668
893
|
}
|
|
@@ -679,14 +904,14 @@ function QuestionChatPanel({
|
|
|
679
904
|
0%, 60%, 100% { transform: translateY(0); }
|
|
680
905
|
30% { transform: translateY(-4px); }
|
|
681
906
|
}
|
|
907
|
+
@keyframes pulse {
|
|
908
|
+
0%, 100% { opacity: 1; }
|
|
909
|
+
50% { opacity: 0.5; }
|
|
910
|
+
}
|
|
682
911
|
` }),
|
|
683
912
|
/* @__PURE__ */ jsx2("div", { style: panelStyles.header, children: /* @__PURE__ */ jsx2("span", { children: "Need Help?" }) }),
|
|
684
913
|
/* @__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__ */
|
|
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
|
-
] }) }),
|
|
914
|
+
/* @__PURE__ */ jsx2("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */ jsx2(HelpIcon, {}) }),
|
|
690
915
|
/* @__PURE__ */ jsx2("div", { style: { fontSize: "14px", fontWeight: "500", marginBottom: "8px" }, children: "Hi! I'm your question helper" }),
|
|
691
916
|
/* @__PURE__ */ jsx2("div", { style: { fontSize: "13px", color: "#9ca3af" }, children: "Ask me if you need help understanding this question" }),
|
|
692
917
|
/* @__PURE__ */ jsx2("div", { style: { ...panelStyles.starterPrompts, marginTop: "16px" }, children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ jsx2(
|
|
@@ -713,7 +938,26 @@ function QuestionChatPanel({
|
|
|
713
938
|
...panelStyles.messageRow,
|
|
714
939
|
justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
|
|
715
940
|
},
|
|
716
|
-
children: /* @__PURE__ */
|
|
941
|
+
children: /* @__PURE__ */ jsxs2("div", { style: {
|
|
942
|
+
...panelStyles.messageContent,
|
|
943
|
+
flexDirection: msg.role === "user" ? "row-reverse" : "row"
|
|
944
|
+
}, children: [
|
|
945
|
+
/* @__PURE__ */ jsx2("div", { style: msg.role === "user" ? panelStyles.userMessage : panelStyles.assistantMessage, children: msg.content }),
|
|
946
|
+
msg.role === "assistant" && /* @__PURE__ */ jsx2(
|
|
947
|
+
"button",
|
|
948
|
+
{
|
|
949
|
+
style: {
|
|
950
|
+
...panelStyles.speakButton,
|
|
951
|
+
...speakingIndex === idx ? panelStyles.speakButtonPlaying : audioReadyMap.get(msg.content) === true ? panelStyles.speakButtonReady : audioReadyMap.get(msg.content) === false ? panelStyles.speakButtonLoading : {},
|
|
952
|
+
...audioReadyMap.get(msg.content) === false ? { animation: "pulse 1.5s ease-in-out infinite" } : {}
|
|
953
|
+
},
|
|
954
|
+
onClick: () => speakMessage(msg.content, idx),
|
|
955
|
+
"data-testid": `button-speak-${idx}`,
|
|
956
|
+
title: "Listen to this message",
|
|
957
|
+
children: /* @__PURE__ */ jsx2(VolumeIcon2, {})
|
|
958
|
+
}
|
|
959
|
+
)
|
|
960
|
+
] })
|
|
717
961
|
},
|
|
718
962
|
idx
|
|
719
963
|
)),
|
|
@@ -732,26 +976,38 @@ function QuestionChatPanel({
|
|
|
732
976
|
value: inputValue,
|
|
733
977
|
onChange: (e) => setInputValue(e.target.value),
|
|
734
978
|
onKeyPress: handleKeyPress,
|
|
735
|
-
placeholder: "Ask about this question...",
|
|
979
|
+
placeholder: isListening ? "Listening..." : "Ask about this question...",
|
|
736
980
|
style: panelStyles.input,
|
|
737
|
-
disabled: isLoading,
|
|
981
|
+
disabled: isLoading || isListening,
|
|
738
982
|
"data-testid": "input-chat-message"
|
|
739
983
|
}
|
|
740
984
|
),
|
|
985
|
+
isSpeechSupported && /* @__PURE__ */ jsx2(
|
|
986
|
+
"button",
|
|
987
|
+
{
|
|
988
|
+
onClick: isListening ? stopListening : startListening,
|
|
989
|
+
disabled: isLoading,
|
|
990
|
+
style: {
|
|
991
|
+
...panelStyles.buttonBase,
|
|
992
|
+
...isListening ? panelStyles.micButtonActive : panelStyles.micButton
|
|
993
|
+
},
|
|
994
|
+
"data-testid": "button-voice-input",
|
|
995
|
+
title: isListening ? "Stop listening" : "Speak your question",
|
|
996
|
+
children: isListening ? /* @__PURE__ */ jsx2(MicOffIcon, {}) : /* @__PURE__ */ jsx2(MicIcon, {})
|
|
997
|
+
}
|
|
998
|
+
),
|
|
741
999
|
/* @__PURE__ */ jsx2(
|
|
742
1000
|
"button",
|
|
743
1001
|
{
|
|
744
1002
|
onClick: () => sendMessage(inputValue),
|
|
745
1003
|
disabled: isLoading || !inputValue.trim(),
|
|
746
1004
|
style: {
|
|
1005
|
+
...panelStyles.buttonBase,
|
|
747
1006
|
...panelStyles.sendButton,
|
|
748
1007
|
...isLoading || !inputValue.trim() ? panelStyles.sendButtonDisabled : {}
|
|
749
1008
|
},
|
|
750
1009
|
"data-testid": "button-send-chat",
|
|
751
|
-
children: /* @__PURE__ */
|
|
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
|
-
] })
|
|
1010
|
+
children: /* @__PURE__ */ jsx2(SendIcon, {})
|
|
755
1011
|
}
|
|
756
1012
|
)
|
|
757
1013
|
] })
|
|
@@ -764,15 +1020,28 @@ var defaultStyles = {
|
|
|
764
1020
|
container: {
|
|
765
1021
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
766
1022
|
width: "100%",
|
|
1023
|
+
height: "100%",
|
|
767
1024
|
padding: "20px",
|
|
768
1025
|
backgroundColor: "#ffffff",
|
|
769
1026
|
borderRadius: "12px",
|
|
770
|
-
boxSizing: "border-box"
|
|
1027
|
+
boxSizing: "border-box",
|
|
1028
|
+
display: "flex",
|
|
1029
|
+
flexDirection: "column"
|
|
771
1030
|
},
|
|
772
1031
|
header: {
|
|
773
1032
|
marginBottom: "20px",
|
|
774
1033
|
borderBottom: "1px solid #e5e7eb",
|
|
775
|
-
paddingBottom: "16px"
|
|
1034
|
+
paddingBottom: "16px",
|
|
1035
|
+
display: "flex",
|
|
1036
|
+
flexDirection: "column"
|
|
1037
|
+
},
|
|
1038
|
+
headerTop: {
|
|
1039
|
+
display: "flex",
|
|
1040
|
+
justifyContent: "space-between",
|
|
1041
|
+
alignItems: "flex-start"
|
|
1042
|
+
},
|
|
1043
|
+
headerLeft: {
|
|
1044
|
+
flex: 1
|
|
776
1045
|
},
|
|
777
1046
|
title: {
|
|
778
1047
|
fontSize: "24px",
|
|
@@ -784,7 +1053,8 @@ var defaultStyles = {
|
|
|
784
1053
|
color: "#6b7280"
|
|
785
1054
|
},
|
|
786
1055
|
progressBar: {
|
|
787
|
-
width: "
|
|
1056
|
+
width: "50%",
|
|
1057
|
+
maxWidth: "300px",
|
|
788
1058
|
height: "8px",
|
|
789
1059
|
backgroundColor: "#e5e7eb",
|
|
790
1060
|
borderRadius: "4px",
|
|
@@ -899,16 +1169,21 @@ var defaultStyles = {
|
|
|
899
1169
|
},
|
|
900
1170
|
mainLayout: {
|
|
901
1171
|
display: "flex",
|
|
902
|
-
gap: "24px"
|
|
1172
|
+
gap: "24px",
|
|
1173
|
+
flex: 1,
|
|
1174
|
+
minHeight: 0,
|
|
1175
|
+
alignItems: "stretch"
|
|
903
1176
|
},
|
|
904
1177
|
quizContent: {
|
|
905
1178
|
flex: 1,
|
|
906
|
-
minWidth: 0
|
|
1179
|
+
minWidth: 0,
|
|
1180
|
+
overflow: "auto"
|
|
907
1181
|
},
|
|
908
1182
|
chatPanel: {
|
|
909
1183
|
width: "320px",
|
|
910
1184
|
flexShrink: 0,
|
|
911
|
-
|
|
1185
|
+
display: "flex",
|
|
1186
|
+
flexDirection: "column"
|
|
912
1187
|
},
|
|
913
1188
|
timer: {
|
|
914
1189
|
fontSize: "14px",
|
|
@@ -1116,6 +1391,16 @@ function QuizPlayer({
|
|
|
1116
1391
|
const apiClient = useRef3(null);
|
|
1117
1392
|
const timerRef = useRef3(null);
|
|
1118
1393
|
const startTimeRef = useRef3(0);
|
|
1394
|
+
const onCompleteRef = useRef3(onComplete);
|
|
1395
|
+
const onErrorRef = useRef3(onError);
|
|
1396
|
+
const onProgressRef = useRef3(onProgress);
|
|
1397
|
+
const onGenerateMoreQuestionsRef = useRef3(onGenerateMoreQuestions);
|
|
1398
|
+
useEffect3(() => {
|
|
1399
|
+
onCompleteRef.current = onComplete;
|
|
1400
|
+
onErrorRef.current = onError;
|
|
1401
|
+
onProgressRef.current = onProgress;
|
|
1402
|
+
onGenerateMoreQuestionsRef.current = onGenerateMoreQuestions;
|
|
1403
|
+
});
|
|
1119
1404
|
useEffect3(() => {
|
|
1120
1405
|
apiClient.current = new QuizApiClient({ baseUrl: apiBaseUrl, authToken });
|
|
1121
1406
|
}, [apiBaseUrl, authToken]);
|
|
@@ -1162,11 +1447,11 @@ function QuizPlayer({
|
|
|
1162
1447
|
const message = err instanceof Error ? err.message : "Failed to load quiz";
|
|
1163
1448
|
setError(message);
|
|
1164
1449
|
setIsLoading(false);
|
|
1165
|
-
|
|
1450
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error(message));
|
|
1166
1451
|
}
|
|
1167
1452
|
}
|
|
1168
1453
|
initialize();
|
|
1169
|
-
}, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt
|
|
1454
|
+
}, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt]);
|
|
1170
1455
|
useEffect3(() => {
|
|
1171
1456
|
if (timerStarted && !isCompleted && !error) {
|
|
1172
1457
|
startTimeRef.current = Date.now();
|
|
@@ -1192,14 +1477,14 @@ function QuizPlayer({
|
|
|
1192
1477
|
const totalQuestions = allQuestions.length;
|
|
1193
1478
|
const maxQuestions = 50;
|
|
1194
1479
|
useEffect3(() => {
|
|
1195
|
-
if (quiz &&
|
|
1196
|
-
|
|
1480
|
+
if (quiz && onProgressRef.current) {
|
|
1481
|
+
onProgressRef.current({
|
|
1197
1482
|
currentQuestion: currentQuestionIndex + 1,
|
|
1198
1483
|
totalQuestions,
|
|
1199
1484
|
answeredQuestions: answers.size
|
|
1200
1485
|
});
|
|
1201
1486
|
}
|
|
1202
|
-
}, [currentQuestionIndex, answers.size, quiz,
|
|
1487
|
+
}, [currentQuestionIndex, answers.size, quiz, totalQuestions]);
|
|
1203
1488
|
const currentQuestion = allQuestions[currentQuestionIndex];
|
|
1204
1489
|
const handleAnswerChange = useCallback2((value) => {
|
|
1205
1490
|
if (!currentQuestion) return;
|
|
@@ -1244,7 +1529,7 @@ function QuizPlayer({
|
|
|
1244
1529
|
if (totalQuestions >= maxQuestions) return;
|
|
1245
1530
|
setIsGeneratingExtra(true);
|
|
1246
1531
|
try {
|
|
1247
|
-
const result2 = await
|
|
1532
|
+
const result2 = await onGenerateMoreQuestionsRef.current(attempt.id, totalQuestions);
|
|
1248
1533
|
if (result2.extraQuestions && result2.extraQuestions.length > 0) {
|
|
1249
1534
|
const slotsAvailable = maxQuestions - totalQuestions;
|
|
1250
1535
|
const questionsToAppend = result2.extraQuestions.slice(0, slotsAvailable);
|
|
@@ -1257,11 +1542,11 @@ function QuizPlayer({
|
|
|
1257
1542
|
}
|
|
1258
1543
|
} catch (err) {
|
|
1259
1544
|
console.error("Failed to generate extra questions:", err);
|
|
1260
|
-
|
|
1545
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error("Failed to generate extra questions"));
|
|
1261
1546
|
} finally {
|
|
1262
1547
|
setIsGeneratingExtra(false);
|
|
1263
1548
|
}
|
|
1264
|
-
}, [attempt,
|
|
1549
|
+
}, [attempt, isGeneratingExtra, totalQuestions, maxQuestions]);
|
|
1265
1550
|
const handleSubmit = useCallback2(async () => {
|
|
1266
1551
|
if (!quiz || !attempt || !apiClient.current) return;
|
|
1267
1552
|
setIsSubmitting(true);
|
|
@@ -1300,15 +1585,15 @@ function QuizPlayer({
|
|
|
1300
1585
|
if (timerRef.current) {
|
|
1301
1586
|
clearInterval(timerRef.current);
|
|
1302
1587
|
}
|
|
1303
|
-
|
|
1588
|
+
onCompleteRef.current?.(quizResult);
|
|
1304
1589
|
} catch (err) {
|
|
1305
1590
|
const message = err instanceof Error ? err.message : "Failed to submit quiz";
|
|
1306
1591
|
setError(message);
|
|
1307
|
-
|
|
1592
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error(message));
|
|
1308
1593
|
} finally {
|
|
1309
1594
|
setIsSubmitting(false);
|
|
1310
1595
|
}
|
|
1311
|
-
}, [quiz, attempt, currentQuestion, answers, answersDetail,
|
|
1596
|
+
}, [quiz, attempt, currentQuestion, answers, answersDetail, totalQuestions, timerStarted, elapsedSeconds]);
|
|
1312
1597
|
const isExtraQuestion = currentQuestion && extraQuestions.some((q) => q.id === currentQuestion.id);
|
|
1313
1598
|
const handleSkipQuestion = useCallback2(async (reason, comment) => {
|
|
1314
1599
|
if (!currentQuestion || !apiClient.current || !attempt) return;
|
|
@@ -1344,7 +1629,7 @@ function QuizPlayer({
|
|
|
1344
1629
|
timeSpentSeconds: elapsedSeconds
|
|
1345
1630
|
};
|
|
1346
1631
|
setResult(quizResult);
|
|
1347
|
-
|
|
1632
|
+
onCompleteRef.current?.(quizResult);
|
|
1348
1633
|
} else if (currentQuestionIndex >= newTotalQuestions) {
|
|
1349
1634
|
setCurrentQuestionIndex(newTotalQuestions - 1);
|
|
1350
1635
|
}
|
|
@@ -1356,7 +1641,7 @@ function QuizPlayer({
|
|
|
1356
1641
|
} finally {
|
|
1357
1642
|
setIsSkipping(false);
|
|
1358
1643
|
}
|
|
1359
|
-
}, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId, skippedQuestionIds, extraQuestions, currentQuestionIndex, elapsedSeconds
|
|
1644
|
+
}, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId, skippedQuestionIds, extraQuestions, currentQuestionIndex, elapsedSeconds]);
|
|
1360
1645
|
const handleReportQuestion = useCallback2(async (comment) => {
|
|
1361
1646
|
if (!currentQuestion || !apiClient.current || !attempt || !comment.trim()) return;
|
|
1362
1647
|
setIsReporting(true);
|
|
@@ -1726,533 +2011,537 @@ function QuizPlayer({
|
|
|
1726
2011
|
const remainingSlots = maxQuestions - totalQuestions;
|
|
1727
2012
|
const questionsToAdd = Math.min(5, remainingSlots);
|
|
1728
2013
|
const canAddMore = onGenerateMoreQuestions && remainingSlots > 0;
|
|
1729
|
-
return /* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2014
|
+
return /* @__PURE__ */ jsx3("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ jsxs3("div", { style: defaultStyles.mainLayout, children: [
|
|
2015
|
+
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.quizContent, children: [
|
|
2016
|
+
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.header, children: [
|
|
2017
|
+
/* @__PURE__ */ jsx3("div", { style: defaultStyles.title, children: quiz.title }),
|
|
2018
|
+
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.progress, children: [
|
|
2019
|
+
"Question ",
|
|
2020
|
+
currentQuestionIndex + 1,
|
|
2021
|
+
" of ",
|
|
2022
|
+
totalQuestions
|
|
2023
|
+
] }),
|
|
2024
|
+
/* @__PURE__ */ jsx3("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ jsx3("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
|
|
1737
2025
|
] }),
|
|
1738
|
-
/* @__PURE__ */
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2026
|
+
/* @__PURE__ */ jsxs3("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
|
|
2027
|
+
/* @__PURE__ */ jsx3("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ jsx3(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
|
|
2028
|
+
isExtraQuestion && /* @__PURE__ */ jsxs3(
|
|
2029
|
+
"button",
|
|
2030
|
+
{
|
|
2031
|
+
onClick: () => setShowSkipModal(true),
|
|
2032
|
+
title: "Skip question",
|
|
2033
|
+
style: {
|
|
2034
|
+
position: "absolute",
|
|
2035
|
+
bottom: "8px",
|
|
2036
|
+
left: "0",
|
|
2037
|
+
background: "transparent",
|
|
2038
|
+
border: "none",
|
|
2039
|
+
cursor: "pointer",
|
|
2040
|
+
padding: "6px 10px",
|
|
2041
|
+
borderRadius: "6px",
|
|
2042
|
+
color: "#9ca3af",
|
|
2043
|
+
display: "flex",
|
|
2044
|
+
alignItems: "center",
|
|
2045
|
+
justifyContent: "center",
|
|
2046
|
+
gap: "4px",
|
|
2047
|
+
fontSize: "12px",
|
|
2048
|
+
opacity: 0.6,
|
|
2049
|
+
transition: "opacity 0.2s, color 0.2s"
|
|
2050
|
+
},
|
|
2051
|
+
onMouseEnter: (e) => {
|
|
2052
|
+
e.currentTarget.style.opacity = "1";
|
|
2053
|
+
e.currentTarget.style.color = "#6b7280";
|
|
2054
|
+
},
|
|
2055
|
+
onMouseLeave: (e) => {
|
|
2056
|
+
e.currentTarget.style.opacity = "0.6";
|
|
2057
|
+
e.currentTarget.style.color = "#9ca3af";
|
|
2058
|
+
},
|
|
2059
|
+
"data-testid": "button-skip-question",
|
|
2060
|
+
children: [
|
|
2061
|
+
/* @__PURE__ */ jsxs3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2062
|
+
/* @__PURE__ */ jsx3("polygon", { points: "5 4 15 12 5 20 5 4" }),
|
|
2063
|
+
/* @__PURE__ */ jsx3("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
|
|
2064
|
+
] }),
|
|
2065
|
+
/* @__PURE__ */ jsx3("span", { children: "Skip" })
|
|
2066
|
+
]
|
|
2067
|
+
}
|
|
2068
|
+
),
|
|
2069
|
+
!isExtraQuestion && /* @__PURE__ */ jsxs3(
|
|
2070
|
+
"button",
|
|
2071
|
+
{
|
|
2072
|
+
onClick: () => setShowReportModal(true),
|
|
2073
|
+
title: "Report an issue with this question",
|
|
2074
|
+
style: {
|
|
2075
|
+
position: "absolute",
|
|
2076
|
+
bottom: "8px",
|
|
2077
|
+
left: "0",
|
|
2078
|
+
background: "transparent",
|
|
2079
|
+
border: "none",
|
|
2080
|
+
cursor: "pointer",
|
|
2081
|
+
padding: "6px 10px",
|
|
2082
|
+
borderRadius: "6px",
|
|
2083
|
+
color: "#9ca3af",
|
|
2084
|
+
display: "flex",
|
|
2085
|
+
alignItems: "center",
|
|
2086
|
+
justifyContent: "center",
|
|
2087
|
+
gap: "4px",
|
|
2088
|
+
fontSize: "12px",
|
|
2089
|
+
opacity: 0.6,
|
|
2090
|
+
transition: "opacity 0.2s, color 0.2s"
|
|
2091
|
+
},
|
|
2092
|
+
onMouseEnter: (e) => {
|
|
2093
|
+
e.currentTarget.style.opacity = "1";
|
|
2094
|
+
e.currentTarget.style.color = "#ef4444";
|
|
2095
|
+
},
|
|
2096
|
+
onMouseLeave: (e) => {
|
|
2097
|
+
e.currentTarget.style.opacity = "0.6";
|
|
2098
|
+
e.currentTarget.style.color = "#9ca3af";
|
|
2099
|
+
},
|
|
2100
|
+
"data-testid": "button-report-question",
|
|
2101
|
+
children: [
|
|
2102
|
+
/* @__PURE__ */ jsxs3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2103
|
+
/* @__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" }),
|
|
2104
|
+
/* @__PURE__ */ jsx3("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
|
|
2105
|
+
] }),
|
|
2106
|
+
/* @__PURE__ */ jsx3("span", { children: "Report" })
|
|
2107
|
+
]
|
|
2108
|
+
}
|
|
2109
|
+
),
|
|
2110
|
+
(currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
|
|
2111
|
+
const isSelected = selectedAnswer === option;
|
|
2112
|
+
const isCorrectOption = currentQuestion.correctAnswer === option;
|
|
2113
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2114
|
+
if (showFeedback) {
|
|
2115
|
+
if (isCorrectOption) {
|
|
2116
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
|
|
2117
|
+
} else if (isSelected && !isCorrectOption) {
|
|
2118
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
|
|
2119
|
+
}
|
|
2120
|
+
} else if (isSelected) {
|
|
2121
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2122
|
+
}
|
|
2123
|
+
return /* @__PURE__ */ jsxs3(
|
|
2124
|
+
"div",
|
|
1746
2125
|
{
|
|
1747
|
-
onClick: () => setShowSkipModal(true),
|
|
1748
|
-
title: "Skip question",
|
|
1749
2126
|
style: {
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
left: "0",
|
|
1753
|
-
background: "transparent",
|
|
1754
|
-
border: "none",
|
|
1755
|
-
cursor: "pointer",
|
|
1756
|
-
padding: "6px 10px",
|
|
1757
|
-
borderRadius: "6px",
|
|
1758
|
-
color: "#9ca3af",
|
|
2127
|
+
...optionStyle,
|
|
2128
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
1759
2129
|
display: "flex",
|
|
1760
2130
|
alignItems: "center",
|
|
1761
|
-
|
|
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";
|
|
2131
|
+
gap: "8px"
|
|
1770
2132
|
},
|
|
1771
|
-
|
|
1772
|
-
e.currentTarget.style.opacity = "0.6";
|
|
1773
|
-
e.currentTarget.style.color = "#9ca3af";
|
|
1774
|
-
},
|
|
1775
|
-
"data-testid": "button-skip-question",
|
|
2133
|
+
onClick: () => !showFeedback && handleAnswerChange(option),
|
|
1776
2134
|
children: [
|
|
1777
|
-
/* @__PURE__ */
|
|
1778
|
-
|
|
1779
|
-
/* @__PURE__ */ jsx3("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
|
|
1780
|
-
] }),
|
|
1781
|
-
/* @__PURE__ */ jsx3("span", { children: "Skip" })
|
|
2135
|
+
/* @__PURE__ */ jsx3("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx3(TextToSpeech, { text: option, size: "sm" }) }),
|
|
2136
|
+
/* @__PURE__ */ jsx3("span", { style: { flex: 1 }, children: option })
|
|
1782
2137
|
]
|
|
2138
|
+
},
|
|
2139
|
+
idx
|
|
2140
|
+
);
|
|
2141
|
+
}) }),
|
|
2142
|
+
currentQuestion.type === "multiple" && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
|
|
2143
|
+
const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
|
|
2144
|
+
const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
|
|
2145
|
+
const isCorrectOption = correctAnswers.includes(option);
|
|
2146
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2147
|
+
if (showFeedback) {
|
|
2148
|
+
if (isCorrectOption) {
|
|
2149
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
|
|
2150
|
+
} else if (selected && !isCorrectOption) {
|
|
2151
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
|
|
1783
2152
|
}
|
|
1784
|
-
)
|
|
1785
|
-
|
|
1786
|
-
|
|
2153
|
+
} else if (selected) {
|
|
2154
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2155
|
+
}
|
|
2156
|
+
return /* @__PURE__ */ jsxs3(
|
|
2157
|
+
"div",
|
|
1787
2158
|
{
|
|
1788
|
-
onClick: () => setShowReportModal(true),
|
|
1789
|
-
title: "Report an issue with this question",
|
|
1790
2159
|
style: {
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
left: "0",
|
|
1794
|
-
background: "transparent",
|
|
1795
|
-
border: "none",
|
|
1796
|
-
cursor: "pointer",
|
|
1797
|
-
padding: "6px 10px",
|
|
1798
|
-
borderRadius: "6px",
|
|
1799
|
-
color: "#9ca3af",
|
|
2160
|
+
...optionStyle,
|
|
2161
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
1800
2162
|
display: "flex",
|
|
1801
2163
|
alignItems: "center",
|
|
1802
|
-
|
|
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";
|
|
2164
|
+
gap: "8px"
|
|
1811
2165
|
},
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2166
|
+
onClick: () => {
|
|
2167
|
+
if (showFeedback) return;
|
|
2168
|
+
const current = Array.isArray(selectedAnswer) ? selectedAnswer : [];
|
|
2169
|
+
if (selected) {
|
|
2170
|
+
handleAnswerChange(current.filter((o) => o !== option));
|
|
2171
|
+
} else {
|
|
2172
|
+
handleAnswerChange([...current, option]);
|
|
2173
|
+
}
|
|
1815
2174
|
},
|
|
1816
|
-
"data-testid": "button-report-question",
|
|
1817
2175
|
children: [
|
|
1818
|
-
/* @__PURE__ */
|
|
1819
|
-
|
|
1820
|
-
/* @__PURE__ */ jsx3("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
|
|
1821
|
-
] }),
|
|
1822
|
-
/* @__PURE__ */ jsx3("span", { children: "Report" })
|
|
2176
|
+
/* @__PURE__ */ jsx3("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx3(TextToSpeech, { text: option, size: "sm" }) }),
|
|
2177
|
+
/* @__PURE__ */ jsx3("span", { style: { flex: 1 }, children: option })
|
|
1823
2178
|
]
|
|
2179
|
+
},
|
|
2180
|
+
idx
|
|
2181
|
+
);
|
|
2182
|
+
}) }),
|
|
2183
|
+
(currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ jsx3(
|
|
2184
|
+
"textarea",
|
|
2185
|
+
{
|
|
2186
|
+
style: { ...defaultStyles.input, minHeight: currentQuestion.type === "essay" ? "150px" : "60px" },
|
|
2187
|
+
value: selectedAnswer || "",
|
|
2188
|
+
onChange: (e) => handleAnswerChange(e.target.value),
|
|
2189
|
+
placeholder: "Type your answer here...",
|
|
2190
|
+
disabled: showFeedback
|
|
2191
|
+
}
|
|
2192
|
+
),
|
|
2193
|
+
currentQuestion.type === "fill" && /* @__PURE__ */ jsx3("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ jsx3(
|
|
2194
|
+
"input",
|
|
2195
|
+
{
|
|
2196
|
+
style: defaultStyles.input,
|
|
2197
|
+
value: (Array.isArray(selectedAnswer) ? selectedAnswer[idx] : "") || "",
|
|
2198
|
+
onChange: (e) => {
|
|
2199
|
+
const current = Array.isArray(selectedAnswer) ? [...selectedAnswer] : [];
|
|
2200
|
+
current[idx] = e.target.value;
|
|
2201
|
+
handleAnswerChange(current);
|
|
2202
|
+
},
|
|
2203
|
+
placeholder: `Blank ${idx + 1}`,
|
|
2204
|
+
disabled: showFeedback
|
|
2205
|
+
},
|
|
2206
|
+
idx
|
|
2207
|
+
)) }),
|
|
2208
|
+
showFeedback && currentAnswerDetail && /* @__PURE__ */ jsxs3("div", { style: {
|
|
2209
|
+
...defaultStyles.feedback,
|
|
2210
|
+
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
|
|
2211
|
+
}, children: [
|
|
2212
|
+
/* @__PURE__ */ jsx3("div", { style: {
|
|
2213
|
+
...defaultStyles.feedbackTitle,
|
|
2214
|
+
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
|
|
2215
|
+
}, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
|
|
2216
|
+
currentQuestion.explanation && /* @__PURE__ */ jsx3("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
|
|
2217
|
+
] })
|
|
2218
|
+
] }),
|
|
2219
|
+
showSkipModal && /* @__PURE__ */ jsx3("div", { style: {
|
|
2220
|
+
position: "fixed",
|
|
2221
|
+
top: 0,
|
|
2222
|
+
left: 0,
|
|
2223
|
+
right: 0,
|
|
2224
|
+
bottom: 0,
|
|
2225
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2226
|
+
display: "flex",
|
|
2227
|
+
alignItems: "center",
|
|
2228
|
+
justifyContent: "center",
|
|
2229
|
+
zIndex: 1e3
|
|
2230
|
+
}, children: /* @__PURE__ */ jsxs3("div", { style: {
|
|
2231
|
+
backgroundColor: "#ffffff",
|
|
2232
|
+
borderRadius: "12px",
|
|
2233
|
+
padding: "24px",
|
|
2234
|
+
maxWidth: "400px",
|
|
2235
|
+
width: "90%",
|
|
2236
|
+
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
2237
|
+
}, children: [
|
|
2238
|
+
/* @__PURE__ */ jsx3("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
|
|
2239
|
+
/* @__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." }),
|
|
2240
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
|
|
2241
|
+
/* @__PURE__ */ jsx3(
|
|
2242
|
+
"button",
|
|
2243
|
+
{
|
|
2244
|
+
onClick: () => setSelectedSkipReason("question_issue"),
|
|
2245
|
+
disabled: isSkipping,
|
|
2246
|
+
style: {
|
|
2247
|
+
padding: "12px 16px",
|
|
2248
|
+
borderRadius: "8px",
|
|
2249
|
+
border: selectedSkipReason === "question_issue" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
2250
|
+
backgroundColor: selectedSkipReason === "question_issue" ? "#f5f3ff" : "#f9fafb",
|
|
2251
|
+
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2252
|
+
fontSize: "14px",
|
|
2253
|
+
fontWeight: "500",
|
|
2254
|
+
color: "#374151",
|
|
2255
|
+
textAlign: "left",
|
|
2256
|
+
opacity: isSkipping ? 0.6 : 1
|
|
2257
|
+
},
|
|
2258
|
+
"data-testid": "button-skip-reason-issue",
|
|
2259
|
+
children: "Question has an issue"
|
|
1824
2260
|
}
|
|
1825
2261
|
),
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
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
|
-
]
|
|
2262
|
+
/* @__PURE__ */ jsx3(
|
|
2263
|
+
"button",
|
|
2264
|
+
{
|
|
2265
|
+
onClick: () => setSelectedSkipReason("dont_know"),
|
|
2266
|
+
disabled: isSkipping,
|
|
2267
|
+
style: {
|
|
2268
|
+
padding: "12px 16px",
|
|
2269
|
+
borderRadius: "8px",
|
|
2270
|
+
border: selectedSkipReason === "dont_know" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
2271
|
+
backgroundColor: selectedSkipReason === "dont_know" ? "#f5f3ff" : "#f9fafb",
|
|
2272
|
+
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2273
|
+
fontSize: "14px",
|
|
2274
|
+
fontWeight: "500",
|
|
2275
|
+
color: "#374151",
|
|
2276
|
+
textAlign: "left",
|
|
2277
|
+
opacity: isSkipping ? 0.6 : 1
|
|
1854
2278
|
},
|
|
1855
|
-
|
|
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 };
|
|
2279
|
+
"data-testid": "button-skip-reason-dont-know",
|
|
2280
|
+
children: "I don't know the answer"
|
|
1871
2281
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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(
|
|
2282
|
+
)
|
|
2283
|
+
] }),
|
|
2284
|
+
/* @__PURE__ */ jsxs3("div", { style: { marginBottom: "16px" }, children: [
|
|
2285
|
+
/* @__PURE__ */ jsx3("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
|
|
2286
|
+
/* @__PURE__ */ jsx3(
|
|
1900
2287
|
"textarea",
|
|
1901
2288
|
{
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2289
|
+
value: skipComment,
|
|
2290
|
+
onChange: (e) => setSkipComment(e.target.value.slice(0, 200)),
|
|
2291
|
+
placeholder: "Tell us more about the issue...",
|
|
2292
|
+
disabled: isSkipping,
|
|
2293
|
+
style: {
|
|
2294
|
+
width: "100%",
|
|
2295
|
+
minHeight: "80px",
|
|
2296
|
+
padding: "10px 12px",
|
|
2297
|
+
borderRadius: "8px",
|
|
2298
|
+
border: "1px solid #e5e7eb",
|
|
2299
|
+
fontSize: "14px",
|
|
2300
|
+
resize: "vertical",
|
|
2301
|
+
fontFamily: "inherit",
|
|
2302
|
+
boxSizing: "border-box"
|
|
2303
|
+
},
|
|
2304
|
+
"data-testid": "input-skip-comment"
|
|
1907
2305
|
}
|
|
1908
2306
|
),
|
|
1909
|
-
|
|
1910
|
-
|
|
2307
|
+
/* @__PURE__ */ jsxs3("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2308
|
+
skipComment.length,
|
|
2309
|
+
"/200"
|
|
2310
|
+
] })
|
|
2311
|
+
] }),
|
|
2312
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2313
|
+
/* @__PURE__ */ jsx3(
|
|
2314
|
+
"button",
|
|
1911
2315
|
{
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
current[idx] = e.target.value;
|
|
1917
|
-
handleAnswerChange(current);
|
|
2316
|
+
onClick: () => {
|
|
2317
|
+
setShowSkipModal(false);
|
|
2318
|
+
setSkipComment("");
|
|
2319
|
+
setSelectedSkipReason(null);
|
|
1918
2320
|
},
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2321
|
+
style: {
|
|
2322
|
+
flex: 1,
|
|
2323
|
+
padding: "10px 16px",
|
|
2324
|
+
borderRadius: "8px",
|
|
2325
|
+
border: "1px solid #e5e7eb",
|
|
2326
|
+
backgroundColor: "#ffffff",
|
|
2327
|
+
cursor: "pointer",
|
|
2328
|
+
fontSize: "14px",
|
|
2329
|
+
fontWeight: "500",
|
|
2330
|
+
color: "#6b7280"
|
|
2331
|
+
},
|
|
2332
|
+
"data-testid": "button-skip-cancel",
|
|
2333
|
+
children: "Cancel"
|
|
2334
|
+
}
|
|
2335
|
+
),
|
|
2336
|
+
/* @__PURE__ */ jsx3(
|
|
2337
|
+
"button",
|
|
2338
|
+
{
|
|
2339
|
+
onClick: () => selectedSkipReason && handleSkipQuestion(selectedSkipReason, skipComment),
|
|
2340
|
+
disabled: isSkipping || !selectedSkipReason,
|
|
2341
|
+
style: {
|
|
2342
|
+
flex: 1,
|
|
2343
|
+
padding: "10px 16px",
|
|
2344
|
+
borderRadius: "8px",
|
|
2345
|
+
border: "none",
|
|
2346
|
+
backgroundColor: selectedSkipReason ? "#8b5cf6" : "#d1d5db",
|
|
2347
|
+
cursor: isSkipping || !selectedSkipReason ? "not-allowed" : "pointer",
|
|
2348
|
+
fontSize: "14px",
|
|
2349
|
+
fontWeight: "500",
|
|
2350
|
+
color: "#ffffff",
|
|
2351
|
+
opacity: isSkipping ? 0.6 : 1
|
|
2352
|
+
},
|
|
2353
|
+
"data-testid": "button-skip-submit",
|
|
2354
|
+
children: isSkipping ? "Skipping..." : "Skip Question"
|
|
2355
|
+
}
|
|
2356
|
+
)
|
|
2357
|
+
] })
|
|
2358
|
+
] }) }),
|
|
2359
|
+
showReportModal && /* @__PURE__ */ jsx3("div", { style: {
|
|
2360
|
+
position: "fixed",
|
|
2361
|
+
top: 0,
|
|
2362
|
+
left: 0,
|
|
2363
|
+
right: 0,
|
|
2364
|
+
bottom: 0,
|
|
2365
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2366
|
+
display: "flex",
|
|
2367
|
+
alignItems: "center",
|
|
2368
|
+
justifyContent: "center",
|
|
2369
|
+
zIndex: 1e3
|
|
2370
|
+
}, children: /* @__PURE__ */ jsxs3("div", { style: {
|
|
2371
|
+
backgroundColor: "#ffffff",
|
|
2372
|
+
borderRadius: "12px",
|
|
2373
|
+
padding: "24px",
|
|
2374
|
+
maxWidth: "400px",
|
|
2375
|
+
width: "90%",
|
|
2376
|
+
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
2377
|
+
}, children: [
|
|
2378
|
+
/* @__PURE__ */ jsx3("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
|
|
2379
|
+
/* @__PURE__ */ jsx3("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
|
|
2380
|
+
/* @__PURE__ */ jsxs3("div", { style: { marginBottom: "16px" }, children: [
|
|
2381
|
+
/* @__PURE__ */ jsx3(
|
|
2382
|
+
"textarea",
|
|
2383
|
+
{
|
|
2384
|
+
value: reportComment,
|
|
2385
|
+
onChange: (e) => setReportComment(e.target.value.slice(0, 300)),
|
|
2386
|
+
placeholder: "Describe the issue with this question...",
|
|
2387
|
+
disabled: isReporting,
|
|
2388
|
+
style: {
|
|
2389
|
+
width: "100%",
|
|
2390
|
+
minHeight: "120px",
|
|
2391
|
+
padding: "10px 12px",
|
|
2392
|
+
borderRadius: "8px",
|
|
2393
|
+
border: "1px solid #e5e7eb",
|
|
2394
|
+
fontSize: "14px",
|
|
2395
|
+
resize: "vertical",
|
|
2396
|
+
fontFamily: "inherit",
|
|
2397
|
+
boxSizing: "border-box"
|
|
2398
|
+
},
|
|
2399
|
+
"data-testid": "input-report-comment"
|
|
2400
|
+
}
|
|
2401
|
+
),
|
|
2402
|
+
/* @__PURE__ */ jsxs3("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2403
|
+
reportComment.length,
|
|
2404
|
+
"/300"
|
|
1933
2405
|
] })
|
|
1934
2406
|
] }),
|
|
1935
|
-
|
|
1936
|
-
|
|
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(
|
|
2407
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2408
|
+
/* @__PURE__ */ jsx3(
|
|
2171
2409
|
"button",
|
|
2172
2410
|
{
|
|
2411
|
+
onClick: () => {
|
|
2412
|
+
setShowReportModal(false);
|
|
2413
|
+
setReportComment("");
|
|
2414
|
+
},
|
|
2173
2415
|
style: {
|
|
2174
|
-
|
|
2175
|
-
|
|
2416
|
+
flex: 1,
|
|
2417
|
+
padding: "10px 16px",
|
|
2418
|
+
borderRadius: "8px",
|
|
2419
|
+
border: "1px solid #e5e7eb",
|
|
2420
|
+
backgroundColor: "#ffffff",
|
|
2421
|
+
cursor: "pointer",
|
|
2422
|
+
fontSize: "14px",
|
|
2423
|
+
fontWeight: "500",
|
|
2424
|
+
color: "#6b7280"
|
|
2176
2425
|
},
|
|
2177
|
-
|
|
2178
|
-
|
|
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
|
-
] })
|
|
2426
|
+
"data-testid": "button-report-cancel",
|
|
2427
|
+
children: "Cancel"
|
|
2189
2428
|
}
|
|
2190
2429
|
),
|
|
2191
|
-
/* @__PURE__ */ jsx3(
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
"
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
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
|
-
) })
|
|
2430
|
+
/* @__PURE__ */ jsx3(
|
|
2431
|
+
"button",
|
|
2432
|
+
{
|
|
2433
|
+
onClick: () => handleReportQuestion(reportComment),
|
|
2434
|
+
disabled: isReporting || !reportComment.trim(),
|
|
2435
|
+
style: {
|
|
2436
|
+
flex: 1,
|
|
2437
|
+
padding: "10px 16px",
|
|
2438
|
+
borderRadius: "8px",
|
|
2439
|
+
border: "none",
|
|
2440
|
+
backgroundColor: reportComment.trim() ? "#ef4444" : "#d1d5db",
|
|
2441
|
+
cursor: isReporting || !reportComment.trim() ? "not-allowed" : "pointer",
|
|
2442
|
+
fontSize: "14px",
|
|
2443
|
+
fontWeight: "500",
|
|
2444
|
+
color: "#ffffff",
|
|
2445
|
+
opacity: isReporting ? 0.6 : 1
|
|
2446
|
+
},
|
|
2447
|
+
"data-testid": "button-report-submit",
|
|
2448
|
+
children: isReporting ? "Reporting..." : "Report"
|
|
2449
|
+
}
|
|
2450
|
+
)
|
|
2233
2451
|
] })
|
|
2234
|
-
] }),
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2452
|
+
] }) }),
|
|
2453
|
+
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.buttonsColumn, children: [
|
|
2454
|
+
showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ jsx3(
|
|
2455
|
+
"button",
|
|
2456
|
+
{
|
|
2457
|
+
style: {
|
|
2458
|
+
...defaultStyles.buttonAddMore,
|
|
2459
|
+
...isGeneratingExtra ? defaultStyles.buttonAddMoreDisabled : {}
|
|
2460
|
+
},
|
|
2461
|
+
onClick: handleAddMoreQuestions,
|
|
2462
|
+
disabled: isGeneratingExtra,
|
|
2463
|
+
"data-testid": "button-add-more-questions",
|
|
2464
|
+
children: isGeneratingExtra ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
2465
|
+
/* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }),
|
|
2466
|
+
"Generating Questions..."
|
|
2467
|
+
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
2468
|
+
"+ Add ",
|
|
2469
|
+
questionsToAdd,
|
|
2470
|
+
" More Question",
|
|
2471
|
+
questionsToAdd !== 1 ? "s" : ""
|
|
2472
|
+
] })
|
|
2473
|
+
}
|
|
2474
|
+
),
|
|
2475
|
+
/* @__PURE__ */ jsx3("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
|
|
2476
|
+
// After viewing feedback
|
|
2477
|
+
isLastQuestion ? /* @__PURE__ */ jsx3(
|
|
2478
|
+
"button",
|
|
2479
|
+
{
|
|
2480
|
+
style: {
|
|
2481
|
+
...defaultStyles.button,
|
|
2482
|
+
...isSubmitting || isGeneratingExtra ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
|
|
2483
|
+
},
|
|
2484
|
+
onClick: handleSubmit,
|
|
2485
|
+
disabled: isSubmitting || isGeneratingExtra,
|
|
2486
|
+
"data-testid": "button-submit-quiz",
|
|
2487
|
+
children: isSubmitting ? /* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
|
|
2488
|
+
}
|
|
2489
|
+
) : /* @__PURE__ */ jsx3(
|
|
2490
|
+
"button",
|
|
2491
|
+
{
|
|
2492
|
+
style: {
|
|
2493
|
+
...defaultStyles.button,
|
|
2494
|
+
...defaultStyles.buttonPrimary
|
|
2495
|
+
},
|
|
2496
|
+
onClick: handleContinue,
|
|
2497
|
+
"data-testid": "button-continue",
|
|
2498
|
+
children: "Continue"
|
|
2499
|
+
}
|
|
2500
|
+
)
|
|
2501
|
+
) : (
|
|
2502
|
+
// Before checking answer
|
|
2503
|
+
/* @__PURE__ */ jsx3(
|
|
2504
|
+
"button",
|
|
2505
|
+
{
|
|
2506
|
+
style: {
|
|
2507
|
+
...defaultStyles.button,
|
|
2508
|
+
...isNavigating || selectedAnswer === void 0 ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
|
|
2509
|
+
},
|
|
2510
|
+
onClick: handleCheckAnswer,
|
|
2511
|
+
disabled: isNavigating || selectedAnswer === void 0,
|
|
2512
|
+
"data-testid": "button-check-answer",
|
|
2513
|
+
children: isNavigating ? /* @__PURE__ */ jsx3(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
|
|
2514
|
+
}
|
|
2515
|
+
)
|
|
2516
|
+
) })
|
|
2517
|
+
] })
|
|
2518
|
+
] }),
|
|
2519
|
+
/* @__PURE__ */ jsx3("div", { style: defaultStyles.chatPanel, children: apiClient.current && /* @__PURE__ */ jsx3(
|
|
2520
|
+
QuestionChatPanel,
|
|
2521
|
+
{
|
|
2522
|
+
apiClient: apiClient.current,
|
|
2523
|
+
question: {
|
|
2524
|
+
id: currentQuestion.id,
|
|
2525
|
+
question: currentQuestion.question,
|
|
2526
|
+
type: currentQuestion.type,
|
|
2527
|
+
options: currentQuestion.options,
|
|
2528
|
+
correctAnswer: currentQuestion.correctAnswer,
|
|
2529
|
+
explanation: currentQuestion.explanation
|
|
2530
|
+
},
|
|
2531
|
+
quizId: quiz.id,
|
|
2532
|
+
childId,
|
|
2533
|
+
parentId,
|
|
2534
|
+
lessonId,
|
|
2535
|
+
courseId,
|
|
2536
|
+
answerResult: showFeedback && currentAnswerDetail ? {
|
|
2537
|
+
wasIncorrect: !currentAnswerDetail.isCorrect,
|
|
2538
|
+
selectedAnswer: typeof selectedAnswer === "string" ? selectedAnswer : Array.isArray(selectedAnswer) ? selectedAnswer.join(", ") : void 0,
|
|
2539
|
+
correctAnswer: typeof currentQuestion.correctAnswer === "string" ? currentQuestion.correctAnswer : Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer.join(", ") : void 0,
|
|
2540
|
+
explanation: currentQuestion.explanation
|
|
2541
|
+
} : void 0
|
|
2542
|
+
}
|
|
2543
|
+
) })
|
|
2544
|
+
] }) });
|
|
2256
2545
|
}
|
|
2257
2546
|
|
|
2258
2547
|
// src/AttemptViewer.tsx
|
|
@@ -2378,6 +2667,46 @@ var defaultStyles2 = {
|
|
|
2378
2667
|
fontSize: "14px",
|
|
2379
2668
|
color: "#581c87"
|
|
2380
2669
|
},
|
|
2670
|
+
chatHistorySection: {
|
|
2671
|
+
marginTop: "12px",
|
|
2672
|
+
borderTop: "1px solid #e5e7eb",
|
|
2673
|
+
paddingTop: "12px"
|
|
2674
|
+
},
|
|
2675
|
+
chatToggleButton: {
|
|
2676
|
+
display: "flex",
|
|
2677
|
+
alignItems: "center",
|
|
2678
|
+
gap: "6px",
|
|
2679
|
+
padding: "6px 12px",
|
|
2680
|
+
backgroundColor: "#f3f4f6",
|
|
2681
|
+
border: "none",
|
|
2682
|
+
borderRadius: "6px",
|
|
2683
|
+
fontSize: "13px",
|
|
2684
|
+
color: "#6b7280",
|
|
2685
|
+
cursor: "pointer",
|
|
2686
|
+
fontWeight: "500"
|
|
2687
|
+
},
|
|
2688
|
+
chatMessages: {
|
|
2689
|
+
marginTop: "12px",
|
|
2690
|
+
display: "flex",
|
|
2691
|
+
flexDirection: "column",
|
|
2692
|
+
gap: "8px"
|
|
2693
|
+
},
|
|
2694
|
+
chatMessage: {
|
|
2695
|
+
padding: "8px 12px",
|
|
2696
|
+
borderRadius: "8px",
|
|
2697
|
+
fontSize: "13px",
|
|
2698
|
+
maxWidth: "85%"
|
|
2699
|
+
},
|
|
2700
|
+
chatMessageUser: {
|
|
2701
|
+
backgroundColor: "#6721b0",
|
|
2702
|
+
color: "#ffffff",
|
|
2703
|
+
alignSelf: "flex-end"
|
|
2704
|
+
},
|
|
2705
|
+
chatMessageAssistant: {
|
|
2706
|
+
backgroundColor: "#f3f4f6",
|
|
2707
|
+
color: "#111827",
|
|
2708
|
+
alignSelf: "flex-start"
|
|
2709
|
+
},
|
|
2381
2710
|
loading: {
|
|
2382
2711
|
textAlign: "center",
|
|
2383
2712
|
padding: "40px 20px"
|
|
@@ -2435,11 +2764,14 @@ function AttemptViewer({
|
|
|
2435
2764
|
onError,
|
|
2436
2765
|
className,
|
|
2437
2766
|
showExplanations = true,
|
|
2767
|
+
showConversation = false,
|
|
2438
2768
|
title
|
|
2439
2769
|
}) {
|
|
2440
2770
|
const [attempt, setAttempt] = useState4(null);
|
|
2441
2771
|
const [loading, setLoading] = useState4(true);
|
|
2442
2772
|
const [error, setError] = useState4(null);
|
|
2773
|
+
const [chatHistories, setChatHistories] = useState4({});
|
|
2774
|
+
const [expandedChats, setExpandedChats] = useState4(/* @__PURE__ */ new Set());
|
|
2443
2775
|
useEffect4(() => {
|
|
2444
2776
|
const apiClient = new QuizApiClient({
|
|
2445
2777
|
baseUrl: apiBaseUrl,
|
|
@@ -2457,6 +2789,14 @@ function AttemptViewer({
|
|
|
2457
2789
|
}
|
|
2458
2790
|
const data = await response.json();
|
|
2459
2791
|
setAttempt(data);
|
|
2792
|
+
if (showConversation) {
|
|
2793
|
+
try {
|
|
2794
|
+
const chats = await apiClient.getChatsByAttempt(attemptId);
|
|
2795
|
+
setChatHistories(chats);
|
|
2796
|
+
} catch (chatErr) {
|
|
2797
|
+
console.error("Failed to load chat histories:", chatErr);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2460
2800
|
} catch (err) {
|
|
2461
2801
|
const errorMessage = err instanceof Error ? err.message : "Failed to load attempt";
|
|
2462
2802
|
setError(errorMessage);
|
|
@@ -2466,7 +2806,18 @@ function AttemptViewer({
|
|
|
2466
2806
|
}
|
|
2467
2807
|
}
|
|
2468
2808
|
fetchAttempt();
|
|
2469
|
-
}, [attemptId, apiBaseUrl, authToken, onError]);
|
|
2809
|
+
}, [attemptId, apiBaseUrl, authToken, onError, showConversation]);
|
|
2810
|
+
const toggleChatExpanded = (questionId) => {
|
|
2811
|
+
setExpandedChats((prev) => {
|
|
2812
|
+
const newSet = new Set(prev);
|
|
2813
|
+
if (newSet.has(questionId)) {
|
|
2814
|
+
newSet.delete(questionId);
|
|
2815
|
+
} else {
|
|
2816
|
+
newSet.add(questionId);
|
|
2817
|
+
}
|
|
2818
|
+
return newSet;
|
|
2819
|
+
});
|
|
2820
|
+
};
|
|
2470
2821
|
const handleRetry = () => {
|
|
2471
2822
|
setLoading(true);
|
|
2472
2823
|
setError(null);
|
|
@@ -2558,6 +2909,34 @@ function AttemptViewer({
|
|
|
2558
2909
|
/* @__PURE__ */ jsx4("strong", { children: "Explanation:" }),
|
|
2559
2910
|
" ",
|
|
2560
2911
|
answer.explanation
|
|
2912
|
+
] }),
|
|
2913
|
+
showConversation && chatHistories[answer.questionId] && chatHistories[answer.questionId].messages.length > 0 && /* @__PURE__ */ jsxs4("div", { style: defaultStyles2.chatHistorySection, children: [
|
|
2914
|
+
/* @__PURE__ */ jsxs4(
|
|
2915
|
+
"button",
|
|
2916
|
+
{
|
|
2917
|
+
style: defaultStyles2.chatToggleButton,
|
|
2918
|
+
onClick: () => toggleChatExpanded(answer.questionId),
|
|
2919
|
+
"data-testid": `button-toggle-chat-${answer.questionId}`,
|
|
2920
|
+
children: [
|
|
2921
|
+
/* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }),
|
|
2922
|
+
expandedChats.has(answer.questionId) ? "Hide" : "View",
|
|
2923
|
+
" Chat History (",
|
|
2924
|
+
chatHistories[answer.questionId].messages.length,
|
|
2925
|
+
" messages)"
|
|
2926
|
+
]
|
|
2927
|
+
}
|
|
2928
|
+
),
|
|
2929
|
+
expandedChats.has(answer.questionId) && /* @__PURE__ */ jsx4("div", { style: defaultStyles2.chatMessages, children: chatHistories[answer.questionId].messages.map((msg, msgIndex) => /* @__PURE__ */ jsx4(
|
|
2930
|
+
"div",
|
|
2931
|
+
{
|
|
2932
|
+
style: {
|
|
2933
|
+
...defaultStyles2.chatMessage,
|
|
2934
|
+
...msg.role === "user" ? defaultStyles2.chatMessageUser : defaultStyles2.chatMessageAssistant
|
|
2935
|
+
},
|
|
2936
|
+
children: msg.content
|
|
2937
|
+
},
|
|
2938
|
+
msgIndex
|
|
2939
|
+
)) })
|
|
2561
2940
|
] })
|
|
2562
2941
|
]
|
|
2563
2942
|
},
|