Spaces:
Sleeping
Sleeping
| 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 style={{ textAlign: 'center' }}>🌳 Welcome to AI Energy Efficiency Trainer 🌿</h2> | |
| <p className="modal-subtitle">Learn to choose the right AI model to save energy and improve responses</p> | |
| <div className="modal-instruction-block"> | |
| <div className="instruction-header"></div> | |
| <div className="instruction-content"> | |
| <p className="instruction-title" style={{ fontWeight: 'bold', fontSize: '1.1em', marginBottom: '8px', marginTop: '16px' }}>️1. Review the prompt:</p> | |
| <div className="input-example-container"> | |
| <div className="example-textarea"> | |
| "Calculate the compound interest on $5,000 invested for 3 years at 4.5% per annum." | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="modal-instruction-block"> | |
| <div className="instruction-content"> | |
| <p className="instruction-title" style={{ fontWeight: 'bold', fontSize: '1.1em', marginBottom: '8px', marginTop: '16px' }}>2. Select the appropriate model (if needed):</p> | |
| <div className="button-example-container" style={{ display: 'flex', alignItems: 'center', marginBottom: '10px' }}> | |
| <button className="standard-button reasoning-button example" style={{ minWidth: '80px' }}>Reason</button> | |
| <span style={{ marginLeft: '10px', display: 'inline-block', verticalAlign: 'middle' }}>For calculations, step-by-step explanations, and complex reasoning</span> | |
| </div> | |
| <div className="button-example-container" style={{ display: 'flex', alignItems: 'center' }}> | |
| <button className="standard-button search-button example" style={{ minWidth: '80px' }}>Search</button> | |
| <span style={{ marginLeft: '10px', display: 'inline-block', verticalAlign: 'middle' }}>For factual answers, simple lookups, and direct responses</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="modal-instruction-block"> | |
| <div className="instruction-content"> | |
| <p className="instruction-title" style={{ fontWeight: 'bold', fontSize: '1.1em', marginBottom: '8px', marginTop: '16px' }}>3. Click Send to submit your answer!</p> | |
| <div className="send-button-example"> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="modal-footer"> | |
| <p className="energy-note">⚡ Making the right choice helps reduce AI energy consumption</p> | |
| <button onClick={() => setShowModal(false)} className="start-button" style={{ float: 'right' }}>Let's Begin</button> | |
| </div> | |
| </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 initialLoadComplete is true | |
| 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 /> | |
| </> | |
| ); | |
| } | |