import { useState, useEffect, useRef } from 'react'; import messageDatabase from './data.json'; import './App.css'; export default function ChatDisplay() { const [isActive, setIsActive] = useState(false); const [showModal, setShowModal] = useState(true); const [currentMessageIndex, setCurrentMessageIndex] = useState( Math.floor(Math.random() * messageDatabase.length) ); const [showFeedback, setShowFeedback] = useState(false); const [message, setMessage] = useState(() => messageDatabase[currentMessageIndex].question); const [messagesSent, setMessagesSent] = useState(false); const [sentMessage, setSentMessage] = useState(''); const [isThinking, setIsThinking] = useState(false); const [userGuessCorrect, setUserGuessCorrect] = useState(false); const [showAnswer, setShowAnswer] = useState(false); const [showTopAnimation, setShowTopAnimation] = useState(false); const [animatedMessages, setAnimatedMessages] = useState({ userMessage: false, thinking: false, feedback: false }); const [reasoningButtonActive, setReasoningButtonActive] = useState(false); const [searchButtonActive, setSearchButtonActive] = useState(false); const getRandomMessageIndex = () => { let newIndex; do { newIndex = Math.floor(Math.random() * messageDatabase.length); } while (newIndex === currentMessageIndex && messageDatabase.length > 1); return newIndex; }; const loadNewQuestion = () => { const newIndex = getRandomMessageIndex(); setCurrentMessageIndex(newIndex); setMessage(messageDatabase[newIndex].question); setMessagesSent(false); setShowFeedback(false); setShowAnswer(false); setSentMessage(''); }; const isMessageTypeMatching = () => { const currentType = messageDatabase[currentMessageIndex].type; // Match reasoning type with reasoning button if (currentType === "reasoning" && reasoningButtonActive) return true; // Match search/standard type with search button if ((currentType === "search" || currentType === "standard") && searchButtonActive) return true; // Match default/standard type with no button selection if (currentType === "standard" && !reasoningButtonActive && !searchButtonActive) return true; return false; }; const handleUserGuess = () => { const isCorrect = isMessageTypeMatching(); setUserGuessCorrect(isCorrect); setShowFeedback(true); }; const handleSubmit = (e) => { e.preventDefault(); if (!message.trim()) return; // When "Send" is clicked (not in Next state) if (!messagesSent) { setSentMessage(message); setMessagesSent(true); setIsThinking(true); setMessage(''); // Clear input immediately when Send is clicked setTimeout(() => { setIsThinking(false); handleUserGuess(); }, 1500); } }; const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(e); } }; useEffect(() => { if (!messagesSent) { setAnimatedMessages({ userMessage: false, thinking: false, feedback: false }); } }, [messagesSent]); function UserMessage({ text }) { const shouldAnimate = !animatedMessages.userMessage; useEffect(() => { if (shouldAnimate) { setAnimatedMessages(prev => ({ ...prev, userMessage: true })); } }, [shouldAnimate]); return ( <div className={`message user-message ${shouldAnimate ? 'message-send-animation' : ''}`}> <p>{text}</p> </div> ); } function FeedbackMessage() { const currentType = messageDatabase[currentMessageIndex].type; const shouldBeReasoning = currentType === "reasoning"; const shouldAnimate = !animatedMessages.feedback; useEffect(() => { if (shouldAnimate) { setAnimatedMessages(prev => ({ ...prev, feedback: true })); } }, [shouldAnimate]); if (userGuessCorrect) { return ( <div className={`message feedback-message success ${shouldAnimate ? 'message-send-animation' : ''}`}> <p>🎉 Congratulations! You correctly identified this as a{shouldBeReasoning ? ' reasoning' : ' standard'} prompt.</p> </div> ); } else { return ( <div className={`message feedback-message error ${shouldAnimate ? 'message-send-animation' : ''}`}> <p>❌ Incorrect. This is a{shouldBeReasoning ? ' reasoning' : ' standard'} prompt.</p> <p className="error-explanation"> {shouldBeReasoning ? "This prompt requires detailed explanation and step-by-step reasoning to solve the problem." : "This prompt requires a direct, concise answer without detailed explanation."} </p> </div> ); } } function WelcomeModal() { return ( showModal && ( <div className="modal-overlay"> <div className="modal"> <h2>🌿 Did you know? </h2> <p>Every prompt counts! Choosing the right AI model saves energy and protects our planet. 🌎✨</p> <img src="images/cute_world_mini.png" style={{ width: '55%' }} /> <p>Play smart, pick wisely, and reduce your digital footprint! 🌱💡</p> <button onClick={() => setShowModal(false)}>Start</button> </div> </div> ) ); } function ChatInput() { const [isNewPrompt, setIsNewPrompt] = useState(false); const initialLoadComplete = useRef(false); // Only run once when component mounts useEffect(() => { // Mark initial load as complete after a short delay const timer = setTimeout(() => { initialLoadComplete.current = true; }, 500); return () => clearTimeout(timer); }, []); const handleButtonClick = (button) => { // Allow toggling between active/inactive states if (button === 'reasoning') { // If already active, deselect it if (reasoningButtonActive) { setReasoningButtonActive(false); } else { // Otherwise, activate it and deactivate the other button setReasoningButtonActive(true); setSearchButtonActive(false); } } else if (button === 'search') { // If already active, deselect it if (searchButtonActive) { setSearchButtonActive(false); } else { // Otherwise, activate it and deactivate the other button setSearchButtonActive(true); setReasoningButtonActive(false); } } }; const handleNextClick = () => { // Only set new prompt flag if initial load is complete if (initialLoadComplete.current) { setIsNewPrompt(true); } // Show the energy animation setShowTopAnimation(true); // Hide animation after a delay setTimeout(() => { setShowTopAnimation(false); }, 3000); loadNewQuestion(); // Reset the new prompt flag after animation if (initialLoadComplete.current) { setTimeout(() => setIsNewPrompt(false), 800); } }; // Simplify class name logic - no complex conditions const getInputClassName = () => { const classes = ['chat-input']; // Only apply new-prompt class if initial load is complete if (initialLoadComplete.current && isNewPrompt) { classes.push('new-prompt'); } return classes.join(' '); }; return ( <div className="chat-input-container"> <form className="chat-input-wrapper" onSubmit={handleSubmit}> <div className="button-group"> <div className="tooltip-wrapper"> <button onClick={() => handleButtonClick('reasoning')} className={`standard-button reasoning-button ${reasoningButtonActive ? 'active' : ''}`} type="button" disabled={messagesSent} > Reason </button> <div className="tooltip"> Use for prompts that require detailed explanations and step-by-step reasoning </div> </div> <div className="tooltip-wrapper"> <button onClick={() => handleButtonClick('search')} className={`standard-button search-button ${searchButtonActive ? 'active' : ''}`} type="button" disabled={messagesSent} > Search </button> <div className="tooltip"> Use for prompts that need simple, direct answers without detailed explanation </div> </div> </div> <div className="input-with-button"> <div className="tooltip-wrapper input-tooltip-wrapper"> <textarea className={getInputClassName()} value={showModal ? '' : message} onKeyPress={handleKeyPress} placeholder="Click next for next prompt" rows="3" readOnly={!messagesSent && message === messageDatabase[currentMessageIndex].question} /> <div className="tooltip input-tooltip"> First select a model type above, then press Send to submit your answer </div> </div> <button type={messagesSent && !showFeedback ? "button" : "submit"} className="answer-button" onClick={messagesSent && showFeedback ? handleNextClick : undefined} disabled={messagesSent && !showFeedback} > {messagesSent && showFeedback ? 'Next' : 'Send'} </button> </div> </form> </div> ); } function ThinkingAnimation() { const shouldAnimate = !animatedMessages.thinking; useEffect(() => { if (shouldAnimate) { setAnimatedMessages(prev => ({ ...prev, thinking: true })); } }, [shouldAnimate]); return ( <div className={`thinking ${shouldAnimate ? 'message-send-animation' : ''}`}> <div className="dot"></div> <div className="dot"></div> <div className="dot"></div> </div> ); } // Updated CompanyLogo component with both logos at the bottom function CompanyLogo() { return ( <> <img src="images/cotec.png" alt="Company Logo Right" className="company-logo company-logo-right" /> <img src="images/upm4.png" alt="Company Logo Left" className="company-logo company-logo-left" /> </> ); } function EnergyAnimation() { return ( <div className={`energy-animation ${showTopAnimation ? 'show' : ''}`}> <div className="energy-icon"> {userGuessCorrect ? '🌳' : '🗑️'} </div> <div className="energy-text"> {userGuessCorrect ? 'Energy saved! Good choice!' : 'Choose wisely next time to save energy!'} </div> </div> ); } return ( <> <WelcomeModal /> <EnergyAnimation /> <div className="chat-container"> <div className="messages-container"> {messagesSent && <UserMessage text={`"${sentMessage}"`} />} {messagesSent && isThinking && <ThinkingAnimation />} {messagesSent && showFeedback && !isThinking && <FeedbackMessage />} </div> <ChatInput /> </div> <CompanyLogo /> </> ); }