lila/apps/web/src/components/game/QuestionCard.tsx
2026-04-19 19:25:55 +02:00

106 lines
3.8 KiB
TypeScript

import { useState } from "react";
import type { GameQuestion, AnswerResult } from "@lila/shared";
import { OptionButton } from "./OptionButton";
type QuestionCardProps = {
question: GameQuestion;
questionNumber: number;
totalQuestions: number;
currentResult: AnswerResult | null;
onAnswer: (optionId: number) => void;
onNext: () => void;
};
export const QuestionCard = ({
question,
questionNumber,
totalQuestions,
currentResult,
onAnswer,
onNext,
}: QuestionCardProps) => {
const [selectedOptionId, setSelectedOptionId] = useState<number | null>(null);
const getOptionState = (optionId: number) => {
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 (
<div className="flex flex-col items-center gap-6 w-full max-w-md mx-auto">
<div className="w-full flex items-center justify-between">
<div className="inline-flex items-center gap-2 rounded-full bg-(--color-surface) border border-(--color-primary-light) px-4 py-1 text-xs font-bold tracking-widest uppercase text-(--color-primary)">
Round {questionNumber}/{totalQuestions}
</div>
<div className="text-xs font-semibold text-(--color-text-muted)">
{currentResult ? "Checked" : selectedOptionId !== null ? "Ready" : "Pick one"}
</div>
</div>
<div className="relative w-full overflow-hidden rounded-3xl border border-(--color-primary-light) bg-white/40 dark:bg-black/10 backdrop-blur shadow-sm p-8 text-center">
<div className="absolute -top-16 -left-20 h-40 w-40 rounded-full bg-(--color-accent) opacity-[0.10] blur-3xl" />
<div className="absolute -bottom-20 -right-20 h-44 w-44 rounded-full bg-(--color-primary) opacity-[0.12] blur-3xl" />
<h2 className="relative text-3xl font-black tracking-tight text-(--color-text) mb-2">
{question.prompt}
</h2>
{question.gloss && (
<p className="relative text-sm text-(--color-text-muted) italic">
{question.gloss}
</p>
)}
</div>
<div className="w-full rounded-3xl border border-(--color-primary-light) bg-white/55 dark:bg-black/10 backdrop-blur shadow-sm p-4">
<div className="flex flex-col gap-3">
{question.options.map((option) => (
<OptionButton
key={option.optionId}
text={option.text}
state={getOptionState(option.optionId)}
onSelect={() => handleSelect(option.optionId)}
/>
))}
</div>
</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 shadow-sm hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 transition-all duration-200 cursor-pointer"
>
Lock it in
</button>
)}
{currentResult && (
<button
onClick={handleNext}
className="w-full py-3 rounded-2xl text-lg font-bold bg-(--color-primary) text-white shadow-sm hover:shadow-md hover:bg-(--color-primary-dark) hover:-translate-y-0.5 active:translate-y-0 transition-all duration-200 cursor-pointer"
>
{questionNumber === totalQuestions ? "See Results" : "Next"}
</button>
)}
</div>
);
};