Initial commit of Vellum, an event photo product for guest uploads, host moderation, and original-quality galleries.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-07 19:36:14 -04:00
co-authored by Cursor
commit 27e2f196eb
149 changed files with 13847 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
import Link from "next/link";
import { CameraIcon } from "lucide-react";
import { createServerCaller } from "@/trpc/server";
import { Button } from "@/components/ui/button";
import { BrandMark } from "@/components/brand-mark";
import { formatEventDate } from "@/lib/utils";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export default async function HomePage() {
const caller = await createServerCaller();
const listed = await caller.event.listed();
const viewer = await caller.viewer.me();
return (
<main className="page-pad mx-auto flex min-h-[calc(100dvh-3.5rem)] w-full max-w-5xl flex-col gap-12 py-10 sm:min-h-[calc(100dvh-4rem)] sm:py-16">
<div className="reveal flex max-w-2xl flex-col gap-5">
<p className="text-xs font-medium tracking-[0.22em] text-primary uppercase sm:text-sm">
Photos from the day
</p>
<h1 className="font-display text-4xl leading-[1.1] sm:text-6xl">
Keep the originals. Share the day.
</h1>
<p className="max-w-xl text-base text-muted-foreground sm:text-lg">
Guests upload from a link. You choose what the public sees. Full-quality
files stay yours.
</p>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
{viewer.openSignup ? (
<Button asChild size="lg" className="tap-target w-full sm:w-auto">
<Link href="/sign-up">
<CameraIcon data-icon="inline-start" aria-hidden="true" />
Host an event
</Link>
</Button>
) : (
<Button asChild size="lg" className="tap-target w-full sm:w-auto">
<Link href="/sign-up">Have an invite?</Link>
</Button>
)}
<Button asChild variant="outline" size="lg" className="tap-target w-full sm:w-auto">
<Link href="/sign-in">Sign in</Link>
</Button>
</div>
</div>
{listed.length > 0 ? (
<section className="reveal-2 flex flex-col gap-4">
<h2 className="text-2xl font-semibold tracking-tight sm:text-3xl">Open galleries</h2>
<div className="grid gap-3 sm:grid-cols-2">
{listed.map((event) => (
<Link
key={event.id}
href={`/e/${event.slug}`}
className="group block min-w-0 rounded-xl focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none"
>
<Card className="h-full transition-transform duration-200 ease-out group-hover:-translate-y-0.5 group-hover:shadow-md motion-reduce:transition-none motion-reduce:group-hover:translate-y-0">
<CardHeader>
<div className="mb-2 flex size-10 items-center justify-center rounded-full bg-accent text-primary">
<BrandMark className="size-5" />
</div>
<CardTitle className="truncate text-xl font-semibold tracking-tight">
{event.title}
</CardTitle>
<CardDescription>
{formatEventDate(event.startsAt) ?? "Open gallery"}
</CardDescription>
</CardHeader>
<CardContent>
<span className="text-sm text-primary">View gallery</span>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
) : null}
</main>
);
}