Add Create form with subject input and status polling
This commit is contained in:
1 parent
b39bdaaa78
commit
21376fd1b4
1 file changed
+131
@@ -0,0 +1,131 @@
|
||||
"use client";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
type Phase = "idle" | "submitting" | "generating" | "review" | "ready" | "failed";
|
||||
|
||||
export default function CreateForm({ level: initialLevel, enabled }: { level: string; enabled: boolean }) {
|
||||
const [level, setLevel] = useState(initialLevel);
|
||||
const [subject, setSubject] = useState("");
|
||||
const [phase, setPhase] = useState<Phase>("idle");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [slug, setSlug] = useState("");
|
||||
const poll = useRef<ReturnType<typeof setInterval> | undefined>(undefined);
|
||||
|
||||
useEffect(() => () => clearInterval(poll.current), []);
|
||||
|
||||
async function start(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setMsg("");
|
||||
setSlug("");
|
||||
setPhase("submitting");
|
||||
try {
|
||||
const res = await fetch("/api/create", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ level, subject }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setPhase("failed");
|
||||
setMsg(data.error || "Something went wrong.");
|
||||
return;
|
||||
}
|
||||
if (data.status === "needs_review") {
|
||||
setPhase("review");
|
||||
return;
|
||||
}
|
||||
setSlug(data.slug);
|
||||
setPhase("generating");
|
||||
watch(data.id, data.slug);
|
||||
} catch {
|
||||
setPhase("failed");
|
||||
setMsg("Couldn't reach the server.");
|
||||
}
|
||||
}
|
||||
|
||||
function watch(id: number, baseSlug: string) {
|
||||
clearInterval(poll.current);
|
||||
poll.current = setInterval(async () => {
|
||||
try {
|
||||
const r = await fetch(`/api/create/${id}`);
|
||||
const d = await r.json();
|
||||
if (d.status === "ready") {
|
||||
clearInterval(poll.current);
|
||||
setSlug(baseSlug);
|
||||
setPhase("ready");
|
||||
} else if (d.status === "failed" || d.status === "blocked") {
|
||||
clearInterval(poll.current);
|
||||
setPhase("failed");
|
||||
setMsg(d.error || "The drawing couldn't be made. Try a different subject.");
|
||||
}
|
||||
} catch {
|
||||
/* keep polling */
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
return (
|
||||
<div className="card" style={{ marginTop: 16 }}>
|
||||
<p className="muted" style={{ margin: 0 }}>
|
||||
The Create feature isn't turned on for this DrawIt instance yet. Ask your administrator to configure a
|
||||
drawing server (ComfyUI).
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const busy = phase === "submitting" || phase === "generating";
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 16, maxWidth: 560 }}>
|
||||
<form onSubmit={start} className="card" style={{ display: "grid", gap: 12 }}>
|
||||
<label className="field">
|
||||
<span style={{ fontWeight: 700 }}>What do you want to draw?</span>
|
||||
<input
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
placeholder="e.g. Zebra"
|
||||
maxLength={30}
|
||||
disabled={busy}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span style={{ fontWeight: 700 }}>Level</span>
|
||||
<select value={level} onChange={(e) => setLevel(e.target.value)} disabled={busy}>
|
||||
<option value="early-beginner">Early Beginner</option>
|
||||
<option value="beginner">Beginner</option>
|
||||
</select>
|
||||
</label>
|
||||
<button className="btn big" type="submit" disabled={busy || subject.trim().length < 2}>
|
||||
{busy ? "Drawing…" : "✨ Make my lesson"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{phase === "generating" && (
|
||||
<div className="card" style={{ marginTop: 12, textAlign: "center" }}>
|
||||
<p style={{ margin: 0 }}>🎨 Drawing your {subject || "picture"}… this can take a little while.</p>
|
||||
<p className="muted" style={{ marginTop: 6, fontSize: "0.9rem" }}>You can stay on this page.</p>
|
||||
</div>
|
||||
)}
|
||||
{phase === "review" && (
|
||||
<div className="card" style={{ marginTop: 12 }}>
|
||||
<p style={{ margin: 0 }}>📨 Thanks! "{subject}" was sent to a grown-up to approve. Check back soon.</p>
|
||||
</div>
|
||||
)}
|
||||
{phase === "ready" && (
|
||||
<div className="card" style={{ marginTop: 12, textAlign: "center" }}>
|
||||
<p style={{ marginTop: 0 }}>🎉 Your lesson is ready!</p>
|
||||
<Link className="btn big" href={`/learn/${level}/${slug}-trace`}>Start drawing</Link>
|
||||
</div>
|
||||
)}
|
||||
{phase === "failed" && (
|
||||
<div className="notice" style={{ marginTop: 12 }}>
|
||||
<p style={{ margin: 0 }}>{msg || "Something went wrong. Try a different subject."}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user