SPB Git

spb/worthdoing Public

Autonomous investigation agent that discovers, challenges, and ranks things genuinely worth doing — Claude + Firecrawl, Next.js 16, PostgreSQL

TypeScript 91.5% SQL 5.8% CSS 2.2%
3.0 KB · 89 lines tsx
Raw Blame History
1/**2 * WorthDoing.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: src/components/wd/ObjectiveForm.tsx6 * Description: Discover-page objective input — creates an investigation and routes to the live view.7 */8"use client";910import { useState } from "react";11import { useRouter } from "next/navigation";1213const SUGGESTIONS = [14  "Find things worth doing in local AI",15  "What's worth building for independent booksellers?",16  "Underexplored opportunities in home energy monitoring",17  "What should exist for parents of kids with ADHD?",18];1920export function ObjectiveForm() {21  const router = useRouter();22  const [objective, setObjective] = useState("");23  const [submitting, setSubmitting] = useState(false);24  const [error, setError] = useState<string | null>(null);2526  const start = async (text: string) => {27    if (submitting || text.trim().length < 8) return;28    setSubmitting(true);29    setError(null);30    try {31      const res = await fetch("/api/investigations", {32        method: "POST",33        headers: { "Content-Type": "application/json" },34        body: JSON.stringify({ objective: text.trim() }),35      });36      const data = await res.json();37      if (!res.ok) throw new Error(data.error ?? "Failed to start the investigation.");38      router.push(`/investigate/${data.id}`);39    } catch (e) {40      setError(e instanceof Error ? e.message : "Something went wrong.");41      setSubmitting(false);42    }43  };4445  return (46    <div className="w-full">47      <form48        onSubmit={(e) => {49          e.preventDefault();50          void start(objective);51        }}52        className="flex flex-col gap-2 sm:flex-row"53      >54        <input55          value={objective}56          onChange={(e) => setObjective(e.target.value)}57          placeholder="What domain should the agent investigate?"58          maxLength={500}59          className="min-h-[52px] flex-1 rounded-xl border border-line bg-card px-4 text-[15px] text-ink shadow-sm outline-none transition-colors placeholder:text-ink-soft/60 focus:border-verdict focus:ring-2 focus:ring-verdict/20"60        />61        <button62          type="submit"63          disabled={submitting || objective.trim().length < 8}64          className="min-h-[52px] rounded-xl bg-ink px-6 text-[15px] font-medium text-paper transition-opacity hover:opacity-85 disabled:opacity-40"65        >66          {submitting ? "Launching…" : "Investigate"}67        </button>68      </form>69      {error && <p className="mt-2 text-sm text-rust">{error}</p>}70      <div className="mt-4 flex flex-wrap gap-2">71        {SUGGESTIONS.map((s) => (72          <button73            key={s}74            type="button"75            disabled={submitting}76            onClick={() => {77              setObjective(s);78              void start(s);79            }}80            className="min-h-[44px] rounded-full border border-line bg-card px-4 py-2 text-left text-[13px] text-ink-soft transition-colors hover:border-verdict/50 hover:text-ink disabled:opacity-40"81          >82            {s}83          </button>84        ))}85      </div>86    </div>87  );88}89