Spaces:
Runtime error
Runtime error
Download webapp/src/components/QuestionScreen.tsx from yuk1qwe/flowquizbot: direct link, hf CLI and curl.
- Browser
- Download file 6.77 kB
-
https://huggingface.co/spaces/yuk1qwe/flowquizbot/resolve/main/webapp/src/components/QuestionScreen.tsx
- Command line
-
hf download hf://spaces/yuk1qwe/flowquizbot/webapp/src/components/QuestionScreen.tsx
-
curl -L -o QuestionScreen.tsx https://huggingface.co/spaces/yuk1qwe/flowquizbot/resolve/main/webapp/src/components/QuestionScreen.tsx
6.77 kB
| import { useState } from 'react' | |
| import type { Question } from '../types/quiz' | |
| import { ProgressBar } from './ProgressBar' | |
| interface QuestionScreenProps { | |
| question: Question | |
| current: number | |
| total: number | |
| onAnswer: (answer: string | string[]) => void | |
| } | |
| export function QuestionScreen({ question, current, total, onAnswer }: QuestionScreenProps) { | |
| const [selected, setSelected] = useState<string[]>([]) | |
| const [textValue, setTextValue] = useState('') | |
| const handleSingleSelect = (optionId: string) => { | |
| setSelected([optionId]) | |
| setTimeout(() => onAnswer(optionId), 400) | |
| } | |
| const handleMultiToggle = (optionId: string) => { | |
| setSelected(prev => | |
| prev.includes(optionId) | |
| ? prev.filter(id => id !== optionId) | |
| : [...prev, optionId] | |
| ) | |
| } | |
| const handleMultiSubmit = () => { | |
| if (selected.length > 0) onAnswer(selected) | |
| } | |
| const handleTextSubmit = () => { | |
| if (textValue.trim()) onAnswer(textValue.trim()) | |
| } | |
| return ( | |
| <div key={question.id} className="animate-fade-in flex flex-col min-h-screen px-5 pt-14 pb-8"> | |
| <ProgressBar current={current} total={total} /> | |
| <h2 className="text-[22px] font-bold leading-snug tracking-tight text-(--color-text-primary) mb-7"> | |
| {question.text} | |
| </h2> | |
| {/* Single choice */} | |
| {question.type === 'single' && ( | |
| <div className="flex flex-col gap-2"> | |
| {question.options.map(opt => ( | |
| <button | |
| key={opt.id} | |
| onClick={() => handleSingleSelect(opt.id)} | |
| className={`flex items-center gap-3.5 px-4 py-3.5 bg-(--color-bg-card) border rounded-xl cursor-pointer transition-all duration-200 shadow-sm text-left | |
| ${selected.includes(opt.id) ? 'border-(--color-border-selected) bg-(--color-bg-selected)' : 'border-(--color-border) hover:bg-(--color-bg-hover) hover:border-(--color-border-hover)'}`} | |
| > | |
| {opt.emoji && <span className="text-[22px] w-8 text-center shrink-0">{opt.emoji}</span>} | |
| <span className="text-[15px] font-medium text-(--color-text-primary) flex-1">{opt.text}</span> | |
| <span className={`w-5 h-5 rounded-full border-2 shrink-0 relative transition-all duration-200 | |
| ${selected.includes(opt.id) ? 'border-(--color-accent) bg-(--color-accent)' : 'border-(--color-border)'}`}> | |
| {selected.includes(opt.id) && ( | |
| <span className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-2 h-2 rounded-full bg-(--color-accent-text)" /> | |
| )} | |
| </span> | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| {/* Multi choice */} | |
| {question.type === 'multi' && ( | |
| <> | |
| <div className="flex flex-col gap-2"> | |
| {question.options.map(opt => ( | |
| <button | |
| key={opt.id} | |
| onClick={() => handleMultiToggle(opt.id)} | |
| className={`flex items-center gap-3.5 px-4 py-3.5 bg-(--color-bg-card) border rounded-xl cursor-pointer transition-all duration-200 shadow-sm text-left | |
| ${selected.includes(opt.id) ? 'border-(--color-border-selected) bg-(--color-bg-selected)' : 'border-(--color-border) hover:bg-(--color-bg-hover) hover:border-(--color-border-hover)'}`} | |
| > | |
| {opt.emoji && <span className="text-[22px] w-8 text-center shrink-0">{opt.emoji}</span>} | |
| <span className="text-[15px] font-medium text-(--color-text-primary) flex-1">{opt.text}</span> | |
| <span className={`w-5 h-5 rounded shrink-0 border-2 flex items-center justify-center transition-all duration-200 | |
| ${selected.includes(opt.id) ? 'border-(--color-accent) bg-(--color-accent)' : 'border-(--color-border)'}`}> | |
| {selected.includes(opt.id) && ( | |
| <svg className="w-3 h-3 text-(--color-accent-text)" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}> | |
| <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" /> | |
| </svg> | |
| )} | |
| </span> | |
| </button> | |
| ))} | |
| </div> | |
| <div className="flex-1" /> | |
| <button | |
| onClick={handleMultiSubmit} | |
| disabled={selected.length === 0} | |
| className="w-full py-3.5 bg-(--color-accent) text-(--color-accent-text) rounded-xl text-[15px] font-semibold tracking-tight transition-all disabled:opacity-30 mt-6" | |
| > | |
| Continue | |
| </button> | |
| </> | |
| )} | |
| {/* Image cards */} | |
| {question.type === 'image_cards' && ( | |
| <div className="grid grid-cols-2 gap-2"> | |
| {question.options.map(opt => ( | |
| <button | |
| key={opt.id} | |
| onClick={() => handleSingleSelect(opt.id)} | |
| className={`flex flex-col items-center p-3 bg-(--color-bg-card) border rounded-xl cursor-pointer transition-all duration-200 shadow-sm | |
| ${selected.includes(opt.id) ? 'border-(--color-border-selected) bg-(--color-bg-selected)' : 'border-(--color-border) hover:bg-(--color-bg-hover)'}`} | |
| > | |
| {opt.image_url ? ( | |
| <img src={opt.image_url} alt={opt.text} className="w-full aspect-square object-cover rounded-lg mb-2" /> | |
| ) : ( | |
| <div className="w-full aspect-square bg-(--color-bg-hover) rounded-lg mb-2 flex items-center justify-center text-3xl"> | |
| {opt.emoji || '📷'} | |
| </div> | |
| )} | |
| <span className="text-[13px] font-medium text-(--color-text-primary) text-center">{opt.text}</span> | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| {/* Text input */} | |
| {question.type === 'text_input' && ( | |
| <> | |
| <input | |
| type="text" | |
| value={textValue} | |
| onChange={e => setTextValue(e.target.value)} | |
| onKeyDown={e => e.key === 'Enter' && handleTextSubmit()} | |
| placeholder={question.placeholder || ''} | |
| className="w-full px-3.5 py-3 text-[15px] bg-(--color-bg-card) border border-(--color-border) rounded-lg text-(--color-text-primary) outline-none transition-colors focus:border-(--color-accent) placeholder:text-(--color-text-tertiary)" | |
| autoFocus | |
| /> | |
| <div className="flex-1" /> | |
| <button | |
| onClick={handleTextSubmit} | |
| disabled={!textValue.trim()} | |
| className="w-full py-3.5 bg-(--color-accent) text-(--color-accent-text) rounded-xl text-[15px] font-semibold tracking-tight transition-all disabled:opacity-30 mt-6" | |
| > | |
| Continue | |
| </button> | |
| </> | |
| )} | |
| </div> | |
| ) | |
| } | |