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:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user