profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
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