aakashbansal commited on
Commit
bc741e4
·
1 Parent(s): 6dfc146

the text that says, it looks like you've tried all other options should

Browse files
Files changed (1) hide show
  1. src/components/chatbot/chatbot.tsx +72 -32
src/components/chatbot/chatbot.tsx CHANGED
@@ -39,7 +39,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
39
 
40
 
41
  useEffect(() => {
42
- let ignore = false;
43
 
44
  setMessages([]);
45
  setCurrentMCQ(null);
@@ -47,7 +47,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
47
  setIsAwaitingAnswer(false);
48
  setHasAnsweredCorrectly(false);
49
  setIncorrectAttempts([]);
50
- isInitialQuestionRef.current = true;
51
 
52
  const performInitialFetch = async () => {
53
  if (!imageDataUri) {
@@ -66,7 +66,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
66
 
67
  let activeMcqData: ActiveMCQ = {
68
  ...mcqResult,
69
- originalQuestionTextForFlow: mcqResult.mcq,
70
  };
71
 
72
  if (isInitialQuestionRef.current) {
@@ -94,14 +94,25 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
94
  if (!ignore) setIsLoading(false);
95
  }
96
  };
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- performInitialFetch();
99
 
100
  return () => {
101
  ignore = true;
102
  };
103
  // eslint-disable-next-line react-hooks/exhaustive-deps
104
- }, [imageDataUri, journeyTitle, addMessage, toast]);
105
 
106
 
107
  useEffect(() => {
@@ -140,32 +151,52 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
140
  }
141
  setHasAnsweredCorrectly(true);
142
  setIncorrectAttempts([]);
143
- } else {
144
  const updatedIncorrectAttempts = [...incorrectAttempts, option];
145
  setIncorrectAttempts(updatedIncorrectAttempts);
146
 
147
  if (updatedIncorrectAttempts.length >= currentMCQ.options.length - 1) {
148
- addMessage({
149
- sender: 'ai',
150
- type: 'feedback',
151
- text: `It looks like you've tried all other options. The correct answer is "${currentMCQ.correctAnswer}".`,
152
- isCorrect: true,
153
- });
 
 
 
 
 
 
 
 
 
 
 
 
154
  try {
155
- const explanationResult = await explainCorrectAnswer({
156
  question: questionForAI,
157
  options: currentMCQ.options,
158
  correctAnswer: currentMCQ.correctAnswer,
159
  });
160
- addMessage({ sender: 'ai', type: 'feedback', text: explanationResult.explanation, isCorrect: true });
161
  } catch (error) {
162
  console.error("Error fetching correct answer explanation after exhausting options:", error);
163
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
164
- addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't provide an explanation for that. ${errorMsg}` });
165
  }
 
 
 
 
 
 
 
166
  setHasAnsweredCorrectly(true);
167
  setIncorrectAttempts([]);
168
- } else {
 
169
  try {
170
  const explanationResult = await explainIncorrectAnswer({
171
  question: questionForAI,
@@ -176,25 +207,21 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
176
 
177
  const repromptText = `That's not quite right. ${explanationResult.explanation} Would you like to try again?`;
178
  const repromptMCQData: ActiveMCQ = {
179
- ...currentMCQ,
180
- mcq: repromptText,
181
  };
182
 
183
  addMessage({ sender: 'ai', type: 'mcq', mcq: repromptMCQData });
184
- setCurrentMCQ(repromptMCQData); // Update currentMCQ to the reprompt version
185
  setIsAwaitingAnswer(true);
186
 
187
  } catch (error) {
188
  console.error("Error fetching explanation for incorrect answer:", error);
189
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
190
  addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't explain that. ${errorMsg}. Please try another option from the question above.` });
191
- // Fallback: re-enable options for the original currentMCQ if explanation fails
192
- // We need to ensure the options are re-enabled for the *last presented* mcq
193
- // This part is tricky if the explanation fails BEFORE the new message is added.
194
- // For now, let's assume the error message is enough and user can try again on the current active mcq.
195
- // To be safe, re-add the current (pre-error) mcq to re-enable its options
196
- addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ });
197
- setIsAwaitingAnswer(true); // Re-enable options for the MCQ instance already in messages
198
  }
199
  }
200
  }
@@ -204,7 +231,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
204
  const handleNextQuestionClick = async () => {
205
  setIsAwaitingAnswer(false);
206
  setHasAnsweredCorrectly(false);
207
- setCurrentMCQ(null);
208
  setIncorrectAttempts([]);
209
 
210
  if (!imageDataUri) {
@@ -216,7 +243,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
216
  const mcqResult = await generateMCQ({ imageDataUri });
217
  const nextMCQData: ActiveMCQ = {
218
  ...mcqResult,
219
- originalQuestionTextForFlow: mcqResult.mcq,
220
  };
221
  setCurrentMCQ(nextMCQData);
222
  addMessage({ sender: 'ai', type: 'mcq', mcq: nextMCQData });
@@ -244,14 +271,19 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
244
  <ScrollArea className="flex-1 p-4" ref={scrollAreaRef}>
245
  <div className="space-y-4">
246
  {messages.map((msg, index) => {
 
 
 
247
  const isThisMessageTheCurrentMCQ =
248
  msg.type === 'mcq' &&
249
  msg.mcq &&
250
  currentMCQ &&
251
- msg.mcq.mcq === currentMCQ.mcq && // This comparison should now work due to setCurrentMCQ(repromptMCQData)
252
  JSON.stringify(msg.mcq.options) === JSON.stringify(currentMCQ.options) &&
253
  msg.mcq.correctAnswer === currentMCQ.correctAnswer;
254
 
 
 
255
  const lastInstanceOfCurrentMCQIndex = messages.findLastIndex(
256
  (m) =>
257
  m.type === 'mcq' &&
@@ -262,6 +294,9 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
262
  m.mcq.correctAnswer === currentMCQ.correctAnswer
263
  );
264
 
 
 
 
265
  const shouldThisMessageBeInteractive =
266
  isThisMessageTheCurrentMCQ &&
267
  index === lastInstanceOfCurrentMCQIndex &&
@@ -272,11 +307,11 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
272
  <ChatMessage
273
  key={msg.id}
274
  message={msg}
275
- activeMCQ={currentMCQ}
276
  isAwaitingActiveMCQAnswer={shouldThisMessageBeInteractive}
277
  onOptionSelectActiveMCQ={handleOptionSelect}
278
  incorrectAttemptsForMCQ={
279
- shouldThisMessageBeInteractive
280
  ? incorrectAttempts
281
  : []
282
  }
@@ -284,6 +319,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
284
  );
285
  })}
286
  {isLoading && !currentMCQ && messages[messages.length -1]?.type !== 'feedback' && (
 
287
  <div className="flex items-start space-x-3">
288
  <Skeleton className="h-8 w-8 rounded-full" />
289
  <div className="space-y-2">
@@ -303,18 +339,22 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
303
  </Button>
304
  )}
305
 
 
306
  {!isLoading && currentMCQ && !hasAnsweredCorrectly && isAwaitingAnswer && (
307
  <p className="text-center text-sm text-muted-foreground">
308
  Please choose an answer from the options above.
309
  </p>
310
  )}
311
 
312
- {!imageDataUri && !isLoading && (
 
313
  <p className="text-center text-sm text-muted-foreground">Loading image, please wait...</p>
314
  )}
315
  {!currentMCQ && !isLoading && imageDataUri && messages.length === 0 && (
 
316
  <p className="text-center text-sm text-muted-foreground">Loading first question...</p>
317
  )}
 
318
  {!currentMCQ && !isLoading && imageDataUri && messages.length > 0 && messages[messages.length -1]?.type === 'error' && (
319
  <p className="text-center text-sm text-destructive">Could not load question. Try refreshing.</p>
320
  )}
 
39
 
40
 
41
  useEffect(() => {
42
+ let ignore = false;
43
 
44
  setMessages([]);
45
  setCurrentMCQ(null);
 
47
  setIsAwaitingAnswer(false);
48
  setHasAnsweredCorrectly(false);
49
  setIncorrectAttempts([]);
50
+ isInitialQuestionRef.current = true; // Reset for new journey
51
 
52
  const performInitialFetch = async () => {
53
  if (!imageDataUri) {
 
66
 
67
  let activeMcqData: ActiveMCQ = {
68
  ...mcqResult,
69
+ originalQuestionTextForFlow: mcqResult.mcq,
70
  };
71
 
72
  if (isInitialQuestionRef.current) {
 
94
  if (!ignore) setIsLoading(false);
95
  }
96
  };
97
+
98
+ // If imageDataUri is present, fetch immediately.
99
+ // Otherwise, the effect will re-run when imageDataUri becomes available.
100
+ if (imageDataUri) {
101
+ performInitialFetch();
102
+ } else {
103
+ // Add a placeholder message if image data is not yet ready.
104
+ // This handles the case where journey page loads but imageToDataUri is still pending.
105
+ if (messages.length === 0) { // Only add if no messages exist
106
+ addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
107
+ }
108
+ }
109
 
 
110
 
111
  return () => {
112
  ignore = true;
113
  };
114
  // eslint-disable-next-line react-hooks/exhaustive-deps
115
+ }, [imageDataUri, journeyTitle, addMessage, toast]); // addMessage and toast are stable
116
 
117
 
118
  useEffect(() => {
 
151
  }
152
  setHasAnsweredCorrectly(true);
153
  setIncorrectAttempts([]);
154
+ } else { // Incorrect answer
155
  const updatedIncorrectAttempts = [...incorrectAttempts, option];
156
  setIncorrectAttempts(updatedIncorrectAttempts);
157
 
158
  if (updatedIncorrectAttempts.length >= currentMCQ.options.length - 1) {
159
+ // All incorrect options have been tried
160
+ let combinedText = "";
161
+ try {
162
+ const incorrectExplanationResult = await explainIncorrectAnswer({
163
+ question: questionForAI,
164
+ options: currentMCQ.options,
165
+ selectedAnswer: option, // The last incorrect option chosen by the user
166
+ correctAnswer: currentMCQ.correctAnswer,
167
+ });
168
+ combinedText += `That's not quite right. ${incorrectExplanationResult.explanation} `;
169
+ } catch (error) {
170
+ console.error("Error fetching explanation for the last incorrect answer:", error);
171
+ const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
172
+ combinedText += `That's not quite right. (Failed to get explanation for your choice: ${errorMsg}) `;
173
+ }
174
+
175
+ combinedText += `The correct answer is "${currentMCQ.correctAnswer}". `;
176
+
177
  try {
178
+ const correctExplanationResult = await explainCorrectAnswer({
179
  question: questionForAI,
180
  options: currentMCQ.options,
181
  correctAnswer: currentMCQ.correctAnswer,
182
  });
183
+ combinedText += correctExplanationResult.explanation;
184
  } catch (error) {
185
  console.error("Error fetching correct answer explanation after exhausting options:", error);
186
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
187
+ combinedText += `(Failed to get explanation for the correct answer: ${errorMsg})`;
188
  }
189
+
190
+ addMessage({
191
+ sender: 'ai',
192
+ type: 'feedback',
193
+ text: combinedText.trim(),
194
+ isCorrect: true, // Mark as correct to enable "Next Question"
195
+ });
196
  setHasAnsweredCorrectly(true);
197
  setIncorrectAttempts([]);
198
+
199
+ } else { // Still other options left to try
200
  try {
201
  const explanationResult = await explainIncorrectAnswer({
202
  question: questionForAI,
 
207
 
208
  const repromptText = `That's not quite right. ${explanationResult.explanation} Would you like to try again?`;
209
  const repromptMCQData: ActiveMCQ = {
210
+ ...currentMCQ, // Carries over options, correctAnswer, originalQuestionTextForFlow
211
+ mcq: repromptText, // This is the new display text for the question part of the message
212
  };
213
 
214
  addMessage({ sender: 'ai', type: 'mcq', mcq: repromptMCQData });
215
+ setCurrentMCQ(repromptMCQData); // Update currentMCQ to the reprompt version with new text
216
  setIsAwaitingAnswer(true);
217
 
218
  } catch (error) {
219
  console.error("Error fetching explanation for incorrect answer:", error);
220
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
221
  addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't explain that. ${errorMsg}. Please try another option from the question above.` });
222
+ // Fallback to re-activate the original MCQ if explanation fails during re-prompt
223
+ addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ }); // Re-add currentMCQ to make its options active
224
+ setIsAwaitingAnswer(true);
 
 
 
 
225
  }
226
  }
227
  }
 
231
  const handleNextQuestionClick = async () => {
232
  setIsAwaitingAnswer(false);
233
  setHasAnsweredCorrectly(false);
234
+ setCurrentMCQ(null); // Clear currentMCQ before fetching a new one
235
  setIncorrectAttempts([]);
236
 
237
  if (!imageDataUri) {
 
243
  const mcqResult = await generateMCQ({ imageDataUri });
244
  const nextMCQData: ActiveMCQ = {
245
  ...mcqResult,
246
+ originalQuestionTextForFlow: mcqResult.mcq, // Store original question
247
  };
248
  setCurrentMCQ(nextMCQData);
249
  addMessage({ sender: 'ai', type: 'mcq', mcq: nextMCQData });
 
271
  <ScrollArea className="flex-1 p-4" ref={scrollAreaRef}>
272
  <div className="space-y-4">
273
  {messages.map((msg, index) => {
274
+ // Determine if this message instance is the *current* active MCQ
275
+ // whose options should be interactive. This relies on currentMCQ state having the
276
+ // updated mcq text for re-prompts.
277
  const isThisMessageTheCurrentMCQ =
278
  msg.type === 'mcq' &&
279
  msg.mcq &&
280
  currentMCQ &&
281
+ msg.mcq.mcq === currentMCQ.mcq && // Match the display text
282
  JSON.stringify(msg.mcq.options) === JSON.stringify(currentMCQ.options) &&
283
  msg.mcq.correctAnswer === currentMCQ.correctAnswer;
284
 
285
+ // Find the index of the *last* occurrence of the currentMCQ in the messages array.
286
+ // This ensures only the latest re-prompt of a question is interactive.
287
  const lastInstanceOfCurrentMCQIndex = messages.findLastIndex(
288
  (m) =>
289
  m.type === 'mcq' &&
 
294
  m.mcq.correctAnswer === currentMCQ.correctAnswer
295
  );
296
 
297
+ // Options should be interactive if this message is the current one,
298
+ // it's the last time this exact MCQ content appears, we're awaiting an answer,
299
+ // and it hasn't been answered correctly yet.
300
  const shouldThisMessageBeInteractive =
301
  isThisMessageTheCurrentMCQ &&
302
  index === lastInstanceOfCurrentMCQIndex &&
 
307
  <ChatMessage
308
  key={msg.id}
309
  message={msg}
310
+ activeMCQ={currentMCQ} // Pass currentMCQ from chatbot state
311
  isAwaitingActiveMCQAnswer={shouldThisMessageBeInteractive}
312
  onOptionSelectActiveMCQ={handleOptionSelect}
313
  incorrectAttemptsForMCQ={
314
+ shouldThisMessageBeInteractive // Only pass attempts for the currently interactive MCQ
315
  ? incorrectAttempts
316
  : []
317
  }
 
319
  );
320
  })}
321
  {isLoading && !currentMCQ && messages[messages.length -1]?.type !== 'feedback' && (
322
+ // Skeleton for initial load or "Next Question" load before MCQ is ready
323
  <div className="flex items-start space-x-3">
324
  <Skeleton className="h-8 w-8 rounded-full" />
325
  <div className="space-y-2">
 
339
  </Button>
340
  )}
341
 
342
+ {/* Show "Please choose an answer" only if an MCQ is present, not yet answered correctly, and we are awaiting an answer */}
343
  {!isLoading && currentMCQ && !hasAnsweredCorrectly && isAwaitingAnswer && (
344
  <p className="text-center text-sm text-muted-foreground">
345
  Please choose an answer from the options above.
346
  </p>
347
  )}
348
 
349
+ {/* Initial state messages */}
350
+ {!imageDataUri && !isLoading && messages.length > 0 && messages[messages.length -1]?.text === "Preparing your journey..." &&(
351
  <p className="text-center text-sm text-muted-foreground">Loading image, please wait...</p>
352
  )}
353
  {!currentMCQ && !isLoading && imageDataUri && messages.length === 0 && (
354
+ // This covers the brief moment after image is loaded but before first MCQ is fetched by useEffect
355
  <p className="text-center text-sm text-muted-foreground">Loading first question...</p>
356
  )}
357
+ {/* Error state for loading question */}
358
  {!currentMCQ && !isLoading && imageDataUri && messages.length > 0 && messages[messages.length -1]?.type === 'error' && (
359
  <p className="text-center text-sm text-destructive">Could not load question. Try refreshing.</p>
360
  )}