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%
4.1 KB · 112 lines tsx
Raw Blame History
1/**2 * WorthDoing.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: src/app/page.tsx6 * Description: Discover page — launch an investigation; top-ranked opportunities and recent activity.7 */8import Link from "next/link";9import { desc, eq } from "drizzle-orm";10import { db } from "@/lib/db/client";11import { investigations, opportunities } from "@/lib/db/schema";12import { ObjectiveForm } from "@/components/wd/ObjectiveForm";13import { OpportunityCard } from "@/components/wd/OpportunityCard";14import { InvestigationStatusBadge } from "@/components/wd/badges";1516export const dynamic = "force-dynamic";1718export default async function DiscoverPage() {19  const [topOpportunities, recent] = await Promise.all([20    db21      .select({22        id: opportunities.id,23        title: opportunities.title,24        summary: opportunities.summary,25        worthScore: opportunities.worthScore,26        evidenceConfidence: opportunities.evidenceConfidence,27        objective: investigations.objective,28        reportMd: opportunities.reportMd,29      })30      .from(opportunities)31      .innerJoin(investigations, eq(investigations.id, opportunities.investigationId))32      .orderBy(desc(opportunities.worthScore))33      .limit(6),34    db35      .select({36        id: investigations.id,37        objective: investigations.objective,38        status: investigations.status,39        createdAt: investigations.createdAt,40      })41      .from(investigations)42      .orderBy(desc(investigations.createdAt))43      .limit(5),44  ]);4546  return (47    <div className="mx-auto w-full max-w-6xl px-4 sm:px-6">48      <section className="py-14 sm:py-20">49        <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-verdict">50          autonomous opportunity intelligence51        </p>52        <h1 className="mt-4 max-w-3xl font-heading text-4xl font-semibold leading-[1.08] tracking-tight text-ink sm:text-6xl">53          Google finds what exists.54          <br />55          <span className="italic text-verdict">WorthDoing</span> finds what should.56        </h1>57        <p className="mt-5 max-w-2xl text-base leading-relaxed text-ink-soft sm:text-lg">58          Give the agent a domain. It searches, reads, forms falsifiable hypotheses, hunts for59          counterevidence, and ranks what survives — every conclusion traced to real sources.60        </p>61        <div className="mt-8 max-w-3xl">62          <ObjectiveForm />63        </div>64      </section>6566      {topOpportunities.length > 0 && (67        <section className="pb-12">68          <div className="mb-4 flex items-baseline justify-between">69            <h2 className="font-heading text-2xl font-semibold text-ink">Worth doing</h2>70            <Link href="/explore" className="font-mono text-xs uppercase tracking-wider text-tele hover:underline">71              explore all →72            </Link>73          </div>74          <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">75            {topOpportunities.map((o) => (76              <OpportunityCard77                key={o.id}78                id={o.id}79                title={o.title}80                summary={o.summary}81                worthScore={o.worthScore}82                evidenceConfidence={o.evidenceConfidence}83                objective={o.objective}84                hasReport={!!o.reportMd}85              />86            ))}87          </div>88        </section>89      )}9091      {recent.length > 0 && (92        <section className="pb-16">93          <h2 className="mb-4 font-heading text-2xl font-semibold text-ink">Recent investigations</h2>94          <ul className="divide-y divide-line rounded-xl border border-line bg-card">95            {recent.map((r) => (96              <li key={r.id}>97                <Link98                  href={`/investigate/${r.id}`}99                  className="flex min-h-[44px] items-center justify-between gap-3 px-4 py-3 transition-colors hover:bg-secondary/50"100                >101                  <span className="min-w-0 truncate text-sm text-ink">{r.objective}</span>102                  <InvestigationStatusBadge status={r.status} live />103                </Link>104              </li>105            ))}106          </ul>107        </section>108      )}109    </div>110  );111}112