@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.js
CHANGED
|
@@ -113,6 +113,29 @@ var QuizApiClient = class {
|
|
|
113
113
|
`/api/external/question-chat/${questionId}/${childId}`
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
|
+
async getChatsByAttempt(attemptId) {
|
|
117
|
+
return this.request(
|
|
118
|
+
"GET",
|
|
119
|
+
`/api/external/quiz-attempts/${attemptId}/chats`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
async getTextToSpeech(text, voice = "nova") {
|
|
123
|
+
const headers = {
|
|
124
|
+
"Content-Type": "application/json"
|
|
125
|
+
};
|
|
126
|
+
if (this.authToken) {
|
|
127
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
128
|
+
}
|
|
129
|
+
const response = await fetch(`${this.baseUrl}/api/external/tts`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers,
|
|
132
|
+
body: JSON.stringify({ text, voice })
|
|
133
|
+
});
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
throw new Error(`TTS request failed: HTTP ${response.status}`);
|
|
136
|
+
}
|
|
137
|
+
return response.blob();
|
|
138
|
+
}
|
|
116
139
|
};
|
|
117
140
|
|
|
118
141
|
// src/utils.ts
|
|
@@ -515,9 +538,13 @@ var panelStyles = {
|
|
|
515
538
|
messageRow: {
|
|
516
539
|
display: "flex"
|
|
517
540
|
},
|
|
541
|
+
messageContent: {
|
|
542
|
+
display: "flex",
|
|
543
|
+
alignItems: "flex-end",
|
|
544
|
+
gap: "4px",
|
|
545
|
+
maxWidth: "85%"
|
|
546
|
+
},
|
|
518
547
|
userMessage: {
|
|
519
|
-
maxWidth: "85%",
|
|
520
|
-
marginLeft: "auto",
|
|
521
548
|
padding: "10px 14px",
|
|
522
549
|
borderRadius: "16px 16px 4px 16px",
|
|
523
550
|
backgroundColor: "#6721b0",
|
|
@@ -526,8 +553,6 @@ var panelStyles = {
|
|
|
526
553
|
lineHeight: 1.4
|
|
527
554
|
},
|
|
528
555
|
assistantMessage: {
|
|
529
|
-
maxWidth: "85%",
|
|
530
|
-
marginRight: "auto",
|
|
531
556
|
padding: "10px 14px",
|
|
532
557
|
borderRadius: "16px 16px 16px 4px",
|
|
533
558
|
backgroundColor: "#ffffff",
|
|
@@ -541,7 +566,8 @@ var panelStyles = {
|
|
|
541
566
|
borderTop: "1px solid #e2e8f0",
|
|
542
567
|
backgroundColor: "#ffffff",
|
|
543
568
|
display: "flex",
|
|
544
|
-
gap: "8px"
|
|
569
|
+
gap: "8px",
|
|
570
|
+
alignItems: "center"
|
|
545
571
|
},
|
|
546
572
|
input: {
|
|
547
573
|
flex: 1,
|
|
@@ -551,13 +577,11 @@ var panelStyles = {
|
|
|
551
577
|
fontSize: "14px",
|
|
552
578
|
outline: "none"
|
|
553
579
|
},
|
|
554
|
-
|
|
580
|
+
buttonBase: {
|
|
555
581
|
width: "40px",
|
|
556
582
|
height: "40px",
|
|
557
583
|
borderRadius: "50%",
|
|
558
584
|
border: "none",
|
|
559
|
-
backgroundColor: "#6721b0",
|
|
560
|
-
color: "#ffffff",
|
|
561
585
|
cursor: "pointer",
|
|
562
586
|
display: "flex",
|
|
563
587
|
alignItems: "center",
|
|
@@ -565,10 +589,47 @@ var panelStyles = {
|
|
|
565
589
|
fontSize: "16px",
|
|
566
590
|
transition: "all 0.2s ease"
|
|
567
591
|
},
|
|
592
|
+
sendButton: {
|
|
593
|
+
backgroundColor: "#6721b0",
|
|
594
|
+
color: "#ffffff"
|
|
595
|
+
},
|
|
568
596
|
sendButtonDisabled: {
|
|
569
597
|
backgroundColor: "#d1d5db",
|
|
570
598
|
cursor: "not-allowed"
|
|
571
599
|
},
|
|
600
|
+
micButton: {
|
|
601
|
+
backgroundColor: "#ffffff",
|
|
602
|
+
border: "1px solid #e2e8f0",
|
|
603
|
+
color: "#374151"
|
|
604
|
+
},
|
|
605
|
+
micButtonActive: {
|
|
606
|
+
backgroundColor: "#ef4444",
|
|
607
|
+
color: "#ffffff",
|
|
608
|
+
border: "none"
|
|
609
|
+
},
|
|
610
|
+
speakButton: {
|
|
611
|
+
width: "28px",
|
|
612
|
+
height: "28px",
|
|
613
|
+
borderRadius: "50%",
|
|
614
|
+
border: "none",
|
|
615
|
+
backgroundColor: "transparent",
|
|
616
|
+
color: "#9ca3af",
|
|
617
|
+
cursor: "pointer",
|
|
618
|
+
display: "flex",
|
|
619
|
+
alignItems: "center",
|
|
620
|
+
justifyContent: "center",
|
|
621
|
+
transition: "all 0.2s ease",
|
|
622
|
+
flexShrink: 0
|
|
623
|
+
},
|
|
624
|
+
speakButtonReady: {
|
|
625
|
+
color: "#22c55e"
|
|
626
|
+
},
|
|
627
|
+
speakButtonPlaying: {
|
|
628
|
+
color: "#6721b0"
|
|
629
|
+
},
|
|
630
|
+
speakButtonLoading: {
|
|
631
|
+
color: "#f97316"
|
|
632
|
+
},
|
|
572
633
|
loadingDots: {
|
|
573
634
|
display: "flex",
|
|
574
635
|
alignItems: "center",
|
|
@@ -606,6 +667,33 @@ var STARTER_PROMPTS = [
|
|
|
606
667
|
{ id: "word_help", label: "I don't understand a word", message: "I don't understand a word in this question. Can you help?" },
|
|
607
668
|
{ id: "explain_again", label: "Explain this again", message: "Can you explain this question to me again in a different way?" }
|
|
608
669
|
];
|
|
670
|
+
var MicIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
671
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
674
|
+
] });
|
|
675
|
+
var MicOffIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "2", x2: "22", y1: "2", y2: "22" }),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }),
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 10v2a7 7 0 0 0 12 5" }),
|
|
679
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }),
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
682
|
+
] });
|
|
683
|
+
var VolumeIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
|
|
685
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
|
|
686
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
|
|
687
|
+
] });
|
|
688
|
+
var SendIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
689
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
690
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
691
|
+
] });
|
|
692
|
+
var HelpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "#6721b0", strokeWidth: "2", children: [
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
694
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
695
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
696
|
+
] });
|
|
609
697
|
function QuestionChatPanel({
|
|
610
698
|
apiClient,
|
|
611
699
|
question,
|
|
@@ -613,20 +701,130 @@ function QuestionChatPanel({
|
|
|
613
701
|
childId,
|
|
614
702
|
parentId,
|
|
615
703
|
lessonId,
|
|
616
|
-
courseId
|
|
704
|
+
courseId,
|
|
705
|
+
answerResult
|
|
617
706
|
}) {
|
|
618
707
|
const [messages, setMessages] = (0, import_react2.useState)([]);
|
|
619
708
|
const [inputValue, setInputValue] = (0, import_react2.useState)("");
|
|
620
709
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
621
710
|
const [chatId, setChatId] = (0, import_react2.useState)(null);
|
|
622
711
|
const [hoveredButton, setHoveredButton] = (0, import_react2.useState)(null);
|
|
712
|
+
const [isListening, setIsListening] = (0, import_react2.useState)(false);
|
|
713
|
+
const [speakingIndex, setSpeakingIndex] = (0, import_react2.useState)(null);
|
|
714
|
+
const [audioReadyMap, setAudioReadyMap] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
|
|
715
|
+
const [hasOfferedHelp, setHasOfferedHelp] = (0, import_react2.useState)(false);
|
|
623
716
|
const messagesContainerRef = (0, import_react2.useRef)(null);
|
|
624
717
|
const messagesEndRef = (0, import_react2.useRef)(null);
|
|
718
|
+
const recognitionRef = (0, import_react2.useRef)(null);
|
|
719
|
+
const audioRef = (0, import_react2.useRef)(null);
|
|
720
|
+
const audioCacheRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
721
|
+
const isSpeechSupported = typeof window !== "undefined" && (window.SpeechRecognition || window.webkitSpeechRecognition);
|
|
625
722
|
const scrollToBottom = (0, import_react2.useCallback)(() => {
|
|
626
723
|
if (messagesContainerRef.current) {
|
|
627
724
|
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
|
|
628
725
|
}
|
|
629
726
|
}, []);
|
|
727
|
+
const preCacheAudio = (0, import_react2.useCallback)(async (text) => {
|
|
728
|
+
if (audioCacheRef.current.has(text)) {
|
|
729
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, false));
|
|
733
|
+
try {
|
|
734
|
+
const audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
735
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
736
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
737
|
+
} catch (error) {
|
|
738
|
+
console.error("Pre-cache TTS error:", error);
|
|
739
|
+
}
|
|
740
|
+
}, [apiClient]);
|
|
741
|
+
const startListening = (0, import_react2.useCallback)(() => {
|
|
742
|
+
const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
743
|
+
if (!SpeechRecognitionAPI) {
|
|
744
|
+
console.log("Speech recognition not supported");
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const recognition = new SpeechRecognitionAPI();
|
|
748
|
+
recognition.continuous = true;
|
|
749
|
+
recognition.interimResults = true;
|
|
750
|
+
recognition.lang = "en-US";
|
|
751
|
+
recognition.onstart = () => {
|
|
752
|
+
console.log("Speech recognition started");
|
|
753
|
+
setIsListening(true);
|
|
754
|
+
};
|
|
755
|
+
recognition.onresult = (event) => {
|
|
756
|
+
let finalTranscript = "";
|
|
757
|
+
for (let i = 0; i < Object.keys(event.results).length; i++) {
|
|
758
|
+
const result = event.results[i];
|
|
759
|
+
if (result && result[0]) {
|
|
760
|
+
finalTranscript += result[0].transcript;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (finalTranscript) {
|
|
764
|
+
setInputValue(finalTranscript);
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
recognition.onerror = (event) => {
|
|
768
|
+
console.log("Speech recognition error:", event.error);
|
|
769
|
+
if (event.error === "not-allowed") {
|
|
770
|
+
alert("Microphone access was denied. Please allow microphone access and try again.");
|
|
771
|
+
}
|
|
772
|
+
setIsListening(false);
|
|
773
|
+
};
|
|
774
|
+
recognition.onend = () => {
|
|
775
|
+
console.log("Speech recognition ended");
|
|
776
|
+
setIsListening(false);
|
|
777
|
+
};
|
|
778
|
+
recognitionRef.current = recognition;
|
|
779
|
+
try {
|
|
780
|
+
recognition.start();
|
|
781
|
+
} catch (e) {
|
|
782
|
+
console.log("Failed to start recognition:", e);
|
|
783
|
+
setIsListening(false);
|
|
784
|
+
}
|
|
785
|
+
}, []);
|
|
786
|
+
const stopListening = (0, import_react2.useCallback)(() => {
|
|
787
|
+
if (recognitionRef.current) {
|
|
788
|
+
recognitionRef.current.stop();
|
|
789
|
+
setIsListening(false);
|
|
790
|
+
}
|
|
791
|
+
}, []);
|
|
792
|
+
const speakMessage = (0, import_react2.useCallback)(async (text, index) => {
|
|
793
|
+
if (audioRef.current) {
|
|
794
|
+
audioRef.current.pause();
|
|
795
|
+
audioRef.current = null;
|
|
796
|
+
}
|
|
797
|
+
if (speakingIndex === index) {
|
|
798
|
+
setSpeakingIndex(null);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
setSpeakingIndex(index);
|
|
802
|
+
try {
|
|
803
|
+
let audioBlob;
|
|
804
|
+
const cachedBlob = audioCacheRef.current.get(text);
|
|
805
|
+
if (cachedBlob) {
|
|
806
|
+
audioBlob = cachedBlob;
|
|
807
|
+
} else {
|
|
808
|
+
audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
809
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
810
|
+
}
|
|
811
|
+
const audioUrl = URL.createObjectURL(audioBlob);
|
|
812
|
+
const audio = new Audio(audioUrl);
|
|
813
|
+
audioRef.current = audio;
|
|
814
|
+
audio.onended = () => {
|
|
815
|
+
setSpeakingIndex(null);
|
|
816
|
+
URL.revokeObjectURL(audioUrl);
|
|
817
|
+
};
|
|
818
|
+
audio.onerror = () => {
|
|
819
|
+
setSpeakingIndex(null);
|
|
820
|
+
URL.revokeObjectURL(audioUrl);
|
|
821
|
+
};
|
|
822
|
+
await audio.play();
|
|
823
|
+
} catch (error) {
|
|
824
|
+
console.error("TTS error:", error);
|
|
825
|
+
setSpeakingIndex(null);
|
|
826
|
+
}
|
|
827
|
+
}, [speakingIndex, apiClient]);
|
|
630
828
|
(0, import_react2.useEffect)(() => {
|
|
631
829
|
scrollToBottom();
|
|
632
830
|
}, [messages, scrollToBottom]);
|
|
@@ -634,19 +832,35 @@ function QuestionChatPanel({
|
|
|
634
832
|
setMessages([]);
|
|
635
833
|
setChatId(null);
|
|
636
834
|
setInputValue("");
|
|
835
|
+
setHasOfferedHelp(false);
|
|
637
836
|
const loadHistory = async () => {
|
|
638
837
|
try {
|
|
639
838
|
const history = await apiClient.getChatHistory(question.id, childId);
|
|
640
839
|
if (history.chatId && history.messages.length > 0) {
|
|
641
840
|
setChatId(history.chatId);
|
|
642
841
|
setMessages(history.messages);
|
|
842
|
+
history.messages.filter((m) => m.role === "assistant").forEach((m) => preCacheAudio(m.content));
|
|
643
843
|
}
|
|
644
844
|
} catch (err) {
|
|
645
845
|
console.error("Failed to load chat history:", err);
|
|
646
846
|
}
|
|
647
847
|
};
|
|
648
848
|
loadHistory();
|
|
649
|
-
}, [question.id, childId, apiClient]);
|
|
849
|
+
}, [question.id, childId, apiClient, preCacheAudio]);
|
|
850
|
+
(0, import_react2.useEffect)(() => {
|
|
851
|
+
if (answerResult?.wasIncorrect && !hasOfferedHelp) {
|
|
852
|
+
setHasOfferedHelp(true);
|
|
853
|
+
const selectedAnswerText = answerResult.selectedAnswer || "that answer";
|
|
854
|
+
const helpMessage = `Looks like you chose "${selectedAnswerText}" which was incorrect. Would you like me to help explain the correct answer?`;
|
|
855
|
+
const assistantMessage = {
|
|
856
|
+
role: "assistant",
|
|
857
|
+
content: helpMessage,
|
|
858
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
859
|
+
};
|
|
860
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
861
|
+
preCacheAudio(helpMessage);
|
|
862
|
+
}
|
|
863
|
+
}, [answerResult, hasOfferedHelp, preCacheAudio]);
|
|
650
864
|
const initializeChat = async () => {
|
|
651
865
|
if (chatId) return chatId;
|
|
652
866
|
try {
|
|
@@ -668,6 +882,9 @@ function QuestionChatPanel({
|
|
|
668
882
|
};
|
|
669
883
|
const sendMessage = async (messageText) => {
|
|
670
884
|
if (!messageText.trim() || isLoading) return;
|
|
885
|
+
if (isListening) {
|
|
886
|
+
stopListening();
|
|
887
|
+
}
|
|
671
888
|
setIsLoading(true);
|
|
672
889
|
const userMsg = {
|
|
673
890
|
role: "user",
|
|
@@ -687,15 +904,23 @@ function QuestionChatPanel({
|
|
|
687
904
|
questionContext: question,
|
|
688
905
|
childId
|
|
689
906
|
});
|
|
690
|
-
|
|
907
|
+
const assistantMessage = response.assistantMessage || {
|
|
908
|
+
role: "assistant",
|
|
909
|
+
content: "I'm here to help!",
|
|
910
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
911
|
+
};
|
|
912
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
913
|
+
preCacheAudio(assistantMessage.content);
|
|
691
914
|
} catch (err) {
|
|
692
915
|
console.error("Failed to send message:", err);
|
|
916
|
+
const content = "Sorry, I'm having trouble right now. Please try again!";
|
|
693
917
|
const errorMsg = {
|
|
694
918
|
role: "assistant",
|
|
695
|
-
content
|
|
919
|
+
content,
|
|
696
920
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
697
921
|
};
|
|
698
922
|
setMessages((prev) => [...prev, errorMsg]);
|
|
923
|
+
preCacheAudio(content);
|
|
699
924
|
} finally {
|
|
700
925
|
setIsLoading(false);
|
|
701
926
|
}
|
|
@@ -712,14 +937,14 @@ function QuestionChatPanel({
|
|
|
712
937
|
0%, 60%, 100% { transform: translateY(0); }
|
|
713
938
|
30% { transform: translateY(-4px); }
|
|
714
939
|
}
|
|
940
|
+
@keyframes pulse {
|
|
941
|
+
0%, 100% { opacity: 1; }
|
|
942
|
+
50% { opacity: 0.5; }
|
|
943
|
+
}
|
|
715
944
|
` }),
|
|
716
945
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: panelStyles.header, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Need Help?" }) }),
|
|
717
946
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: messagesContainerRef, style: panelStyles.messagesContainer, children: messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: panelStyles.emptyState, children: [
|
|
718
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */ (0, import_jsx_runtime2.
|
|
719
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
720
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
721
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
722
|
-
] }) }),
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HelpIcon, {}) }),
|
|
723
948
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: "14px", fontWeight: "500", marginBottom: "8px" }, children: "Hi! I'm your question helper" }),
|
|
724
949
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: "13px", color: "#9ca3af" }, children: "Ask me if you need help understanding this question" }),
|
|
725
950
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...panelStyles.starterPrompts, marginTop: "16px" }, children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -746,7 +971,26 @@ function QuestionChatPanel({
|
|
|
746
971
|
...panelStyles.messageRow,
|
|
747
972
|
justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
|
|
748
973
|
},
|
|
749
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.
|
|
974
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
|
|
975
|
+
...panelStyles.messageContent,
|
|
976
|
+
flexDirection: msg.role === "user" ? "row-reverse" : "row"
|
|
977
|
+
}, children: [
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: msg.role === "user" ? panelStyles.userMessage : panelStyles.assistantMessage, children: msg.content }),
|
|
979
|
+
msg.role === "assistant" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
980
|
+
"button",
|
|
981
|
+
{
|
|
982
|
+
style: {
|
|
983
|
+
...panelStyles.speakButton,
|
|
984
|
+
...speakingIndex === idx ? panelStyles.speakButtonPlaying : audioReadyMap.get(msg.content) === true ? panelStyles.speakButtonReady : audioReadyMap.get(msg.content) === false ? panelStyles.speakButtonLoading : {},
|
|
985
|
+
...audioReadyMap.get(msg.content) === false ? { animation: "pulse 1.5s ease-in-out infinite" } : {}
|
|
986
|
+
},
|
|
987
|
+
onClick: () => speakMessage(msg.content, idx),
|
|
988
|
+
"data-testid": `button-speak-${idx}`,
|
|
989
|
+
title: "Listen to this message",
|
|
990
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VolumeIcon2, {})
|
|
991
|
+
}
|
|
992
|
+
)
|
|
993
|
+
] })
|
|
750
994
|
},
|
|
751
995
|
idx
|
|
752
996
|
)),
|
|
@@ -765,26 +1009,38 @@ function QuestionChatPanel({
|
|
|
765
1009
|
value: inputValue,
|
|
766
1010
|
onChange: (e) => setInputValue(e.target.value),
|
|
767
1011
|
onKeyPress: handleKeyPress,
|
|
768
|
-
placeholder: "Ask about this question...",
|
|
1012
|
+
placeholder: isListening ? "Listening..." : "Ask about this question...",
|
|
769
1013
|
style: panelStyles.input,
|
|
770
|
-
disabled: isLoading,
|
|
1014
|
+
disabled: isLoading || isListening,
|
|
771
1015
|
"data-testid": "input-chat-message"
|
|
772
1016
|
}
|
|
773
1017
|
),
|
|
1018
|
+
isSpeechSupported && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1019
|
+
"button",
|
|
1020
|
+
{
|
|
1021
|
+
onClick: isListening ? stopListening : startListening,
|
|
1022
|
+
disabled: isLoading,
|
|
1023
|
+
style: {
|
|
1024
|
+
...panelStyles.buttonBase,
|
|
1025
|
+
...isListening ? panelStyles.micButtonActive : panelStyles.micButton
|
|
1026
|
+
},
|
|
1027
|
+
"data-testid": "button-voice-input",
|
|
1028
|
+
title: isListening ? "Stop listening" : "Speak your question",
|
|
1029
|
+
children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MicOffIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MicIcon, {})
|
|
1030
|
+
}
|
|
1031
|
+
),
|
|
774
1032
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
775
1033
|
"button",
|
|
776
1034
|
{
|
|
777
1035
|
onClick: () => sendMessage(inputValue),
|
|
778
1036
|
disabled: isLoading || !inputValue.trim(),
|
|
779
1037
|
style: {
|
|
1038
|
+
...panelStyles.buttonBase,
|
|
780
1039
|
...panelStyles.sendButton,
|
|
781
1040
|
...isLoading || !inputValue.trim() ? panelStyles.sendButtonDisabled : {}
|
|
782
1041
|
},
|
|
783
1042
|
"data-testid": "button-send-chat",
|
|
784
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.
|
|
785
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
786
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
787
|
-
] })
|
|
1043
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
788
1044
|
}
|
|
789
1045
|
)
|
|
790
1046
|
] })
|
|
@@ -797,15 +1053,28 @@ var defaultStyles = {
|
|
|
797
1053
|
container: {
|
|
798
1054
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
799
1055
|
width: "100%",
|
|
1056
|
+
height: "100%",
|
|
800
1057
|
padding: "20px",
|
|
801
1058
|
backgroundColor: "#ffffff",
|
|
802
1059
|
borderRadius: "12px",
|
|
803
|
-
boxSizing: "border-box"
|
|
1060
|
+
boxSizing: "border-box",
|
|
1061
|
+
display: "flex",
|
|
1062
|
+
flexDirection: "column"
|
|
804
1063
|
},
|
|
805
1064
|
header: {
|
|
806
1065
|
marginBottom: "20px",
|
|
807
1066
|
borderBottom: "1px solid #e5e7eb",
|
|
808
|
-
paddingBottom: "16px"
|
|
1067
|
+
paddingBottom: "16px",
|
|
1068
|
+
display: "flex",
|
|
1069
|
+
flexDirection: "column"
|
|
1070
|
+
},
|
|
1071
|
+
headerTop: {
|
|
1072
|
+
display: "flex",
|
|
1073
|
+
justifyContent: "space-between",
|
|
1074
|
+
alignItems: "flex-start"
|
|
1075
|
+
},
|
|
1076
|
+
headerLeft: {
|
|
1077
|
+
flex: 1
|
|
809
1078
|
},
|
|
810
1079
|
title: {
|
|
811
1080
|
fontSize: "24px",
|
|
@@ -817,7 +1086,8 @@ var defaultStyles = {
|
|
|
817
1086
|
color: "#6b7280"
|
|
818
1087
|
},
|
|
819
1088
|
progressBar: {
|
|
820
|
-
width: "
|
|
1089
|
+
width: "50%",
|
|
1090
|
+
maxWidth: "300px",
|
|
821
1091
|
height: "8px",
|
|
822
1092
|
backgroundColor: "#e5e7eb",
|
|
823
1093
|
borderRadius: "4px",
|
|
@@ -932,16 +1202,21 @@ var defaultStyles = {
|
|
|
932
1202
|
},
|
|
933
1203
|
mainLayout: {
|
|
934
1204
|
display: "flex",
|
|
935
|
-
gap: "24px"
|
|
1205
|
+
gap: "24px",
|
|
1206
|
+
flex: 1,
|
|
1207
|
+
minHeight: 0,
|
|
1208
|
+
alignItems: "stretch"
|
|
936
1209
|
},
|
|
937
1210
|
quizContent: {
|
|
938
1211
|
flex: 1,
|
|
939
|
-
minWidth: 0
|
|
1212
|
+
minWidth: 0,
|
|
1213
|
+
overflow: "auto"
|
|
940
1214
|
},
|
|
941
1215
|
chatPanel: {
|
|
942
1216
|
width: "320px",
|
|
943
1217
|
flexShrink: 0,
|
|
944
|
-
|
|
1218
|
+
display: "flex",
|
|
1219
|
+
flexDirection: "column"
|
|
945
1220
|
},
|
|
946
1221
|
timer: {
|
|
947
1222
|
fontSize: "14px",
|
|
@@ -1149,6 +1424,16 @@ function QuizPlayer({
|
|
|
1149
1424
|
const apiClient = (0, import_react3.useRef)(null);
|
|
1150
1425
|
const timerRef = (0, import_react3.useRef)(null);
|
|
1151
1426
|
const startTimeRef = (0, import_react3.useRef)(0);
|
|
1427
|
+
const onCompleteRef = (0, import_react3.useRef)(onComplete);
|
|
1428
|
+
const onErrorRef = (0, import_react3.useRef)(onError);
|
|
1429
|
+
const onProgressRef = (0, import_react3.useRef)(onProgress);
|
|
1430
|
+
const onGenerateMoreQuestionsRef = (0, import_react3.useRef)(onGenerateMoreQuestions);
|
|
1431
|
+
(0, import_react3.useEffect)(() => {
|
|
1432
|
+
onCompleteRef.current = onComplete;
|
|
1433
|
+
onErrorRef.current = onError;
|
|
1434
|
+
onProgressRef.current = onProgress;
|
|
1435
|
+
onGenerateMoreQuestionsRef.current = onGenerateMoreQuestions;
|
|
1436
|
+
});
|
|
1152
1437
|
(0, import_react3.useEffect)(() => {
|
|
1153
1438
|
apiClient.current = new QuizApiClient({ baseUrl: apiBaseUrl, authToken });
|
|
1154
1439
|
}, [apiBaseUrl, authToken]);
|
|
@@ -1195,11 +1480,11 @@ function QuizPlayer({
|
|
|
1195
1480
|
const message = err instanceof Error ? err.message : "Failed to load quiz";
|
|
1196
1481
|
setError(message);
|
|
1197
1482
|
setIsLoading(false);
|
|
1198
|
-
|
|
1483
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error(message));
|
|
1199
1484
|
}
|
|
1200
1485
|
}
|
|
1201
1486
|
initialize();
|
|
1202
|
-
}, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt
|
|
1487
|
+
}, [quizId, lessonId, assignLessonId, courseId, childId, parentId, forceNewAttempt]);
|
|
1203
1488
|
(0, import_react3.useEffect)(() => {
|
|
1204
1489
|
if (timerStarted && !isCompleted && !error) {
|
|
1205
1490
|
startTimeRef.current = Date.now();
|
|
@@ -1225,14 +1510,14 @@ function QuizPlayer({
|
|
|
1225
1510
|
const totalQuestions = allQuestions.length;
|
|
1226
1511
|
const maxQuestions = 50;
|
|
1227
1512
|
(0, import_react3.useEffect)(() => {
|
|
1228
|
-
if (quiz &&
|
|
1229
|
-
|
|
1513
|
+
if (quiz && onProgressRef.current) {
|
|
1514
|
+
onProgressRef.current({
|
|
1230
1515
|
currentQuestion: currentQuestionIndex + 1,
|
|
1231
1516
|
totalQuestions,
|
|
1232
1517
|
answeredQuestions: answers.size
|
|
1233
1518
|
});
|
|
1234
1519
|
}
|
|
1235
|
-
}, [currentQuestionIndex, answers.size, quiz,
|
|
1520
|
+
}, [currentQuestionIndex, answers.size, quiz, totalQuestions]);
|
|
1236
1521
|
const currentQuestion = allQuestions[currentQuestionIndex];
|
|
1237
1522
|
const handleAnswerChange = (0, import_react3.useCallback)((value) => {
|
|
1238
1523
|
if (!currentQuestion) return;
|
|
@@ -1277,7 +1562,7 @@ function QuizPlayer({
|
|
|
1277
1562
|
if (totalQuestions >= maxQuestions) return;
|
|
1278
1563
|
setIsGeneratingExtra(true);
|
|
1279
1564
|
try {
|
|
1280
|
-
const result2 = await
|
|
1565
|
+
const result2 = await onGenerateMoreQuestionsRef.current(attempt.id, totalQuestions);
|
|
1281
1566
|
if (result2.extraQuestions && result2.extraQuestions.length > 0) {
|
|
1282
1567
|
const slotsAvailable = maxQuestions - totalQuestions;
|
|
1283
1568
|
const questionsToAppend = result2.extraQuestions.slice(0, slotsAvailable);
|
|
@@ -1290,11 +1575,11 @@ function QuizPlayer({
|
|
|
1290
1575
|
}
|
|
1291
1576
|
} catch (err) {
|
|
1292
1577
|
console.error("Failed to generate extra questions:", err);
|
|
1293
|
-
|
|
1578
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error("Failed to generate extra questions"));
|
|
1294
1579
|
} finally {
|
|
1295
1580
|
setIsGeneratingExtra(false);
|
|
1296
1581
|
}
|
|
1297
|
-
}, [attempt,
|
|
1582
|
+
}, [attempt, isGeneratingExtra, totalQuestions, maxQuestions]);
|
|
1298
1583
|
const handleSubmit = (0, import_react3.useCallback)(async () => {
|
|
1299
1584
|
if (!quiz || !attempt || !apiClient.current) return;
|
|
1300
1585
|
setIsSubmitting(true);
|
|
@@ -1333,15 +1618,15 @@ function QuizPlayer({
|
|
|
1333
1618
|
if (timerRef.current) {
|
|
1334
1619
|
clearInterval(timerRef.current);
|
|
1335
1620
|
}
|
|
1336
|
-
|
|
1621
|
+
onCompleteRef.current?.(quizResult);
|
|
1337
1622
|
} catch (err) {
|
|
1338
1623
|
const message = err instanceof Error ? err.message : "Failed to submit quiz";
|
|
1339
1624
|
setError(message);
|
|
1340
|
-
|
|
1625
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error(message));
|
|
1341
1626
|
} finally {
|
|
1342
1627
|
setIsSubmitting(false);
|
|
1343
1628
|
}
|
|
1344
|
-
}, [quiz, attempt, currentQuestion, answers, answersDetail,
|
|
1629
|
+
}, [quiz, attempt, currentQuestion, answers, answersDetail, totalQuestions, timerStarted, elapsedSeconds]);
|
|
1345
1630
|
const isExtraQuestion = currentQuestion && extraQuestions.some((q) => q.id === currentQuestion.id);
|
|
1346
1631
|
const handleSkipQuestion = (0, import_react3.useCallback)(async (reason, comment) => {
|
|
1347
1632
|
if (!currentQuestion || !apiClient.current || !attempt) return;
|
|
@@ -1377,7 +1662,7 @@ function QuizPlayer({
|
|
|
1377
1662
|
timeSpentSeconds: elapsedSeconds
|
|
1378
1663
|
};
|
|
1379
1664
|
setResult(quizResult);
|
|
1380
|
-
|
|
1665
|
+
onCompleteRef.current?.(quizResult);
|
|
1381
1666
|
} else if (currentQuestionIndex >= newTotalQuestions) {
|
|
1382
1667
|
setCurrentQuestionIndex(newTotalQuestions - 1);
|
|
1383
1668
|
}
|
|
@@ -1389,7 +1674,7 @@ function QuizPlayer({
|
|
|
1389
1674
|
} finally {
|
|
1390
1675
|
setIsSkipping(false);
|
|
1391
1676
|
}
|
|
1392
|
-
}, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId, skippedQuestionIds, extraQuestions, currentQuestionIndex, elapsedSeconds
|
|
1677
|
+
}, [currentQuestion, apiClient, attempt, quiz, childId, parentId, lessonId, courseId, assignLessonId, skippedQuestionIds, extraQuestions, currentQuestionIndex, elapsedSeconds]);
|
|
1393
1678
|
const handleReportQuestion = (0, import_react3.useCallback)(async (comment) => {
|
|
1394
1679
|
if (!currentQuestion || !apiClient.current || !attempt || !comment.trim()) return;
|
|
1395
1680
|
setIsReporting(true);
|
|
@@ -1759,533 +2044,537 @@ function QuizPlayer({
|
|
|
1759
2044
|
const remainingSlots = maxQuestions - totalQuestions;
|
|
1760
2045
|
const questionsToAdd = Math.min(5, remainingSlots);
|
|
1761
2046
|
const canAddMore = onGenerateMoreQuestions && remainingSlots > 0;
|
|
1762
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1763
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.
|
|
1764
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2047
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: defaultStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.mainLayout, children: [
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.quizContent, children: [
|
|
2049
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.header, children: [
|
|
2050
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.title, children: quiz.title }),
|
|
2051
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.progress, children: [
|
|
2052
|
+
"Question ",
|
|
2053
|
+
currentQuestionIndex + 1,
|
|
2054
|
+
" of ",
|
|
2055
|
+
totalQuestions
|
|
2056
|
+
] }),
|
|
2057
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.progressBar, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultStyles.progressFill, width: `${progressPercent}%` } }) })
|
|
1770
2058
|
] }),
|
|
1771
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
|
|
2060
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
|
|
2061
|
+
isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2062
|
+
"button",
|
|
2063
|
+
{
|
|
2064
|
+
onClick: () => setShowSkipModal(true),
|
|
2065
|
+
title: "Skip question",
|
|
2066
|
+
style: {
|
|
2067
|
+
position: "absolute",
|
|
2068
|
+
bottom: "8px",
|
|
2069
|
+
left: "0",
|
|
2070
|
+
background: "transparent",
|
|
2071
|
+
border: "none",
|
|
2072
|
+
cursor: "pointer",
|
|
2073
|
+
padding: "6px 10px",
|
|
2074
|
+
borderRadius: "6px",
|
|
2075
|
+
color: "#9ca3af",
|
|
2076
|
+
display: "flex",
|
|
2077
|
+
alignItems: "center",
|
|
2078
|
+
justifyContent: "center",
|
|
2079
|
+
gap: "4px",
|
|
2080
|
+
fontSize: "12px",
|
|
2081
|
+
opacity: 0.6,
|
|
2082
|
+
transition: "opacity 0.2s, color 0.2s"
|
|
2083
|
+
},
|
|
2084
|
+
onMouseEnter: (e) => {
|
|
2085
|
+
e.currentTarget.style.opacity = "1";
|
|
2086
|
+
e.currentTarget.style.color = "#6b7280";
|
|
2087
|
+
},
|
|
2088
|
+
onMouseLeave: (e) => {
|
|
2089
|
+
e.currentTarget.style.opacity = "0.6";
|
|
2090
|
+
e.currentTarget.style.color = "#9ca3af";
|
|
2091
|
+
},
|
|
2092
|
+
"data-testid": "button-skip-question",
|
|
2093
|
+
children: [
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "5 4 15 12 5 20 5 4" }),
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
|
|
2097
|
+
] }),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Skip" })
|
|
2099
|
+
]
|
|
2100
|
+
}
|
|
2101
|
+
),
|
|
2102
|
+
!isExtraQuestion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2103
|
+
"button",
|
|
2104
|
+
{
|
|
2105
|
+
onClick: () => setShowReportModal(true),
|
|
2106
|
+
title: "Report an issue with this question",
|
|
2107
|
+
style: {
|
|
2108
|
+
position: "absolute",
|
|
2109
|
+
bottom: "8px",
|
|
2110
|
+
left: "0",
|
|
2111
|
+
background: "transparent",
|
|
2112
|
+
border: "none",
|
|
2113
|
+
cursor: "pointer",
|
|
2114
|
+
padding: "6px 10px",
|
|
2115
|
+
borderRadius: "6px",
|
|
2116
|
+
color: "#9ca3af",
|
|
2117
|
+
display: "flex",
|
|
2118
|
+
alignItems: "center",
|
|
2119
|
+
justifyContent: "center",
|
|
2120
|
+
gap: "4px",
|
|
2121
|
+
fontSize: "12px",
|
|
2122
|
+
opacity: 0.6,
|
|
2123
|
+
transition: "opacity 0.2s, color 0.2s"
|
|
2124
|
+
},
|
|
2125
|
+
onMouseEnter: (e) => {
|
|
2126
|
+
e.currentTarget.style.opacity = "1";
|
|
2127
|
+
e.currentTarget.style.color = "#ef4444";
|
|
2128
|
+
},
|
|
2129
|
+
onMouseLeave: (e) => {
|
|
2130
|
+
e.currentTarget.style.opacity = "0.6";
|
|
2131
|
+
e.currentTarget.style.color = "#9ca3af";
|
|
2132
|
+
},
|
|
2133
|
+
"data-testid": "button-report-question",
|
|
2134
|
+
children: [
|
|
2135
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2136
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
|
|
2137
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
|
|
2138
|
+
] }),
|
|
2139
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Report" })
|
|
2140
|
+
]
|
|
2141
|
+
}
|
|
2142
|
+
),
|
|
2143
|
+
(currentQuestion.type === "single" || currentQuestion.type === "true-false") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
|
|
2144
|
+
const isSelected = selectedAnswer === option;
|
|
2145
|
+
const isCorrectOption = currentQuestion.correctAnswer === option;
|
|
2146
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2147
|
+
if (showFeedback) {
|
|
2148
|
+
if (isCorrectOption) {
|
|
2149
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
|
|
2150
|
+
} else if (isSelected && !isCorrectOption) {
|
|
2151
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
|
|
2152
|
+
}
|
|
2153
|
+
} else if (isSelected) {
|
|
2154
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2155
|
+
}
|
|
2156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2157
|
+
"div",
|
|
1779
2158
|
{
|
|
1780
|
-
onClick: () => setShowSkipModal(true),
|
|
1781
|
-
title: "Skip question",
|
|
1782
2159
|
style: {
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
left: "0",
|
|
1786
|
-
background: "transparent",
|
|
1787
|
-
border: "none",
|
|
1788
|
-
cursor: "pointer",
|
|
1789
|
-
padding: "6px 10px",
|
|
1790
|
-
borderRadius: "6px",
|
|
1791
|
-
color: "#9ca3af",
|
|
2160
|
+
...optionStyle,
|
|
2161
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
1792
2162
|
display: "flex",
|
|
1793
2163
|
alignItems: "center",
|
|
1794
|
-
|
|
1795
|
-
gap: "4px",
|
|
1796
|
-
fontSize: "12px",
|
|
1797
|
-
opacity: 0.6,
|
|
1798
|
-
transition: "opacity 0.2s, color 0.2s"
|
|
1799
|
-
},
|
|
1800
|
-
onMouseEnter: (e) => {
|
|
1801
|
-
e.currentTarget.style.opacity = "1";
|
|
1802
|
-
e.currentTarget.style.color = "#6b7280";
|
|
2164
|
+
gap: "8px"
|
|
1803
2165
|
},
|
|
1804
|
-
|
|
1805
|
-
e.currentTarget.style.opacity = "0.6";
|
|
1806
|
-
e.currentTarget.style.color = "#9ca3af";
|
|
1807
|
-
},
|
|
1808
|
-
"data-testid": "button-skip-question",
|
|
2166
|
+
onClick: () => !showFeedback && handleAnswerChange(option),
|
|
1809
2167
|
children: [
|
|
1810
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1811
|
-
|
|
1812
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "19", y1: "5", x2: "19", y2: "19" })
|
|
1813
|
-
] }),
|
|
1814
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Skip" })
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
1815
2170
|
]
|
|
2171
|
+
},
|
|
2172
|
+
idx
|
|
2173
|
+
);
|
|
2174
|
+
}) }),
|
|
2175
|
+
currentQuestion.type === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
|
|
2176
|
+
const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
|
|
2177
|
+
const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
|
|
2178
|
+
const isCorrectOption = correctAnswers.includes(option);
|
|
2179
|
+
let optionStyle = { ...defaultStyles.option };
|
|
2180
|
+
if (showFeedback) {
|
|
2181
|
+
if (isCorrectOption) {
|
|
2182
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
|
|
2183
|
+
} else if (selected && !isCorrectOption) {
|
|
2184
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
|
|
1816
2185
|
}
|
|
1817
|
-
)
|
|
1818
|
-
|
|
1819
|
-
|
|
2186
|
+
} else if (selected) {
|
|
2187
|
+
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2188
|
+
}
|
|
2189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2190
|
+
"div",
|
|
1820
2191
|
{
|
|
1821
|
-
onClick: () => setShowReportModal(true),
|
|
1822
|
-
title: "Report an issue with this question",
|
|
1823
2192
|
style: {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
left: "0",
|
|
1827
|
-
background: "transparent",
|
|
1828
|
-
border: "none",
|
|
1829
|
-
cursor: "pointer",
|
|
1830
|
-
padding: "6px 10px",
|
|
1831
|
-
borderRadius: "6px",
|
|
1832
|
-
color: "#9ca3af",
|
|
2193
|
+
...optionStyle,
|
|
2194
|
+
cursor: showFeedback ? "default" : "pointer",
|
|
1833
2195
|
display: "flex",
|
|
1834
2196
|
alignItems: "center",
|
|
1835
|
-
|
|
1836
|
-
gap: "4px",
|
|
1837
|
-
fontSize: "12px",
|
|
1838
|
-
opacity: 0.6,
|
|
1839
|
-
transition: "opacity 0.2s, color 0.2s"
|
|
1840
|
-
},
|
|
1841
|
-
onMouseEnter: (e) => {
|
|
1842
|
-
e.currentTarget.style.opacity = "1";
|
|
1843
|
-
e.currentTarget.style.color = "#ef4444";
|
|
2197
|
+
gap: "8px"
|
|
1844
2198
|
},
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
2199
|
+
onClick: () => {
|
|
2200
|
+
if (showFeedback) return;
|
|
2201
|
+
const current = Array.isArray(selectedAnswer) ? selectedAnswer : [];
|
|
2202
|
+
if (selected) {
|
|
2203
|
+
handleAnswerChange(current.filter((o) => o !== option));
|
|
2204
|
+
} else {
|
|
2205
|
+
handleAnswerChange([...current, option]);
|
|
2206
|
+
}
|
|
1848
2207
|
},
|
|
1849
|
-
"data-testid": "button-report-question",
|
|
1850
2208
|
children: [
|
|
1851
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1852
|
-
|
|
1853
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
|
|
1854
|
-
] }),
|
|
1855
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Report" })
|
|
2209
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
|
|
2210
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
1856
2211
|
]
|
|
2212
|
+
},
|
|
2213
|
+
idx
|
|
2214
|
+
);
|
|
2215
|
+
}) }),
|
|
2216
|
+
(currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2217
|
+
"textarea",
|
|
2218
|
+
{
|
|
2219
|
+
style: { ...defaultStyles.input, minHeight: currentQuestion.type === "essay" ? "150px" : "60px" },
|
|
2220
|
+
value: selectedAnswer || "",
|
|
2221
|
+
onChange: (e) => handleAnswerChange(e.target.value),
|
|
2222
|
+
placeholder: "Type your answer here...",
|
|
2223
|
+
disabled: showFeedback
|
|
2224
|
+
}
|
|
2225
|
+
),
|
|
2226
|
+
currentQuestion.type === "fill" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.blanks?.map((_, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2227
|
+
"input",
|
|
2228
|
+
{
|
|
2229
|
+
style: defaultStyles.input,
|
|
2230
|
+
value: (Array.isArray(selectedAnswer) ? selectedAnswer[idx] : "") || "",
|
|
2231
|
+
onChange: (e) => {
|
|
2232
|
+
const current = Array.isArray(selectedAnswer) ? [...selectedAnswer] : [];
|
|
2233
|
+
current[idx] = e.target.value;
|
|
2234
|
+
handleAnswerChange(current);
|
|
2235
|
+
},
|
|
2236
|
+
placeholder: `Blank ${idx + 1}`,
|
|
2237
|
+
disabled: showFeedback
|
|
2238
|
+
},
|
|
2239
|
+
idx
|
|
2240
|
+
)) }),
|
|
2241
|
+
showFeedback && currentAnswerDetail && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
2242
|
+
...defaultStyles.feedback,
|
|
2243
|
+
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackCorrect : defaultStyles.feedbackIncorrect
|
|
2244
|
+
}, children: [
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
2246
|
+
...defaultStyles.feedbackTitle,
|
|
2247
|
+
...currentAnswerDetail.isCorrect ? defaultStyles.feedbackTitleCorrect : defaultStyles.feedbackTitleIncorrect
|
|
2248
|
+
}, children: currentAnswerDetail.isCorrect ? "\u2713 Correct!" : "\u2717 Incorrect" }),
|
|
2249
|
+
currentQuestion.explanation && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.feedbackExplanation, children: currentQuestion.explanation })
|
|
2250
|
+
] })
|
|
2251
|
+
] }),
|
|
2252
|
+
showSkipModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
2253
|
+
position: "fixed",
|
|
2254
|
+
top: 0,
|
|
2255
|
+
left: 0,
|
|
2256
|
+
right: 0,
|
|
2257
|
+
bottom: 0,
|
|
2258
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2259
|
+
display: "flex",
|
|
2260
|
+
alignItems: "center",
|
|
2261
|
+
justifyContent: "center",
|
|
2262
|
+
zIndex: 1e3
|
|
2263
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
2264
|
+
backgroundColor: "#ffffff",
|
|
2265
|
+
borderRadius: "12px",
|
|
2266
|
+
padding: "24px",
|
|
2267
|
+
maxWidth: "400px",
|
|
2268
|
+
width: "90%",
|
|
2269
|
+
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
2270
|
+
}, children: [
|
|
2271
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
|
|
2272
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "Help us improve by telling us why you're skipping this question." }),
|
|
2273
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
|
|
2274
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2275
|
+
"button",
|
|
2276
|
+
{
|
|
2277
|
+
onClick: () => setSelectedSkipReason("question_issue"),
|
|
2278
|
+
disabled: isSkipping,
|
|
2279
|
+
style: {
|
|
2280
|
+
padding: "12px 16px",
|
|
2281
|
+
borderRadius: "8px",
|
|
2282
|
+
border: selectedSkipReason === "question_issue" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
2283
|
+
backgroundColor: selectedSkipReason === "question_issue" ? "#f5f3ff" : "#f9fafb",
|
|
2284
|
+
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2285
|
+
fontSize: "14px",
|
|
2286
|
+
fontWeight: "500",
|
|
2287
|
+
color: "#374151",
|
|
2288
|
+
textAlign: "left",
|
|
2289
|
+
opacity: isSkipping ? 0.6 : 1
|
|
2290
|
+
},
|
|
2291
|
+
"data-testid": "button-skip-reason-issue",
|
|
2292
|
+
children: "Question has an issue"
|
|
1857
2293
|
}
|
|
1858
2294
|
),
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
style: {
|
|
1876
|
-
...optionStyle,
|
|
1877
|
-
cursor: showFeedback ? "default" : "pointer",
|
|
1878
|
-
display: "flex",
|
|
1879
|
-
alignItems: "center",
|
|
1880
|
-
gap: "8px"
|
|
1881
|
-
},
|
|
1882
|
-
onClick: () => !showFeedback && handleAnswerChange(option),
|
|
1883
|
-
children: [
|
|
1884
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
|
|
1885
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
1886
|
-
]
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2296
|
+
"button",
|
|
2297
|
+
{
|
|
2298
|
+
onClick: () => setSelectedSkipReason("dont_know"),
|
|
2299
|
+
disabled: isSkipping,
|
|
2300
|
+
style: {
|
|
2301
|
+
padding: "12px 16px",
|
|
2302
|
+
borderRadius: "8px",
|
|
2303
|
+
border: selectedSkipReason === "dont_know" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
2304
|
+
backgroundColor: selectedSkipReason === "dont_know" ? "#f5f3ff" : "#f9fafb",
|
|
2305
|
+
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2306
|
+
fontSize: "14px",
|
|
2307
|
+
fontWeight: "500",
|
|
2308
|
+
color: "#374151",
|
|
2309
|
+
textAlign: "left",
|
|
2310
|
+
opacity: isSkipping ? 0.6 : 1
|
|
1887
2311
|
},
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
}) }),
|
|
1891
|
-
currentQuestion.type === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.options, children: currentQuestion.options?.map((option, idx) => {
|
|
1892
|
-
const selected = Array.isArray(selectedAnswer) && selectedAnswer.includes(option);
|
|
1893
|
-
const correctAnswers = Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer : currentQuestion.correctAnswer ? [currentQuestion.correctAnswer] : [];
|
|
1894
|
-
const isCorrectOption = correctAnswers.includes(option);
|
|
1895
|
-
let optionStyle = { ...defaultStyles.option };
|
|
1896
|
-
if (showFeedback) {
|
|
1897
|
-
if (isCorrectOption) {
|
|
1898
|
-
optionStyle = { ...optionStyle, ...defaultStyles.optionCorrect };
|
|
1899
|
-
} else if (selected && !isCorrectOption) {
|
|
1900
|
-
optionStyle = { ...optionStyle, ...defaultStyles.optionIncorrect };
|
|
1901
|
-
}
|
|
1902
|
-
} else if (selected) {
|
|
1903
|
-
optionStyle = { ...optionStyle, ...defaultStyles.optionSelected };
|
|
2312
|
+
"data-testid": "button-skip-reason-dont-know",
|
|
2313
|
+
children: "I don't know the answer"
|
|
1904
2314
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
cursor: showFeedback ? "default" : "pointer",
|
|
1911
|
-
display: "flex",
|
|
1912
|
-
alignItems: "center",
|
|
1913
|
-
gap: "8px"
|
|
1914
|
-
},
|
|
1915
|
-
onClick: () => {
|
|
1916
|
-
if (showFeedback) return;
|
|
1917
|
-
const current = Array.isArray(selectedAnswer) ? selectedAnswer : [];
|
|
1918
|
-
if (selected) {
|
|
1919
|
-
handleAnswerChange(current.filter((o) => o !== option));
|
|
1920
|
-
} else {
|
|
1921
|
-
handleAnswerChange([...current, option]);
|
|
1922
|
-
}
|
|
1923
|
-
},
|
|
1924
|
-
children: [
|
|
1925
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextToSpeech, { text: option, size: "sm" }) }),
|
|
1926
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 }, children: option })
|
|
1927
|
-
]
|
|
1928
|
-
},
|
|
1929
|
-
idx
|
|
1930
|
-
);
|
|
1931
|
-
}) }),
|
|
1932
|
-
(currentQuestion.type === "free" || currentQuestion.type === "essay") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2315
|
+
)
|
|
2316
|
+
] }),
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
|
|
2318
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
|
|
2319
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1933
2320
|
"textarea",
|
|
1934
2321
|
{
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2322
|
+
value: skipComment,
|
|
2323
|
+
onChange: (e) => setSkipComment(e.target.value.slice(0, 200)),
|
|
2324
|
+
placeholder: "Tell us more about the issue...",
|
|
2325
|
+
disabled: isSkipping,
|
|
2326
|
+
style: {
|
|
2327
|
+
width: "100%",
|
|
2328
|
+
minHeight: "80px",
|
|
2329
|
+
padding: "10px 12px",
|
|
2330
|
+
borderRadius: "8px",
|
|
2331
|
+
border: "1px solid #e5e7eb",
|
|
2332
|
+
fontSize: "14px",
|
|
2333
|
+
resize: "vertical",
|
|
2334
|
+
fontFamily: "inherit",
|
|
2335
|
+
boxSizing: "border-box"
|
|
2336
|
+
},
|
|
2337
|
+
"data-testid": "input-skip-comment"
|
|
1940
2338
|
}
|
|
1941
2339
|
),
|
|
1942
|
-
|
|
1943
|
-
|
|
2340
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2341
|
+
skipComment.length,
|
|
2342
|
+
"/200"
|
|
2343
|
+
] })
|
|
2344
|
+
] }),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2346
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2347
|
+
"button",
|
|
1944
2348
|
{
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
current[idx] = e.target.value;
|
|
1950
|
-
handleAnswerChange(current);
|
|
2349
|
+
onClick: () => {
|
|
2350
|
+
setShowSkipModal(false);
|
|
2351
|
+
setSkipComment("");
|
|
2352
|
+
setSelectedSkipReason(null);
|
|
1951
2353
|
},
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2354
|
+
style: {
|
|
2355
|
+
flex: 1,
|
|
2356
|
+
padding: "10px 16px",
|
|
2357
|
+
borderRadius: "8px",
|
|
2358
|
+
border: "1px solid #e5e7eb",
|
|
2359
|
+
backgroundColor: "#ffffff",
|
|
2360
|
+
cursor: "pointer",
|
|
2361
|
+
fontSize: "14px",
|
|
2362
|
+
fontWeight: "500",
|
|
2363
|
+
color: "#6b7280"
|
|
2364
|
+
},
|
|
2365
|
+
"data-testid": "button-skip-cancel",
|
|
2366
|
+
children: "Cancel"
|
|
2367
|
+
}
|
|
2368
|
+
),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2370
|
+
"button",
|
|
2371
|
+
{
|
|
2372
|
+
onClick: () => selectedSkipReason && handleSkipQuestion(selectedSkipReason, skipComment),
|
|
2373
|
+
disabled: isSkipping || !selectedSkipReason,
|
|
2374
|
+
style: {
|
|
2375
|
+
flex: 1,
|
|
2376
|
+
padding: "10px 16px",
|
|
2377
|
+
borderRadius: "8px",
|
|
2378
|
+
border: "none",
|
|
2379
|
+
backgroundColor: selectedSkipReason ? "#8b5cf6" : "#d1d5db",
|
|
2380
|
+
cursor: isSkipping || !selectedSkipReason ? "not-allowed" : "pointer",
|
|
2381
|
+
fontSize: "14px",
|
|
2382
|
+
fontWeight: "500",
|
|
2383
|
+
color: "#ffffff",
|
|
2384
|
+
opacity: isSkipping ? 0.6 : 1
|
|
2385
|
+
},
|
|
2386
|
+
"data-testid": "button-skip-submit",
|
|
2387
|
+
children: isSkipping ? "Skipping..." : "Skip Question"
|
|
2388
|
+
}
|
|
2389
|
+
)
|
|
2390
|
+
] })
|
|
2391
|
+
] }) }),
|
|
2392
|
+
showReportModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
2393
|
+
position: "fixed",
|
|
2394
|
+
top: 0,
|
|
2395
|
+
left: 0,
|
|
2396
|
+
right: 0,
|
|
2397
|
+
bottom: 0,
|
|
2398
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2399
|
+
display: "flex",
|
|
2400
|
+
alignItems: "center",
|
|
2401
|
+
justifyContent: "center",
|
|
2402
|
+
zIndex: 1e3
|
|
2403
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
2404
|
+
backgroundColor: "#ffffff",
|
|
2405
|
+
borderRadius: "12px",
|
|
2406
|
+
padding: "24px",
|
|
2407
|
+
maxWidth: "400px",
|
|
2408
|
+
width: "90%",
|
|
2409
|
+
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
2410
|
+
}, children: [
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
|
|
2413
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
|
|
2414
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2415
|
+
"textarea",
|
|
2416
|
+
{
|
|
2417
|
+
value: reportComment,
|
|
2418
|
+
onChange: (e) => setReportComment(e.target.value.slice(0, 300)),
|
|
2419
|
+
placeholder: "Describe the issue with this question...",
|
|
2420
|
+
disabled: isReporting,
|
|
2421
|
+
style: {
|
|
2422
|
+
width: "100%",
|
|
2423
|
+
minHeight: "120px",
|
|
2424
|
+
padding: "10px 12px",
|
|
2425
|
+
borderRadius: "8px",
|
|
2426
|
+
border: "1px solid #e5e7eb",
|
|
2427
|
+
fontSize: "14px",
|
|
2428
|
+
resize: "vertical",
|
|
2429
|
+
fontFamily: "inherit",
|
|
2430
|
+
boxSizing: "border-box"
|
|
2431
|
+
},
|
|
2432
|
+
"data-testid": "input-report-comment"
|
|
2433
|
+
}
|
|
2434
|
+
),
|
|
2435
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2436
|
+
reportComment.length,
|
|
2437
|
+
"/300"
|
|
1966
2438
|
] })
|
|
1967
2439
|
] }),
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
top: 0,
|
|
1971
|
-
left: 0,
|
|
1972
|
-
right: 0,
|
|
1973
|
-
bottom: 0,
|
|
1974
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
1975
|
-
display: "flex",
|
|
1976
|
-
alignItems: "center",
|
|
1977
|
-
justifyContent: "center",
|
|
1978
|
-
zIndex: 1e3
|
|
1979
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1980
|
-
backgroundColor: "#ffffff",
|
|
1981
|
-
borderRadius: "12px",
|
|
1982
|
-
padding: "24px",
|
|
1983
|
-
maxWidth: "400px",
|
|
1984
|
-
width: "90%",
|
|
1985
|
-
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
1986
|
-
}, children: [
|
|
1987
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Skip Question" }),
|
|
1988
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "Help us improve by telling us why you're skipping this question." }),
|
|
1989
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "16px" }, children: [
|
|
1990
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1991
|
-
"button",
|
|
1992
|
-
{
|
|
1993
|
-
onClick: () => setSelectedSkipReason("question_issue"),
|
|
1994
|
-
disabled: isSkipping,
|
|
1995
|
-
style: {
|
|
1996
|
-
padding: "12px 16px",
|
|
1997
|
-
borderRadius: "8px",
|
|
1998
|
-
border: selectedSkipReason === "question_issue" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
1999
|
-
backgroundColor: selectedSkipReason === "question_issue" ? "#f5f3ff" : "#f9fafb",
|
|
2000
|
-
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2001
|
-
fontSize: "14px",
|
|
2002
|
-
fontWeight: "500",
|
|
2003
|
-
color: "#374151",
|
|
2004
|
-
textAlign: "left",
|
|
2005
|
-
opacity: isSkipping ? 0.6 : 1
|
|
2006
|
-
},
|
|
2007
|
-
"data-testid": "button-skip-reason-issue",
|
|
2008
|
-
children: "Question has an issue"
|
|
2009
|
-
}
|
|
2010
|
-
),
|
|
2011
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2012
|
-
"button",
|
|
2013
|
-
{
|
|
2014
|
-
onClick: () => setSelectedSkipReason("dont_know"),
|
|
2015
|
-
disabled: isSkipping,
|
|
2016
|
-
style: {
|
|
2017
|
-
padding: "12px 16px",
|
|
2018
|
-
borderRadius: "8px",
|
|
2019
|
-
border: selectedSkipReason === "dont_know" ? "2px solid #8b5cf6" : "1px solid #e5e7eb",
|
|
2020
|
-
backgroundColor: selectedSkipReason === "dont_know" ? "#f5f3ff" : "#f9fafb",
|
|
2021
|
-
cursor: isSkipping ? "not-allowed" : "pointer",
|
|
2022
|
-
fontSize: "14px",
|
|
2023
|
-
fontWeight: "500",
|
|
2024
|
-
color: "#374151",
|
|
2025
|
-
textAlign: "left",
|
|
2026
|
-
opacity: isSkipping ? 0.6 : 1
|
|
2027
|
-
},
|
|
2028
|
-
"data-testid": "button-skip-reason-dont-know",
|
|
2029
|
-
children: "I don't know the answer"
|
|
2030
|
-
}
|
|
2031
|
-
)
|
|
2032
|
-
] }),
|
|
2033
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
|
|
2034
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: { display: "block", fontSize: "13px", fontWeight: "500", color: "#374151", marginBottom: "6px" }, children: "Additional details (optional)" }),
|
|
2035
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2036
|
-
"textarea",
|
|
2037
|
-
{
|
|
2038
|
-
value: skipComment,
|
|
2039
|
-
onChange: (e) => setSkipComment(e.target.value.slice(0, 200)),
|
|
2040
|
-
placeholder: "Tell us more about the issue...",
|
|
2041
|
-
disabled: isSkipping,
|
|
2042
|
-
style: {
|
|
2043
|
-
width: "100%",
|
|
2044
|
-
minHeight: "80px",
|
|
2045
|
-
padding: "10px 12px",
|
|
2046
|
-
borderRadius: "8px",
|
|
2047
|
-
border: "1px solid #e5e7eb",
|
|
2048
|
-
fontSize: "14px",
|
|
2049
|
-
resize: "vertical",
|
|
2050
|
-
fontFamily: "inherit",
|
|
2051
|
-
boxSizing: "border-box"
|
|
2052
|
-
},
|
|
2053
|
-
"data-testid": "input-skip-comment"
|
|
2054
|
-
}
|
|
2055
|
-
),
|
|
2056
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2057
|
-
skipComment.length,
|
|
2058
|
-
"/200"
|
|
2059
|
-
] })
|
|
2060
|
-
] }),
|
|
2061
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2062
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2063
|
-
"button",
|
|
2064
|
-
{
|
|
2065
|
-
onClick: () => {
|
|
2066
|
-
setShowSkipModal(false);
|
|
2067
|
-
setSkipComment("");
|
|
2068
|
-
setSelectedSkipReason(null);
|
|
2069
|
-
},
|
|
2070
|
-
style: {
|
|
2071
|
-
flex: 1,
|
|
2072
|
-
padding: "10px 16px",
|
|
2073
|
-
borderRadius: "8px",
|
|
2074
|
-
border: "1px solid #e5e7eb",
|
|
2075
|
-
backgroundColor: "#ffffff",
|
|
2076
|
-
cursor: "pointer",
|
|
2077
|
-
fontSize: "14px",
|
|
2078
|
-
fontWeight: "500",
|
|
2079
|
-
color: "#6b7280"
|
|
2080
|
-
},
|
|
2081
|
-
"data-testid": "button-skip-cancel",
|
|
2082
|
-
children: "Cancel"
|
|
2083
|
-
}
|
|
2084
|
-
),
|
|
2085
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2086
|
-
"button",
|
|
2087
|
-
{
|
|
2088
|
-
onClick: () => selectedSkipReason && handleSkipQuestion(selectedSkipReason, skipComment),
|
|
2089
|
-
disabled: isSkipping || !selectedSkipReason,
|
|
2090
|
-
style: {
|
|
2091
|
-
flex: 1,
|
|
2092
|
-
padding: "10px 16px",
|
|
2093
|
-
borderRadius: "8px",
|
|
2094
|
-
border: "none",
|
|
2095
|
-
backgroundColor: selectedSkipReason ? "#8b5cf6" : "#d1d5db",
|
|
2096
|
-
cursor: isSkipping || !selectedSkipReason ? "not-allowed" : "pointer",
|
|
2097
|
-
fontSize: "14px",
|
|
2098
|
-
fontWeight: "500",
|
|
2099
|
-
color: "#ffffff",
|
|
2100
|
-
opacity: isSkipping ? 0.6 : 1
|
|
2101
|
-
},
|
|
2102
|
-
"data-testid": "button-skip-submit",
|
|
2103
|
-
children: isSkipping ? "Skipping..." : "Skip Question"
|
|
2104
|
-
}
|
|
2105
|
-
)
|
|
2106
|
-
] })
|
|
2107
|
-
] }) }),
|
|
2108
|
-
showReportModal && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
2109
|
-
position: "fixed",
|
|
2110
|
-
top: 0,
|
|
2111
|
-
left: 0,
|
|
2112
|
-
right: 0,
|
|
2113
|
-
bottom: 0,
|
|
2114
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2115
|
-
display: "flex",
|
|
2116
|
-
alignItems: "center",
|
|
2117
|
-
justifyContent: "center",
|
|
2118
|
-
zIndex: 1e3
|
|
2119
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
2120
|
-
backgroundColor: "#ffffff",
|
|
2121
|
-
borderRadius: "12px",
|
|
2122
|
-
padding: "24px",
|
|
2123
|
-
maxWidth: "400px",
|
|
2124
|
-
width: "90%",
|
|
2125
|
-
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)"
|
|
2126
|
-
}, children: [
|
|
2127
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { style: { margin: "0 0 8px 0", fontSize: "18px", fontWeight: "600", color: "#1f2937" }, children: "Report Question" }),
|
|
2128
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: "0 0 16px 0", fontSize: "14px", color: "#6b7280" }, children: "What's wrong with this question?" }),
|
|
2129
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
|
|
2130
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2131
|
-
"textarea",
|
|
2132
|
-
{
|
|
2133
|
-
value: reportComment,
|
|
2134
|
-
onChange: (e) => setReportComment(e.target.value.slice(0, 300)),
|
|
2135
|
-
placeholder: "Describe the issue with this question...",
|
|
2136
|
-
disabled: isReporting,
|
|
2137
|
-
style: {
|
|
2138
|
-
width: "100%",
|
|
2139
|
-
minHeight: "120px",
|
|
2140
|
-
padding: "10px 12px",
|
|
2141
|
-
borderRadius: "8px",
|
|
2142
|
-
border: "1px solid #e5e7eb",
|
|
2143
|
-
fontSize: "14px",
|
|
2144
|
-
resize: "vertical",
|
|
2145
|
-
fontFamily: "inherit",
|
|
2146
|
-
boxSizing: "border-box"
|
|
2147
|
-
},
|
|
2148
|
-
"data-testid": "input-report-comment"
|
|
2149
|
-
}
|
|
2150
|
-
),
|
|
2151
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: "#9ca3af", marginTop: "4px", textAlign: "right" }, children: [
|
|
2152
|
-
reportComment.length,
|
|
2153
|
-
"/300"
|
|
2154
|
-
] })
|
|
2155
|
-
] }),
|
|
2156
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2157
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2158
|
-
"button",
|
|
2159
|
-
{
|
|
2160
|
-
onClick: () => {
|
|
2161
|
-
setShowReportModal(false);
|
|
2162
|
-
setReportComment("");
|
|
2163
|
-
},
|
|
2164
|
-
style: {
|
|
2165
|
-
flex: 1,
|
|
2166
|
-
padding: "10px 16px",
|
|
2167
|
-
borderRadius: "8px",
|
|
2168
|
-
border: "1px solid #e5e7eb",
|
|
2169
|
-
backgroundColor: "#ffffff",
|
|
2170
|
-
cursor: "pointer",
|
|
2171
|
-
fontSize: "14px",
|
|
2172
|
-
fontWeight: "500",
|
|
2173
|
-
color: "#6b7280"
|
|
2174
|
-
},
|
|
2175
|
-
"data-testid": "button-report-cancel",
|
|
2176
|
-
children: "Cancel"
|
|
2177
|
-
}
|
|
2178
|
-
),
|
|
2179
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2180
|
-
"button",
|
|
2181
|
-
{
|
|
2182
|
-
onClick: () => handleReportQuestion(reportComment),
|
|
2183
|
-
disabled: isReporting || !reportComment.trim(),
|
|
2184
|
-
style: {
|
|
2185
|
-
flex: 1,
|
|
2186
|
-
padding: "10px 16px",
|
|
2187
|
-
borderRadius: "8px",
|
|
2188
|
-
border: "none",
|
|
2189
|
-
backgroundColor: reportComment.trim() ? "#ef4444" : "#d1d5db",
|
|
2190
|
-
cursor: isReporting || !reportComment.trim() ? "not-allowed" : "pointer",
|
|
2191
|
-
fontSize: "14px",
|
|
2192
|
-
fontWeight: "500",
|
|
2193
|
-
color: "#ffffff",
|
|
2194
|
-
opacity: isReporting ? 0.6 : 1
|
|
2195
|
-
},
|
|
2196
|
-
"data-testid": "button-report-submit",
|
|
2197
|
-
children: isReporting ? "Reporting..." : "Report"
|
|
2198
|
-
}
|
|
2199
|
-
)
|
|
2200
|
-
] })
|
|
2201
|
-
] }) }),
|
|
2202
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.buttonsColumn, children: [
|
|
2203
|
-
showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2204
2442
|
"button",
|
|
2205
2443
|
{
|
|
2444
|
+
onClick: () => {
|
|
2445
|
+
setShowReportModal(false);
|
|
2446
|
+
setReportComment("");
|
|
2447
|
+
},
|
|
2206
2448
|
style: {
|
|
2207
|
-
|
|
2208
|
-
|
|
2449
|
+
flex: 1,
|
|
2450
|
+
padding: "10px 16px",
|
|
2451
|
+
borderRadius: "8px",
|
|
2452
|
+
border: "1px solid #e5e7eb",
|
|
2453
|
+
backgroundColor: "#ffffff",
|
|
2454
|
+
cursor: "pointer",
|
|
2455
|
+
fontSize: "14px",
|
|
2456
|
+
fontWeight: "500",
|
|
2457
|
+
color: "#6b7280"
|
|
2209
2458
|
},
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
"data-testid": "button-add-more-questions",
|
|
2213
|
-
children: isGeneratingExtra ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2214
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }),
|
|
2215
|
-
"Generating Questions..."
|
|
2216
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2217
|
-
"+ Add ",
|
|
2218
|
-
questionsToAdd,
|
|
2219
|
-
" More Question",
|
|
2220
|
-
questionsToAdd !== 1 ? "s" : ""
|
|
2221
|
-
] })
|
|
2459
|
+
"data-testid": "button-report-cancel",
|
|
2460
|
+
children: "Cancel"
|
|
2222
2461
|
}
|
|
2223
2462
|
),
|
|
2224
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
"
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
onClick: handleContinue,
|
|
2246
|
-
"data-testid": "button-continue",
|
|
2247
|
-
children: "Continue"
|
|
2248
|
-
}
|
|
2249
|
-
)
|
|
2250
|
-
) : (
|
|
2251
|
-
// Before checking answer
|
|
2252
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2253
|
-
"button",
|
|
2254
|
-
{
|
|
2255
|
-
style: {
|
|
2256
|
-
...defaultStyles.button,
|
|
2257
|
-
...isNavigating || selectedAnswer === void 0 ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
|
|
2258
|
-
},
|
|
2259
|
-
onClick: handleCheckAnswer,
|
|
2260
|
-
disabled: isNavigating || selectedAnswer === void 0,
|
|
2261
|
-
"data-testid": "button-check-answer",
|
|
2262
|
-
children: isNavigating ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
|
|
2263
|
-
}
|
|
2264
|
-
)
|
|
2265
|
-
) })
|
|
2463
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2464
|
+
"button",
|
|
2465
|
+
{
|
|
2466
|
+
onClick: () => handleReportQuestion(reportComment),
|
|
2467
|
+
disabled: isReporting || !reportComment.trim(),
|
|
2468
|
+
style: {
|
|
2469
|
+
flex: 1,
|
|
2470
|
+
padding: "10px 16px",
|
|
2471
|
+
borderRadius: "8px",
|
|
2472
|
+
border: "none",
|
|
2473
|
+
backgroundColor: reportComment.trim() ? "#ef4444" : "#d1d5db",
|
|
2474
|
+
cursor: isReporting || !reportComment.trim() ? "not-allowed" : "pointer",
|
|
2475
|
+
fontSize: "14px",
|
|
2476
|
+
fontWeight: "500",
|
|
2477
|
+
color: "#ffffff",
|
|
2478
|
+
opacity: isReporting ? 0.6 : 1
|
|
2479
|
+
},
|
|
2480
|
+
"data-testid": "button-report-submit",
|
|
2481
|
+
children: isReporting ? "Reporting..." : "Report"
|
|
2482
|
+
}
|
|
2483
|
+
)
|
|
2266
2484
|
] })
|
|
2267
|
-
] }),
|
|
2268
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2485
|
+
] }) }),
|
|
2486
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: defaultStyles.buttonsColumn, children: [
|
|
2487
|
+
showFeedback && isLastQuestion && canAddMore && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2488
|
+
"button",
|
|
2489
|
+
{
|
|
2490
|
+
style: {
|
|
2491
|
+
...defaultStyles.buttonAddMore,
|
|
2492
|
+
...isGeneratingExtra ? defaultStyles.buttonAddMoreDisabled : {}
|
|
2493
|
+
},
|
|
2494
|
+
onClick: handleAddMoreQuestions,
|
|
2495
|
+
disabled: isGeneratingExtra,
|
|
2496
|
+
"data-testid": "button-add-more-questions",
|
|
2497
|
+
children: isGeneratingExtra ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }),
|
|
2499
|
+
"Generating Questions..."
|
|
2500
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2501
|
+
"+ Add ",
|
|
2502
|
+
questionsToAdd,
|
|
2503
|
+
" More Question",
|
|
2504
|
+
questionsToAdd !== 1 ? "s" : ""
|
|
2505
|
+
] })
|
|
2506
|
+
}
|
|
2507
|
+
),
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultStyles.buttons, justifyContent: "flex-end" }, children: showFeedback ? (
|
|
2509
|
+
// After viewing feedback
|
|
2510
|
+
isLastQuestion ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2511
|
+
"button",
|
|
2512
|
+
{
|
|
2513
|
+
style: {
|
|
2514
|
+
...defaultStyles.button,
|
|
2515
|
+
...isSubmitting || isGeneratingExtra ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
|
|
2516
|
+
},
|
|
2517
|
+
onClick: handleSubmit,
|
|
2518
|
+
disabled: isSubmitting || isGeneratingExtra,
|
|
2519
|
+
"data-testid": "button-submit-quiz",
|
|
2520
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Submit Quiz"
|
|
2521
|
+
}
|
|
2522
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2523
|
+
"button",
|
|
2524
|
+
{
|
|
2525
|
+
style: {
|
|
2526
|
+
...defaultStyles.button,
|
|
2527
|
+
...defaultStyles.buttonPrimary
|
|
2528
|
+
},
|
|
2529
|
+
onClick: handleContinue,
|
|
2530
|
+
"data-testid": "button-continue",
|
|
2531
|
+
children: "Continue"
|
|
2532
|
+
}
|
|
2533
|
+
)
|
|
2534
|
+
) : (
|
|
2535
|
+
// Before checking answer
|
|
2536
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2537
|
+
"button",
|
|
2538
|
+
{
|
|
2539
|
+
style: {
|
|
2540
|
+
...defaultStyles.button,
|
|
2541
|
+
...isNavigating || selectedAnswer === void 0 ? defaultStyles.buttonDisabled : defaultStyles.buttonPrimary
|
|
2542
|
+
},
|
|
2543
|
+
onClick: handleCheckAnswer,
|
|
2544
|
+
disabled: isNavigating || selectedAnswer === void 0,
|
|
2545
|
+
"data-testid": "button-check-answer",
|
|
2546
|
+
children: isNavigating ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { size: 16, color: "#9ca3af" }) : "Check Answer"
|
|
2547
|
+
}
|
|
2548
|
+
)
|
|
2549
|
+
) })
|
|
2550
|
+
] })
|
|
2551
|
+
] }),
|
|
2552
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: defaultStyles.chatPanel, children: apiClient.current && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2553
|
+
QuestionChatPanel,
|
|
2554
|
+
{
|
|
2555
|
+
apiClient: apiClient.current,
|
|
2556
|
+
question: {
|
|
2557
|
+
id: currentQuestion.id,
|
|
2558
|
+
question: currentQuestion.question,
|
|
2559
|
+
type: currentQuestion.type,
|
|
2560
|
+
options: currentQuestion.options,
|
|
2561
|
+
correctAnswer: currentQuestion.correctAnswer,
|
|
2562
|
+
explanation: currentQuestion.explanation
|
|
2563
|
+
},
|
|
2564
|
+
quizId: quiz.id,
|
|
2565
|
+
childId,
|
|
2566
|
+
parentId,
|
|
2567
|
+
lessonId,
|
|
2568
|
+
courseId,
|
|
2569
|
+
answerResult: showFeedback && currentAnswerDetail ? {
|
|
2570
|
+
wasIncorrect: !currentAnswerDetail.isCorrect,
|
|
2571
|
+
selectedAnswer: typeof selectedAnswer === "string" ? selectedAnswer : Array.isArray(selectedAnswer) ? selectedAnswer.join(", ") : void 0,
|
|
2572
|
+
correctAnswer: typeof currentQuestion.correctAnswer === "string" ? currentQuestion.correctAnswer : Array.isArray(currentQuestion.correctAnswer) ? currentQuestion.correctAnswer.join(", ") : void 0,
|
|
2573
|
+
explanation: currentQuestion.explanation
|
|
2574
|
+
} : void 0
|
|
2575
|
+
}
|
|
2576
|
+
) })
|
|
2577
|
+
] }) });
|
|
2289
2578
|
}
|
|
2290
2579
|
|
|
2291
2580
|
// src/AttemptViewer.tsx
|
|
@@ -2411,6 +2700,46 @@ var defaultStyles2 = {
|
|
|
2411
2700
|
fontSize: "14px",
|
|
2412
2701
|
color: "#581c87"
|
|
2413
2702
|
},
|
|
2703
|
+
chatHistorySection: {
|
|
2704
|
+
marginTop: "12px",
|
|
2705
|
+
borderTop: "1px solid #e5e7eb",
|
|
2706
|
+
paddingTop: "12px"
|
|
2707
|
+
},
|
|
2708
|
+
chatToggleButton: {
|
|
2709
|
+
display: "flex",
|
|
2710
|
+
alignItems: "center",
|
|
2711
|
+
gap: "6px",
|
|
2712
|
+
padding: "6px 12px",
|
|
2713
|
+
backgroundColor: "#f3f4f6",
|
|
2714
|
+
border: "none",
|
|
2715
|
+
borderRadius: "6px",
|
|
2716
|
+
fontSize: "13px",
|
|
2717
|
+
color: "#6b7280",
|
|
2718
|
+
cursor: "pointer",
|
|
2719
|
+
fontWeight: "500"
|
|
2720
|
+
},
|
|
2721
|
+
chatMessages: {
|
|
2722
|
+
marginTop: "12px",
|
|
2723
|
+
display: "flex",
|
|
2724
|
+
flexDirection: "column",
|
|
2725
|
+
gap: "8px"
|
|
2726
|
+
},
|
|
2727
|
+
chatMessage: {
|
|
2728
|
+
padding: "8px 12px",
|
|
2729
|
+
borderRadius: "8px",
|
|
2730
|
+
fontSize: "13px",
|
|
2731
|
+
maxWidth: "85%"
|
|
2732
|
+
},
|
|
2733
|
+
chatMessageUser: {
|
|
2734
|
+
backgroundColor: "#6721b0",
|
|
2735
|
+
color: "#ffffff",
|
|
2736
|
+
alignSelf: "flex-end"
|
|
2737
|
+
},
|
|
2738
|
+
chatMessageAssistant: {
|
|
2739
|
+
backgroundColor: "#f3f4f6",
|
|
2740
|
+
color: "#111827",
|
|
2741
|
+
alignSelf: "flex-start"
|
|
2742
|
+
},
|
|
2414
2743
|
loading: {
|
|
2415
2744
|
textAlign: "center",
|
|
2416
2745
|
padding: "40px 20px"
|
|
@@ -2468,11 +2797,14 @@ function AttemptViewer({
|
|
|
2468
2797
|
onError,
|
|
2469
2798
|
className,
|
|
2470
2799
|
showExplanations = true,
|
|
2800
|
+
showConversation = false,
|
|
2471
2801
|
title
|
|
2472
2802
|
}) {
|
|
2473
2803
|
const [attempt, setAttempt] = (0, import_react4.useState)(null);
|
|
2474
2804
|
const [loading, setLoading] = (0, import_react4.useState)(true);
|
|
2475
2805
|
const [error, setError] = (0, import_react4.useState)(null);
|
|
2806
|
+
const [chatHistories, setChatHistories] = (0, import_react4.useState)({});
|
|
2807
|
+
const [expandedChats, setExpandedChats] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
|
|
2476
2808
|
(0, import_react4.useEffect)(() => {
|
|
2477
2809
|
const apiClient = new QuizApiClient({
|
|
2478
2810
|
baseUrl: apiBaseUrl,
|
|
@@ -2490,6 +2822,14 @@ function AttemptViewer({
|
|
|
2490
2822
|
}
|
|
2491
2823
|
const data = await response.json();
|
|
2492
2824
|
setAttempt(data);
|
|
2825
|
+
if (showConversation) {
|
|
2826
|
+
try {
|
|
2827
|
+
const chats = await apiClient.getChatsByAttempt(attemptId);
|
|
2828
|
+
setChatHistories(chats);
|
|
2829
|
+
} catch (chatErr) {
|
|
2830
|
+
console.error("Failed to load chat histories:", chatErr);
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2493
2833
|
} catch (err) {
|
|
2494
2834
|
const errorMessage = err instanceof Error ? err.message : "Failed to load attempt";
|
|
2495
2835
|
setError(errorMessage);
|
|
@@ -2499,7 +2839,18 @@ function AttemptViewer({
|
|
|
2499
2839
|
}
|
|
2500
2840
|
}
|
|
2501
2841
|
fetchAttempt();
|
|
2502
|
-
}, [attemptId, apiBaseUrl, authToken, onError]);
|
|
2842
|
+
}, [attemptId, apiBaseUrl, authToken, onError, showConversation]);
|
|
2843
|
+
const toggleChatExpanded = (questionId) => {
|
|
2844
|
+
setExpandedChats((prev) => {
|
|
2845
|
+
const newSet = new Set(prev);
|
|
2846
|
+
if (newSet.has(questionId)) {
|
|
2847
|
+
newSet.delete(questionId);
|
|
2848
|
+
} else {
|
|
2849
|
+
newSet.add(questionId);
|
|
2850
|
+
}
|
|
2851
|
+
return newSet;
|
|
2852
|
+
});
|
|
2853
|
+
};
|
|
2503
2854
|
const handleRetry = () => {
|
|
2504
2855
|
setLoading(true);
|
|
2505
2856
|
setError(null);
|
|
@@ -2591,6 +2942,34 @@ function AttemptViewer({
|
|
|
2591
2942
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "Explanation:" }),
|
|
2592
2943
|
" ",
|
|
2593
2944
|
answer.explanation
|
|
2945
|
+
] }),
|
|
2946
|
+
showConversation && chatHistories[answer.questionId] && chatHistories[answer.questionId].messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: defaultStyles2.chatHistorySection, children: [
|
|
2947
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2948
|
+
"button",
|
|
2949
|
+
{
|
|
2950
|
+
style: defaultStyles2.chatToggleButton,
|
|
2951
|
+
onClick: () => toggleChatExpanded(answer.questionId),
|
|
2952
|
+
"data-testid": `button-toggle-chat-${answer.questionId}`,
|
|
2953
|
+
children: [
|
|
2954
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }),
|
|
2955
|
+
expandedChats.has(answer.questionId) ? "Hide" : "View",
|
|
2956
|
+
" Chat History (",
|
|
2957
|
+
chatHistories[answer.questionId].messages.length,
|
|
2958
|
+
" messages)"
|
|
2959
|
+
]
|
|
2960
|
+
}
|
|
2961
|
+
),
|
|
2962
|
+
expandedChats.has(answer.questionId) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: defaultStyles2.chatMessages, children: chatHistories[answer.questionId].messages.map((msg, msgIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2963
|
+
"div",
|
|
2964
|
+
{
|
|
2965
|
+
style: {
|
|
2966
|
+
...defaultStyles2.chatMessage,
|
|
2967
|
+
...msg.role === "user" ? defaultStyles2.chatMessageUser : defaultStyles2.chatMessageAssistant
|
|
2968
|
+
},
|
|
2969
|
+
children: msg.content
|
|
2970
|
+
},
|
|
2971
|
+
msgIndex
|
|
2972
|
+
)) })
|
|
2594
2973
|
] })
|
|
2595
2974
|
]
|
|
2596
2975
|
},
|