151 lines
4.6 KiB
TypeScript
151 lines
4.6 KiB
TypeScript
import { useState } from "react";
|
|
import {
|
|
SUPPORTED_LANGUAGE_CODES,
|
|
SUPPORTED_POS,
|
|
DIFFICULTY_LEVELS,
|
|
GAME_ROUNDS,
|
|
} from "@lila/shared";
|
|
import type { GameRequest } from "@lila/shared";
|
|
|
|
const LABELS: Record<string, string> = {
|
|
en: "English",
|
|
it: "Italian",
|
|
de: "German",
|
|
fr: "French",
|
|
es: "Spanish",
|
|
noun: "Nouns",
|
|
verb: "Verbs",
|
|
easy: "Easy",
|
|
intermediate: "Intermediate",
|
|
hard: "Hard",
|
|
"3": "3 rounds",
|
|
"10": "10 rounds",
|
|
};
|
|
|
|
type GameSetupProps = { onStart: (settings: GameRequest) => void };
|
|
|
|
type SettingGroupProps<T extends string | number> = {
|
|
label: string;
|
|
options: readonly T[];
|
|
selected: T;
|
|
onSelect: (value: T) => void;
|
|
};
|
|
|
|
const SettingGroup = <T extends string | number>({
|
|
label,
|
|
options,
|
|
selected,
|
|
onSelect,
|
|
}: SettingGroupProps<T>) => (
|
|
<div className="w-full">
|
|
<p className="text-xs font-bold tracking-widest uppercase text-(--color-primary) mb-2">
|
|
{label}
|
|
</p>
|
|
<div className="flex gap-2 flex-wrap">
|
|
{options.map((option) => (
|
|
<button
|
|
key={option}
|
|
onClick={() => onSelect(option)}
|
|
className={`py-2 px-5 rounded-xl font-semibold text-sm border transition-all duration-200 cursor-pointer ${
|
|
selected === option
|
|
? "bg-(--color-primary) text-white border-(--color-primary-dark) shadow-sm"
|
|
: "bg-white text-(--color-primary-dark) border-(--color-primary-light) hover:bg-(--color-surface) hover:-translate-y-0.5 active:translate-y-0"
|
|
}`}
|
|
>
|
|
{LABELS[String(option)] ?? option}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
export const GameSetup = ({ onStart }: GameSetupProps) => {
|
|
const [sourceLanguage, setSourceLanguage] = useState<string>(
|
|
SUPPORTED_LANGUAGE_CODES[0],
|
|
);
|
|
const [targetLanguage, setTargetLanguage] = useState<string>(
|
|
SUPPORTED_LANGUAGE_CODES[1],
|
|
);
|
|
const [pos, setPos] = useState<string>(SUPPORTED_POS[0]);
|
|
const [difficulty, setDifficulty] = useState<string>(DIFFICULTY_LEVELS[0]);
|
|
const [rounds, setRounds] = useState<number>(GAME_ROUNDS[0]);
|
|
|
|
const handleSourceLanguage = (value: string) => {
|
|
if (value === targetLanguage) {
|
|
setTargetLanguage(sourceLanguage);
|
|
}
|
|
setSourceLanguage(value);
|
|
};
|
|
|
|
const handleTargetLanguage = (value: string) => {
|
|
if (value === sourceLanguage) {
|
|
setSourceLanguage(targetLanguage);
|
|
}
|
|
setTargetLanguage(value);
|
|
};
|
|
|
|
const handleStart = () => {
|
|
onStart({
|
|
source_language: sourceLanguage,
|
|
target_language: targetLanguage,
|
|
pos,
|
|
difficulty,
|
|
rounds,
|
|
} as GameRequest);
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col items-center gap-6 w-full max-w-md mx-auto">
|
|
<div className="relative overflow-hidden w-full rounded-3xl border border-(--color-primary-light) bg-white dark:bg-black/10 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" />
|
|
<h1 className="relative text-3xl font-black tracking-tight text-(--color-text) mb-1">
|
|
lila
|
|
</h1>
|
|
<p className="relative text-sm text-(--color-text-muted)">
|
|
Set up your quiz
|
|
</p>
|
|
</div>
|
|
|
|
<div className="w-full rounded-3xl border border-(--color-primary-light) bg-white dark:bg-black/10 shadow-sm p-6 flex flex-col gap-5">
|
|
<SettingGroup
|
|
label="I speak"
|
|
options={SUPPORTED_LANGUAGE_CODES}
|
|
selected={sourceLanguage}
|
|
onSelect={handleSourceLanguage}
|
|
/>
|
|
<SettingGroup
|
|
label="I want to learn"
|
|
options={SUPPORTED_LANGUAGE_CODES}
|
|
selected={targetLanguage}
|
|
onSelect={handleTargetLanguage}
|
|
/>
|
|
<SettingGroup
|
|
label="Word type"
|
|
options={SUPPORTED_POS}
|
|
selected={pos}
|
|
onSelect={setPos}
|
|
/>
|
|
<SettingGroup
|
|
label="Difficulty"
|
|
options={DIFFICULTY_LEVELS}
|
|
selected={difficulty}
|
|
onSelect={setDifficulty}
|
|
/>
|
|
<SettingGroup
|
|
label="Rounds"
|
|
options={GAME_ROUNDS}
|
|
selected={rounds}
|
|
onSelect={setRounds}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleStart}
|
|
className="w-full py-4 rounded-2xl text-xl font-black 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"
|
|
>
|
|
Start
|
|
</button>
|
|
</div>
|
|
);
|
|
};
|