Skip to content
File

Blob: src/client/pages/landing-page.tsx

typescript84 lines
1import { Link, useNavigate, useOutletContext } from "react-router-dom";
2import { Clock, Mail } from "lucide-react";
3import { CreateInbox } from "@/client/components/create-inbox";
4import { Card } from "@/client/components/ui";
5import type { AppShellContext } from "@/client/components/app-shell";
6import { fullDate } from "@/client/lib/time";
7 
8export function LandingPage() {
9 const { sessions, onCreated } = useOutletContext<AppShellContext>();
10 const navigate = useNavigate();
11 
12 return (
13 <div className="animate-slide-up space-y-8">
14 {/* Compact hero */}
15 <section className="text-center">
16 <h1 className="font-display text-3xl font-extrabold tracking-tight text-zinc-100 sm:text-4xl">
17 Test inboxes for transactional email.
18 </h1>
19 <p className="mx-auto mt-3 max-w-xl text-sm leading-relaxed text-zinc-400">
20 Real addresses that catch real email. Watch signup confirmations, password resets, and magic links land the
21 moment your app sends them.
22 </p>
23 </section>
24
25 {/* Create form + recent inboxes */}
26 <div className="grid gap-6 lg:grid-cols-[minmax(280px,440px)_minmax(0,1fr)]">
27 <CreateInbox
28 onCreated={(session) => {
29 onCreated(session);
30 navigate(`/inbox/${encodeURIComponent(session.address)}`);
31 }}
32 />
33
34 <Card>
35 <h2 className="flex items-center gap-2 text-sm font-semibold uppercase tracking-wider text-zinc-500">
36 <Mail className="h-3.5 w-3.5" />
37 Recent inboxes
38 </h2>
39
40 {sessions.length === 0 ? (
41 <p className="mt-4 text-sm leading-relaxed text-zinc-400">
42 No inboxes yet. They'll show up here when you create one.
43 </p>
44 ) : (
45 <div className="mt-4 space-y-1.5">
46 {sessions.map((session, i) => {
47 const expires = new Date(session.expiresAt);
48 const remaining = expires.getTime() - Date.now();
49 const alive = remaining > 0;
50
51 return (
52 <Link
53 key={session.address}
54 className="animate-scale-fade opacity-0 group flex items-center gap-3 rounded-lg border border-zinc-800/50 bg-zinc-800/30 px-4 py-3 hover:border-zinc-700/60 hover:bg-zinc-800/60 hover:-translate-y-0.5 transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-canvas"
55 style={{ animationDelay: `${Math.min(i, 8) * 60}ms` }}
56 to={`/inbox/${encodeURIComponent(session.address)}`}
57 >
58 <span
59 className={`inline-grid h-7 w-7 shrink-0 place-items-center rounded-full ${
60 alive ? "bg-accent-500/10 text-accent-400" : "bg-zinc-800 text-zinc-600"
61 }`}
62 >
63 <Mail className="h-3.5 w-3.5" />
64 </span>
65 <div className="min-w-0 flex-1">
66 <strong className="block truncate text-sm font-medium text-zinc-200 group-hover:text-accent-400">
67 {session.address}
68 </strong>
69 <span className="flex items-center gap-1 text-xs text-zinc-400">
70 <Clock className="h-3 w-3" />
71 {alive ? `expires ${fullDate(session.expiresAt)}` : "expired"}
72 </span>
73 </div>
74 </Link>
75 );
76 })}
77 </div>
78 )}
79 </Card>
80 </div>
81 </div>
82 );
83}