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%
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