106 lines
3.8 KiB
TypeScript
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>
|
|
);
|
|
};
|