TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { requireUser } from '@/lib/auth/session';4import { listNotifications } from '@/lib/account/queries';5import { clearNotificationsAction, markReadAction } from '@/lib/account/actions';6import { PageHeader, btnSecondary, btnGhost } from '@/components/account/page-header';7import { Card, EmptyState, Badge } from '@/components/ui/primitives';8import { fmtRelative } from '@/lib/format';910export const metadata: Metadata = { title: 'Inbox', robots: { index: false } };1112const TONE: Record<string, 'index' | 'alert' | 'gain' | 'neutral' | 'rarity'> = { alert: 'alert', security: 'loss' as never, system: 'neutral', digest: 'index', target_hit: 'gain', radar: 'rarity' };1314export default async function InboxPage() {15 const u = await requireUser('/notifications');16 const rows = await listNotifications(u.id, 100);17 const unread = rows.filter((r) => !r.readAt).length;18 return (19 <>20 <PageHeader21 title="Inbox"22 description={`${unread} unread · alerts, price targets, security events and digests.`}23 actions={24 <>25 <form action={markReadAction}>26 <input type="hidden" name="id" value="all" />27 <button className={btnSecondary}>Mark all read</button>28 </form>29 <form action={clearNotificationsAction}>30 <button className={btnGhost}>Clear read</button>31 </form>32 </>33 }34 />35 <Card>36 {rows.length === 0 ? (37 <EmptyState title="Nothing here yet" description="When an alert triggers or a price target is hit, it lands here first." action={<Link href="/alerts" className={btnSecondary}>Create an alert</Link>} />38 ) : (39 <ul className="divide-y divide-border">40 {rows.map((n) => (41 <li key={n.id} className={`flex items-start gap-3 px-4 py-3 ${n.readAt ? '' : 'bg-sunken'}`}>42 <span className={`mt-1.5 h-2 w-2 shrink-0 rounded-full ${n.readAt ? 'bg-transparent' : 'bg-index'}`} />43 <div className="min-w-0 flex-1">44 <div className="flex flex-wrap items-center gap-2">45 <Badge tone={TONE[n.kind] ?? 'neutral'}>{n.kind.replace(/_/g, ' ')}</Badge>46 <p className="text-sm font-medium">{n.href ? <Link href={n.href} className="hover:underline">{n.title}</Link> : n.title}</p>47 <span className="text-[11px] text-subtle">{fmtRelative(n.createdAt)}</span>48 </div>49 {n.body ? <p className="mt-0.5 text-xs text-muted">{n.body}</p> : null}50 </div>51 {!n.readAt ? (52 <form action={markReadAction}>53 <input type="hidden" name="id" value={n.id} />54 <button className={btnGhost}>Read</button>55 </form>56 ) : null}57 </li>58 ))}59 </ul>60 )}61 </Card>62 </>63 );64}65