| "use client"; |
|
|
| import { FormEvent, useState } from "react"; |
|
|
| type RunTaskResponse = { |
| providerUsed: string; |
| goal: string; |
| successDefinition: string; |
| executionTrace: string[]; |
| finalResult: string; |
| planNotes?: string | null; |
| error?: string; |
| }; |
|
|
| const DEFAULT_PROMPT = "Plan and summarize a concise hackathon demo for a task runner UI."; |
|
|
| export default function Home() { |
| const [prompt, setPrompt] = useState(DEFAULT_PROMPT); |
| const [result, setResult] = useState<RunTaskResponse | null>(null); |
| const [error, setError] = useState<string | null>(null); |
| const [isRunning, setIsRunning] = useState(false); |
|
|
| async function handleSubmit(event: FormEvent<HTMLFormElement>) { |
| event.preventDefault(); |
| const trimmed = prompt.trim(); |
|
|
| if (!trimmed) { |
| setError("Enter a prompt before running the task."); |
| return; |
| } |
|
|
| setIsRunning(true); |
| setError(null); |
|
|
| try { |
| const response = await fetch("/api/run-task", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ prompt: trimmed }), |
| }); |
|
|
| const payload = (await response.json()) as RunTaskResponse; |
|
|
| if (!response.ok) { |
| throw new Error(payload.error || "Task run failed."); |
| } |
|
|
| setResult(payload); |
| } catch (requestError) { |
| setResult(null); |
| setError(requestError instanceof Error ? requestError.message : "Task run failed."); |
| } finally { |
| setIsRunning(false); |
| } |
| } |
|
|
| return ( |
| <main className="app-shell"> |
| <div className="ambient ambient-one" /> |
| <div className="ambient ambient-two" /> |
| |
| <section className="hero panel"> |
| <p className="eyebrow">Hackathon demo</p> |
| <h1>Shared planner, shared provider, minimal surface.</h1> |
| <p className="hero-copy"> |
| Run the same task planning path used by the worker services, but render it in a single Next.js page that is easy to ship to Hugging Face Spaces. |
| </p> |
| </section> |
| |
| <section className="workspace"> |
| <form className="panel form-panel" onSubmit={handleSubmit}> |
| <label className="label" htmlFor="prompt"> |
| Prompt |
| </label> |
| <textarea |
| id="prompt" |
| className="prompt-input" |
| value={prompt} |
| onChange={(event) => setPrompt(event.target.value)} |
| spellCheck={false} |
| placeholder="Describe the task you want planned and summarized." |
| /> |
| |
| <div className="actions"> |
| <button className="run-button" type="submit" disabled={isRunning}> |
| {isRunning ? "Running..." : "Run task"} |
| </button> |
| <span className="hint">POST /api/run-task</span> |
| </div> |
| |
| {error ? <p className="error-text">{error}</p> : null} |
| </form> |
| |
| <div className="results-column"> |
| <section className="panel result-panel"> |
| <div className="panel-header"> |
| <div> |
| <p className="panel-kicker">Provider used</p> |
| <h2>{result?.providerUsed ?? "Waiting for a run"}</h2> |
| </div> |
| {result ? <span className="status-pill">Ready</span> : <span className="status-pill muted">Idle</span>} |
| </div> |
| |
| <dl className="meta-grid"> |
| <div> |
| <dt>Goal</dt> |
| <dd>{result?.goal ?? "No task has been executed yet."}</dd> |
| </div> |
| <div> |
| <dt>Success definition</dt> |
| <dd>{result?.successDefinition ?? "Run a prompt to see the planner output."}</dd> |
| </div> |
| </dl> |
| </section> |
| |
| <section className="panel trace-panel"> |
| <div className="panel-header"> |
| <div> |
| <p className="panel-kicker">Execution trace</p> |
| <h2>Planner steps</h2> |
| </div> |
| </div> |
| |
| {result?.executionTrace?.length ? ( |
| <ol className="trace-list"> |
| {result.executionTrace.map((entry) => ( |
| <li key={entry}>{entry}</li> |
| ))} |
| </ol> |
| ) : ( |
| <p className="empty-state">The execution trace will appear here after the first run.</p> |
| )} |
| |
| {result?.planNotes ? <p className="plan-notes">{result.planNotes}</p> : null} |
| </section> |
| |
| <section className="panel output-panel"> |
| <div className="panel-header"> |
| <div> |
| <p className="panel-kicker">Final result</p> |
| <h2>Provider output</h2> |
| </div> |
| </div> |
| |
| <pre className="output-box">{result?.finalResult ?? "Run a task to see the final result."}</pre> |
| </section> |
| </div> |
| </section> |
| </main> |
| ); |
| } |