page.tsx
4,403 bytes
| 1 | import { ArrowRight, GitCompareArrows, UsersRound } from 'lucide-react'; |
|---|---|
| 2 | import type { Metadata } from 'next'; |
| 3 | import Link from 'next/link'; |
| 4 | import { notFound } from 'next/navigation'; |
| 5 | import { cache } from 'react'; |
| 6 | import { DebateReplay } from '@/components/debate/debate-replay'; |
| 7 | import { Badge } from '@/components/ui/badge'; |
| 8 | import { Button } from '@/components/ui/button'; |
| 9 | import { loadDebateByShareToken } from '@/db/repositories'; |
| 10 | import { truncate } from '@/lib/utils'; |
| 11 | |
| 12 | export const dynamic = 'force-dynamic'; |
| 13 | |
| 14 | const getSharedResult = cache(loadDebateByShareToken); |
| 15 | |
| 16 | export async function generateMetadata({ |
| 17 | params, |
| 18 | }: { |
| 19 | params: Promise<{ token: string }>; |
| 20 | }): Promise<Metadata> { |
| 21 | const { token } = await params; |
| 22 | const result = await getSharedResult(token); |
| 23 | if (!result) return { title: 'Shared verdict', robots: { index: false, follow: false } }; |
| 24 | |
| 25 | const title = `Verdict: ${truncate(result.config.question, 70)}`; |
| 26 | const description = `See how ${result.participants.length} AI models compared this question, challenged each other, and reached a final recommendation.`; |
| 27 | return { |
| 28 | title, |
| 29 | description, |
| 30 | robots: { index: false, follow: false }, |
| 31 | openGraph: { title, description, type: 'article' }, |
| 32 | twitter: { card: 'summary_large_image', title, description }, |
| 33 | }; |
| 34 | } |
| 35 | |
| 36 | export default async function SharePage({ params }: { params: Promise<{ token: string }> }) { |
| 37 | const { token } = await params; |
| 38 | const result = await getSharedResult(token); |
| 39 | if (!result) notFound(); |
| 40 | |
| 41 | return ( |
| 42 | <div className="container max-w-6xl py-8 sm:py-12"> |
| 43 | <section className="relative overflow-hidden rounded-[1.5rem] border bg-card p-5 sm:p-7"> |
| 44 | <div className="absolute right-[-4rem] top-[-5rem] h-48 w-48 rounded-full bg-primary/10 blur-3xl" /> |
| 45 | <div className="relative flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between"> |
| 46 | <div className="max-w-3xl"> |
| 47 | <Badge variant="secondary" className="mb-4 gap-1.5 rounded-full"> |
| 48 | <GitCompareArrows className="h-3.5 w-3.5 text-primary" /> |
| 49 | Shared Roundtable verdict |
| 50 | </Badge> |
| 51 | <h1 className="font-display text-2xl font-semibold leading-tight tracking-[-0.03em] sm:text-4xl"> |
| 52 | {result.config.question} |
| 53 | </h1> |
| 54 | <div className="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 text-xs text-muted-foreground"> |
| 55 | <span className="flex items-center gap-1.5"> |
| 56 | <UsersRound className="h-3.5 w-3.5" /> |
| 57 | {result.participants.length} independent opinions |
| 58 | </span> |
| 59 | <span>{result.totals.rounds} review rounds</span> |
| 60 | <span>Original opinions and changes included</span> |
| 61 | </div> |
| 62 | </div> |
| 63 | <Button asChild className="shrink-0 rounded-xl"> |
| 64 | <Link href="/debate"> |
| 65 | Ask your own question |
| 66 | <ArrowRight className="h-4 w-4" /> |
| 67 | </Link> |
| 68 | </Button> |
| 69 | </div> |
| 70 | </section> |
| 71 | |
| 72 | <div className="mt-8"> |
| 73 | <DebateReplay result={result} showActions={false} /> |
| 74 | </div> |
| 75 | |
| 76 | <section className="mt-10 rounded-[1.5rem] border bg-foreground px-6 py-9 text-background sm:px-10"> |
| 77 | <div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between"> |
| 78 | <div> |
| 79 | <p className="text-xs font-bold uppercase tracking-[0.16em] text-background/60"> |
| 80 | Your turn |
| 81 | </p> |
| 82 | <h2 className="mt-2 font-display text-2xl font-semibold tracking-tight sm:text-3xl"> |
| 83 | Agree with the verdict? Put it back on the table. |
| 84 | </h2> |
| 85 | <p className="mt-2 max-w-2xl text-sm leading-relaxed text-background/70"> |
| 86 | Ask a new question or run this one again with your own model choices and review rules. |
| 87 | </p> |
| 88 | </div> |
| 89 | <div className="flex shrink-0 flex-wrap gap-2"> |
| 90 | <Button asChild variant="secondary"> |
| 91 | <Link href={{ pathname: '/debate', query: { q: result.config.question } }}> |
| 92 | Run this question again |
| 93 | </Link> |
| 94 | </Button> |
| 95 | <Button asChild className="bg-background text-foreground hover:bg-background/90"> |
| 96 | <Link href="/debate">Ask something else</Link> |
| 97 | </Button> |
| 98 | </div> |
| 99 | </div> |
| 100 | </section> |
| 101 | </div> |
| 102 | ); |
| 103 | } |
| 104 | |