feat(web): add game settings screen and submit confirmation
- Add GameSetup component with Duolingo-style button selectors for language pair, POS, difficulty, and rounds - Language swap: selecting the same language for source and target automatically swaps them instead of allowing duplicates - Add selection-before-submission flow: user clicks an option to highlight it, then confirms with a Submit button to prevent misclicks - Add selected state to OptionButton (purple ring highlight) - Play Again on score screen returns to settings instead of auto-restarting with the same configuration - Remove hardcoded GAME_SETTINGS, game configuration is now user-driven
This commit is contained in:
parent
b7b1cd383f
commit
bc7977463e
4 changed files with 209 additions and 26 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import type { GameQuestion, AnswerResult } from "@glossa/shared";
|
||||
import { OptionButton } from "./OptionButton";
|
||||
|
||||
|
|
@ -18,11 +19,31 @@ export const QuestionCard = ({
|
|||
onAnswer,
|
||||
onNext,
|
||||
}: QuestionCardProps) => {
|
||||
const [selectedOptionId, setSelectedOptionId] = useState<number | null>(null);
|
||||
|
||||
const getOptionState = (optionId: number) => {
|
||||
if (!currentResult) return "idle" as const;
|
||||
if (optionId === currentResult.correctOptionId) return "correct" as const;
|
||||
if (optionId === currentResult.selectedOptionId) return "wrong" as const;
|
||||
return "disabled" as const;
|
||||
if (currentResult) {
|
||||
if (optionId === currentResult.correctOptionId) return "correct" as const;
|
||||
if (optionId === currentResult.selectedOptionId) return "wrong" as const;
|
||||
return "disabled" as const;
|
||||
}
|
||||
if (optionId === selectedOptionId) return "selected" as const;
|
||||
return "idle" as const;
|
||||
};
|
||||
|
||||
const handleSelect = (optionId: number) => {
|
||||
if (currentResult) return;
|
||||
setSelectedOptionId(optionId);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (selectedOptionId === null) return;
|
||||
onAnswer(selectedOptionId);
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
setSelectedOptionId(null);
|
||||
onNext();
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -48,15 +69,24 @@ export const QuestionCard = ({
|
|||
key={option.optionId}
|
||||
text={option.text}
|
||||
state={getOptionState(option.optionId)}
|
||||
onSelect={() => onAnswer(option.optionId)}
|
||||
onSelect={() => handleSelect(option.optionId)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!currentResult && selectedOptionId !== null && (
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className="w-full py-3 rounded-2xl text-lg font-bold bg-linear-to-r from-pink-400 to-purple-500 text-white border-b-4 border-purple-700 hover:-translate-y-0.5 active:translate-y-0 active:border-b-2 transition-all duration-200 cursor-pointer"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
)}
|
||||
|
||||
{currentResult && (
|
||||
<button
|
||||
onClick={onNext}
|
||||
className="mt-2 py-3 px-10 rounded-2xl text-lg font-bold bg-purple-600 text-white border-b-4 border-purple-800 hover:bg-purple-500 hover:-translate-y-0.5 active:translate-y-0 active:border-b-2 transition-all duration-200 cursor-pointer"
|
||||
onClick={handleNext}
|
||||
className="w-full py-3 rounded-2xl text-lg font-bold bg-purple-600 text-white border-b-4 border-purple-800 hover:bg-purple-500 hover:-translate-y-0.5 active:translate-y-0 active:border-b-2 transition-all duration-200 cursor-pointer"
|
||||
>
|
||||
{questionNumber === totalQuestions ? "See Results" : "Next"}
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue