Spaces:
Sleeping
Sleeping
| "use client"; | |
| import type { ChatMessage as ChatMessageType, ActiveMCQ } from '@/types'; | |
| import { cn } from '@/lib/utils'; | |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |
| import { Bot, User, AlertTriangle, CheckCircle, Info } from 'lucide-react'; | |
| import { Card, CardContent } from '@/components/ui/card'; | |
| import { MCQOptions } from './mcq-options'; | |
| import { useState, useEffect } from 'react'; | |
| interface ChatMessageProps { | |
| message: ChatMessageType; | |
| activeMCQ?: ActiveMCQ | null; | |
| isAwaitingActiveMCQAnswer?: boolean; | |
| onOptionSelectActiveMCQ?: (option: string, isCorrect: boolean) => void; | |
| incorrectAttemptsForMCQ?: string[]; | |
| } | |
| export function ChatMessage({ | |
| message, | |
| activeMCQ, | |
| isAwaitingActiveMCQAnswer, | |
| onOptionSelectActiveMCQ, | |
| incorrectAttemptsForMCQ, | |
| }: ChatMessageProps) { | |
| const isUser = message.sender === 'user'; | |
| const Icon = isUser ? User : Bot; | |
| const avatarFallback = isUser ? 'U' : 'AI'; | |
| const [formattedTime, setFormattedTime] = useState(''); | |
| useEffect(() => { | |
| if (message.timestamp) { | |
| setFormattedTime(new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })); | |
| } | |
| }, [message.timestamp]); | |
| return ( | |
| <div | |
| className={cn( | |
| 'mb-4 flex items-end space-x-3', | |
| isUser ? 'justify-end' : 'justify-start' | |
| )} | |
| > | |
| {!isUser && ( | |
| <Avatar className="h-8 w-8 self-start"> | |
| <AvatarImage src="/placeholder-bot.png" alt="AI Avatar" /> | |
| <AvatarFallback><Bot className="h-4 w-4" /></AvatarFallback> | |
| </Avatar> | |
| )} | |
| <Card | |
| className={cn( | |
| 'max-w-sm rounded-2xl p-0 shadow-md md:max-w-md', | |
| isUser | |
| ? 'rounded-br-none bg-primary text-primary-foreground' | |
| : 'rounded-bl-none bg-card text-card-foreground border' | |
| )} | |
| > | |
| <CardContent className="p-3"> | |
| {message.type === 'mcq' && message.mcq && ( | |
| <div className="space-y-2"> | |
| <p className="font-medium whitespace-pre-wrap">{message.mcq.mcq}</p> | |
| {isAwaitingActiveMCQAnswer && onOptionSelectActiveMCQ && ( | |
| <div className="mt-3"> | |
| <MCQOptions | |
| mcq={message.mcq} | |
| onOptionSelect={onOptionSelectActiveMCQ} | |
| disabled={false} | |
| incorrectAttempts={incorrectAttemptsForMCQ || []} | |
| /> | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| {message.type === 'text' && message.text && <p className="whitespace-pre-wrap">{message.text}</p>} | |
| {message.type === 'feedback' && message.text && ( | |
| <div className="flex items-start"> | |
| {/* Show CheckCircle only if isCorrect and not the final resolution message */} | |
| {message.isCorrect && message.text && !message.text.includes("The correct answer is") && ( | |
| <CheckCircle className="mr-2 h-5 w-5 shrink-0 text-green-500 mt-0.5" /> | |
| )} | |
| {/* Show Info icon for incorrect answer explanations where user can try again */} | |
| {!message.isCorrect && ( | |
| <Info className="mr-2 h-5 w-5 shrink-0 text-blue-500 mt-0.5" /> | |
| )} | |
| {/* If message.isCorrect is true AND text includes "The correct answer is", no icon will be shown */} | |
| <p className="flex-1 whitespace-pre-wrap">{message.text}</p> | |
| </div> | |
| )} | |
| {message.type === 'error' && message.text && ( | |
| <div className="flex items-start text-destructive"> | |
| <AlertTriangle className="mr-2 h-5 w-5 shrink-0 mt-0.5" /> | |
| <p className="flex-1 whitespace-pre-wrap">{message.text}</p> | |
| </div> | |
| )} | |
| {message.type === 'user' && message.text && ( | |
| <p className="italic">You chose: "{message.text}"</p> | |
| )} | |
| {formattedTime && ( | |
| <p className={cn("mt-1.5 text-xs", isUser ? "text-primary-foreground/70" : "text-muted-foreground")}> | |
| {formattedTime} | |
| </p> | |
| )} | |
| </CardContent> | |
| </Card> | |
| {isUser && ( | |
| <Avatar className="h-8 w-8 self-start"> | |
| <AvatarImage src="/placeholder-user.png" alt="User Avatar" /> | |
| <AvatarFallback><User className="h-4 w-4" /></AvatarFallback> | |
| </Avatar> | |
| )} | |
| </div> | |
| ); | |
| } | |