Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { useState } from 'react'; | |
| import styles from './FeedbackWidget.module.css'; | |
| export default function FeedbackWidget({ imageHash, predictedId, top5, isOodScreen, onFeedbackSuccess }) { | |
| const [feedbackState, setFeedbackState] = useState('idle'); | |
| const [errorMessage, setErrorMessage] = useState(''); | |
| const sendFeedback = async (isCorrect, userCorrectionId = null) => { | |
| setFeedbackState('submitting'); | |
| try { | |
| const response = await fetch('/api/feedback', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| image_hash: imageHash, | |
| predicted_id: predictedId, | |
| is_correct: isCorrect, | |
| user_correction_id: userCorrectionId, | |
| is_ood_screen: isOodScreen || false | |
| }) | |
| }); | |
| if (!response.ok) { | |
| if (response.status === 429) { | |
| throw new Error("Muitas requisições. Tente novamente mais tarde."); | |
| } | |
| throw new Error("Erro ao salvar feedback"); | |
| } | |
| setFeedbackState('success'); | |
| if (onFeedbackSuccess) { | |
| onFeedbackSuccess(isCorrect); | |
| } | |
| setTimeout(() => { | |
| setFeedbackState('hidden'); | |
| }, 3000); | |
| } catch (error) { | |
| setFeedbackState('error'); | |
| setErrorMessage(error.message); | |
| setTimeout(() => setFeedbackState('idle'), 4000); | |
| } | |
| }; | |
| if (feedbackState === 'hidden') { | |
| return null; | |
| } | |
| if (feedbackState === 'success') { | |
| return ( | |
| <div className={styles.successMessage}> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{marginRight: '8px', color: '#4caf50'}}><polyline points="20 6 9 17 4 12"></polyline></svg> | |
| Feedback sent, thank you! | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className={styles.feedbackContainer}> | |
| {feedbackState === 'idle' || feedbackState === 'error' ? ( | |
| <div className={styles.voteButtons}> | |
| <span className={styles.prompt}> | |
| {isOodScreen ? "Did PokedexNet get this right?" : "Was this prediction accurate?"} | |
| </span> | |
| <button | |
| className={`${styles.voteBtn} ${styles.likeBtn}`} | |
| onClick={() => sendFeedback(true, isOodScreen ? 'none' : null)} | |
| title={isOodScreen ? "Yes, it is NOT a Pokémon" : "Yes, it's correct"} | |
| > | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path></svg> | |
| </button> | |
| <button | |
| className={`${styles.voteBtn} ${styles.dislikeBtn}`} | |
| onClick={() => isOodScreen ? sendFeedback(false, 'unknown_pokemon') : setFeedbackState('dislike_menu')} | |
| title={isOodScreen ? "No, it actually IS a Pokémon" : "No, it's wrong"} | |
| > | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path></svg> | |
| </button> | |
| {feedbackState === 'error' && <span className={styles.errorText}>{errorMessage}</span>} | |
| </div> | |
| ) : feedbackState === 'submitting' ? ( | |
| <div className={styles.loadingMessage}>Sending feedback...</div> | |
| ) : feedbackState === 'dislike_menu' ? ( | |
| <div className={styles.dislikeMenu}> | |
| <span className={styles.prompt}>What Pokémon is it really?</span> | |
| <div className={styles.optionsGrid}> | |
| {top5 && top5.slice(1).map(pred => ( | |
| <button | |
| key={pred.pokemon_id} | |
| className={styles.optionBtn} | |
| onClick={() => sendFeedback(false, pred.pokemon_id)} | |
| > | |
| {pred.name} | |
| </button> | |
| ))} | |
| <button | |
| className={`${styles.optionBtn} ${styles.noneBtn}`} | |
| onClick={() => sendFeedback(false, 'none')} | |
| > | |
| None of these | |
| </button> | |
| </div> | |
| <button | |
| className={styles.cancelBtn} | |
| onClick={() => setFeedbackState('idle')} | |
| > | |
| Cancel | |
| </button> | |
| </div> | |
| ) : null} | |
| {(feedbackState === 'idle' || feedbackState === 'error' || feedbackState === 'dislike_menu') && ( | |
| <span className={styles.privacyNotice}> | |
| By submitting feedback, your image may be anonymously stored for model improvement. | |
| </span> | |
| )} | |
| </div> | |
| ); | |
| } | |