aakashbansal commited on
Commit
003df95
·
1 Parent(s): f84f6d2

the same that's correct well done message should show the rationale as w

Browse files
Files changed (1) hide show
  1. src/components/chatbot/chatbot.tsx +35 -42
src/components/chatbot/chatbot.tsx CHANGED
@@ -41,17 +41,20 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
41
  useEffect(() => {
42
  let ignore = false;
43
 
 
44
  setMessages([]);
45
  setCurrentMCQ(null);
46
  setIsLoading(false);
47
  setIsAwaitingAnswer(false);
48
  setHasAnsweredCorrectly(false);
49
  setIncorrectAttempts([]);
50
- isInitialQuestionRef.current = true; // Reset for new journey
 
 
51
 
52
  const performInitialFetch = async () => {
53
  if (!imageDataUri) {
54
- if (!ignore) {
55
  addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
56
  }
57
  return;
@@ -66,7 +69,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
66
 
67
  let activeMcqData: ActiveMCQ = {
68
  ...mcqResult,
69
- originalQuestionTextForFlow: mcqResult.mcq,
70
  };
71
 
72
  if (isInitialQuestionRef.current) {
@@ -95,16 +98,14 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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
 
@@ -112,7 +113,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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(() => {
@@ -136,19 +137,20 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
136
  const questionForAI = currentMCQ.originalQuestionTextForFlow || currentMCQ.mcq;
137
 
138
  if (isCorrect) {
139
- addMessage({ sender: 'ai', type: 'feedback', text: "That's correct! Well done.", isCorrect: true });
140
  try {
141
  const explanationResult = await explainCorrectAnswer({
142
  question: questionForAI,
143
  options: currentMCQ.options,
144
  correctAnswer: currentMCQ.correctAnswer,
145
  });
146
- addMessage({ sender: 'ai', type: 'feedback', text: explanationResult.explanation, isCorrect: true });
147
  } catch (error) {
148
  console.error("Error fetching correct answer explanation:", error);
149
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
150
- addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't provide an explanation for that. ${errorMsg}` });
151
  }
 
152
  setHasAnsweredCorrectly(true);
153
  setIncorrectAttempts([]);
154
  } else { // Incorrect answer
@@ -162,7 +164,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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} `;
@@ -191,7 +193,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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([]);
@@ -207,21 +209,25 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,7 +237,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,7 +249,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,19 +277,14 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,9 +295,6 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,11 +305,11 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,7 +317,6 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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,22 +336,18 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
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
  )}
 
41
  useEffect(() => {
42
  let ignore = false;
43
 
44
+ // Comprehensive reset for new journey/image
45
  setMessages([]);
46
  setCurrentMCQ(null);
47
  setIsLoading(false);
48
  setIsAwaitingAnswer(false);
49
  setHasAnsweredCorrectly(false);
50
  setIncorrectAttempts([]);
51
+ isInitialQuestionRef.current = true;
52
+ messageIdCounter.current = 0;
53
+
54
 
55
  const performInitialFetch = async () => {
56
  if (!imageDataUri) {
57
+ if (!ignore && messages.length === 0) { // Only add if no messages and not ignoring
58
  addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
59
  }
60
  return;
 
69
 
70
  let activeMcqData: ActiveMCQ = {
71
  ...mcqResult,
72
+ originalQuestionTextForFlow: mcqResult.mcq, // Store original question text
73
  };
74
 
75
  if (isInitialQuestionRef.current) {
 
98
  }
99
  };
100
 
 
 
101
  if (imageDataUri) {
102
+ // If there's an existing "Preparing..." message, clear it before fetching
103
+ if (messages.length === 1 && messages[0].text === "Preparing your journey...") {
104
+ setMessages([]);
 
 
 
105
  }
106
+ performInitialFetch();
107
+ } else if (messages.length === 0) { // Only add "Preparing..." if no messages exist
108
+ addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
109
  }
110
 
111
 
 
113
  ignore = true;
114
  };
115
  // eslint-disable-next-line react-hooks/exhaustive-deps
116
+ }, [imageDataUri, journeyTitle]); // addMessage and toast are stable, so not needed in deps
117
 
118
 
119
  useEffect(() => {
 
137
  const questionForAI = currentMCQ.originalQuestionTextForFlow || currentMCQ.mcq;
138
 
139
  if (isCorrect) {
140
+ let combinedMessage = "That's correct! Well done.";
141
  try {
142
  const explanationResult = await explainCorrectAnswer({
143
  question: questionForAI,
144
  options: currentMCQ.options,
145
  correctAnswer: currentMCQ.correctAnswer,
146
  });
147
+ combinedMessage += ` ${explanationResult.explanation}`;
148
  } catch (error) {
149
  console.error("Error fetching correct answer explanation:", error);
150
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
151
+ combinedMessage += ` (Sorry, I couldn't provide an explanation for that: ${errorMsg})`;
152
  }
153
+ addMessage({ sender: 'ai', type: 'feedback', text: combinedMessage, isCorrect: true });
154
  setHasAnsweredCorrectly(true);
155
  setIncorrectAttempts([]);
156
  } else { // Incorrect answer
 
164
  const incorrectExplanationResult = await explainIncorrectAnswer({
165
  question: questionForAI,
166
  options: currentMCQ.options,
167
+ selectedAnswer: option,
168
  correctAnswer: currentMCQ.correctAnswer,
169
  });
170
  combinedText += `That's not quite right. ${incorrectExplanationResult.explanation} `;
 
193
  sender: 'ai',
194
  type: 'feedback',
195
  text: combinedText.trim(),
196
+ isCorrect: true,
197
  });
198
  setHasAnsweredCorrectly(true);
199
  setIncorrectAttempts([]);
 
209
 
210
  const repromptText = `That's not quite right. ${explanationResult.explanation} Would you like to try again?`;
211
  const repromptMCQData: ActiveMCQ = {
212
+ ...currentMCQ,
213
+ mcq: repromptText,
214
  };
215
 
216
  addMessage({ sender: 'ai', type: 'mcq', mcq: repromptMCQData });
217
+ setCurrentMCQ(repromptMCQData);
218
  setIsAwaitingAnswer(true);
219
 
220
  } catch (error) {
221
  console.error("Error fetching explanation for incorrect answer:", error);
222
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
223
+ const fallbackRepromptText = `That's not quite right. (Sorry, I couldn't explain that: ${errorMsg}) Please try another option from the question above.`;
224
+ const fallbackMCQData: ActiveMCQ = {
225
+ ...currentMCQ,
226
+ mcq: fallbackRepromptText,
227
+ };
228
+ addMessage({ sender: 'ai', type: 'mcq', mcq: fallbackMCQData });
229
+ setCurrentMCQ(fallbackMCQData);
230
+ setIsAwaitingAnswer(true);
231
  }
232
  }
233
  }
 
237
  const handleNextQuestionClick = async () => {
238
  setIsAwaitingAnswer(false);
239
  setHasAnsweredCorrectly(false);
240
+ setCurrentMCQ(null);
241
  setIncorrectAttempts([]);
242
 
243
  if (!imageDataUri) {
 
249
  const mcqResult = await generateMCQ({ imageDataUri });
250
  const nextMCQData: ActiveMCQ = {
251
  ...mcqResult,
252
+ originalQuestionTextForFlow: mcqResult.mcq,
253
  };
254
  setCurrentMCQ(nextMCQData);
255
  addMessage({ sender: 'ai', type: 'mcq', mcq: nextMCQData });
 
277
  <ScrollArea className="flex-1 p-4" ref={scrollAreaRef}>
278
  <div className="space-y-4">
279
  {messages.map((msg, index) => {
 
 
 
280
  const isThisMessageTheCurrentMCQ =
281
  msg.type === 'mcq' &&
282
  msg.mcq &&
283
  currentMCQ &&
284
+ msg.mcq.mcq === currentMCQ.mcq &&
285
  JSON.stringify(msg.mcq.options) === JSON.stringify(currentMCQ.options) &&
286
  msg.mcq.correctAnswer === currentMCQ.correctAnswer;
287
 
 
 
288
  const lastInstanceOfCurrentMCQIndex = messages.findLastIndex(
289
  (m) =>
290
  m.type === 'mcq' &&
 
295
  m.mcq.correctAnswer === currentMCQ.correctAnswer
296
  );
297
 
 
 
 
298
  const shouldThisMessageBeInteractive =
299
  isThisMessageTheCurrentMCQ &&
300
  index === lastInstanceOfCurrentMCQIndex &&
 
305
  <ChatMessage
306
  key={msg.id}
307
  message={msg}
308
+ activeMCQ={currentMCQ}
309
  isAwaitingActiveMCQAnswer={shouldThisMessageBeInteractive}
310
  onOptionSelectActiveMCQ={handleOptionSelect}
311
  incorrectAttemptsForMCQ={
312
+ shouldThisMessageBeInteractive
313
  ? incorrectAttempts
314
  : []
315
  }
 
317
  );
318
  })}
319
  {isLoading && !currentMCQ && messages[messages.length -1]?.type !== 'feedback' && (
 
320
  <div className="flex items-start space-x-3">
321
  <Skeleton className="h-8 w-8 rounded-full" />
322
  <div className="space-y-2">
 
336
  </Button>
337
  )}
338
 
 
339
  {!isLoading && currentMCQ && !hasAnsweredCorrectly && isAwaitingAnswer && (
340
  <p className="text-center text-sm text-muted-foreground">
341
  Please choose an answer from the options above.
342
  </p>
343
  )}
344
 
 
345
  {!imageDataUri && !isLoading && messages.length > 0 && messages[messages.length -1]?.text === "Preparing your journey..." &&(
346
  <p className="text-center text-sm text-muted-foreground">Loading image, please wait...</p>
347
  )}
348
  {!currentMCQ && !isLoading && imageDataUri && messages.length === 0 && (
 
349
  <p className="text-center text-sm text-muted-foreground">Loading first question...</p>
350
  )}
 
351
  {!currentMCQ && !isLoading && imageDataUri && messages.length > 0 && messages[messages.length -1]?.type === 'error' && (
352
  <p className="text-center text-sm text-destructive">Could not load question. Try refreshing.</p>
353
  )}