alpha01 filetti: web app, crossword service and tor batch
This commit is contained in:
183
webapp/components/crossword-config-form.tsx
Normal file
183
webapp/components/crossword-config-form.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
"use client";
|
||||
|
||||
import { startTransition, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import type { Locale } from "@/lib/i18n";
|
||||
import type { CrosswordRequest } from "@/lib/types";
|
||||
|
||||
const DEFAULTS = {
|
||||
topic: "transport",
|
||||
difficulty: "medium",
|
||||
initialWordCount: 19,
|
||||
themedFillCount: 10,
|
||||
targetEmptyRatio: 0.1667,
|
||||
seed: 2,
|
||||
lexiconFile: "lexicon_it_curated_llm_aggressive.json",
|
||||
};
|
||||
|
||||
type CrosswordConfigFormProps = {
|
||||
locale: Locale;
|
||||
dict: {
|
||||
topic: string;
|
||||
difficulty: string;
|
||||
initialWords: string;
|
||||
themedFillCount: string;
|
||||
targetEmptyRatio: string;
|
||||
seed: string;
|
||||
lexiconFile: string;
|
||||
submit: string;
|
||||
submitting: string;
|
||||
note: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function CrosswordConfigForm({ locale, dict }: CrosswordConfigFormProps) {
|
||||
const router = useRouter();
|
||||
const [topic, setTopic] = useState(DEFAULTS.topic);
|
||||
const [difficulty, setDifficulty] = useState(DEFAULTS.difficulty);
|
||||
const [initialWordCount, setInitialWordCount] = useState(DEFAULTS.initialWordCount);
|
||||
const [themedFillCount, setThemedFillCount] = useState(DEFAULTS.themedFillCount);
|
||||
const [targetEmptyRatio, setTargetEmptyRatio] = useState(DEFAULTS.targetEmptyRatio);
|
||||
const [seed, setSeed] = useState(DEFAULTS.seed);
|
||||
const [lexiconFile, setLexiconFile] = useState(DEFAULTS.lexiconFile);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
setErrorMessage("");
|
||||
|
||||
const requestPayload: CrosswordRequest = {
|
||||
schema_version: "1.0",
|
||||
request_id: `req-${Date.now()}`,
|
||||
requested_at: new Date().toISOString(),
|
||||
generator: {
|
||||
topic: topic
|
||||
.split(",")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean),
|
||||
difficulty,
|
||||
seed,
|
||||
initial_word_count: initialWordCount,
|
||||
themed_fill_count: themedFillCount,
|
||||
target_empty_ratio: targetEmptyRatio,
|
||||
diffxy: 7,
|
||||
time_limit_seconds: 8,
|
||||
max_candidates_per_word: 12,
|
||||
lexicon_file: lexiconFile,
|
||||
definitions_enabled: true,
|
||||
definition_style: "classic",
|
||||
preferred_output_language: locale,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/crosswords/generate", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestPayload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorPayload = await response.json().catch(() => ({}));
|
||||
throw new Error(errorPayload.message || "Unable to generate crossword");
|
||||
}
|
||||
|
||||
const generated = await response.json();
|
||||
|
||||
startTransition(() => {
|
||||
router.push(`/${locale}/crosswords/${generated.crossword_id}`);
|
||||
});
|
||||
} catch (error) {
|
||||
setErrorMessage(error instanceof Error ? error.message : "Unexpected error");
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="config-form" onSubmit={handleSubmit}>
|
||||
<div className="config-form__grid">
|
||||
<div className="field">
|
||||
<label htmlFor="topic">{dict.topic}</label>
|
||||
<input id="topic" value={topic} onChange={(event) => setTopic(event.target.value)} />
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="difficulty">{dict.difficulty}</label>
|
||||
<select
|
||||
id="difficulty"
|
||||
value={difficulty}
|
||||
onChange={(event) => setDifficulty(event.target.value)}
|
||||
>
|
||||
<option value="easy">easy</option>
|
||||
<option value="medium">medium</option>
|
||||
<option value="hard">hard</option>
|
||||
<option value="expert">expert</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="initialWordCount">{dict.initialWords}</label>
|
||||
<input
|
||||
id="initialWordCount"
|
||||
type="number"
|
||||
value={initialWordCount}
|
||||
onChange={(event) => setInitialWordCount(Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="themedFillCount">{dict.themedFillCount}</label>
|
||||
<input
|
||||
id="themedFillCount"
|
||||
type="number"
|
||||
value={themedFillCount}
|
||||
onChange={(event) => setThemedFillCount(Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="targetEmptyRatio">{dict.targetEmptyRatio}</label>
|
||||
<input
|
||||
id="targetEmptyRatio"
|
||||
type="number"
|
||||
step="0.0001"
|
||||
value={targetEmptyRatio}
|
||||
onChange={(event) => setTargetEmptyRatio(Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="seed">{dict.seed}</label>
|
||||
<input
|
||||
id="seed"
|
||||
type="number"
|
||||
value={seed}
|
||||
onChange={(event) => setSeed(Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="lexiconFile">{dict.lexiconFile}</label>
|
||||
<input
|
||||
id="lexiconFile"
|
||||
value={lexiconFile}
|
||||
onChange={(event) => setLexiconFile(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="actions">
|
||||
<button className="button" type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? dict.submitting : dict.submit}
|
||||
</button>
|
||||
<span className="muted">{dict.note}</span>
|
||||
</div>
|
||||
|
||||
{errorMessage ? <p className="form-error">{errorMessage}</p> : null}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user