SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
3.0 KB · 64 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { requireUser } from '@/lib/auth/session';4import { listSavedSearches } from '@/lib/account/queries';5import { deleteSavedSearchAction, toggleSavedSearchNotifyAction } from '@/lib/account/actions';6import { PageHeader, btnDanger, btnGhost } from '@/components/account/page-header';7import { Card, CardHeader, EmptyState, Badge } from '@/components/ui/primitives';8import { fmtRelative } from '@/lib/format';9import { SaveSearchForm } from './save-form';1011export const metadata: Metadata = { title: 'Saved searches', robots: { index: false } };1213export default async function SavedPage({ searchParams }: { searchParams: Promise<Record<string, string | undefined>> }) {14  const sp = await searchParams;15  const u = await requireUser('/saved');16  const rows = await listSavedSearches(u.id);17  return (18    <>19      <PageHeader title="Saved searches" description="Keep your Explore and Search filters one click away. Searches with notifications on are re-run daily; new matches land in your inbox." />20      <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_360px]">21        <Card>22          <CardHeader title={`Saved · ${rows.length}`} />23          {rows.length === 0 ? (24            <EmptyState title="No saved searches" description="Run a search, then use “Save this search” — or paste a RareIndex search URL on the right." />25          ) : (26            <ul className="divide-y divide-border">27              {rows.map((s) => (28                <li key={s.id} className="flex items-center justify-between gap-3 px-4 py-2.5 text-sm">29                  <div className="min-w-0">30                    <Link href={s.url} className="block truncate font-medium hover:underline">31                      {s.name}32                    </Link>33                    <p className="truncate text-[11px] text-subtle">34                      {s.url} · saved {fmtRelative(s.createdAt)}35                      {s.lastRunAt ? ` · last run ${fmtRelative(s.lastRunAt)} (${s.lastCount ?? 0} results)` : ''}36                    </p>37                  </div>38                  <div className="flex items-center gap-1">39                    {s.notify ? <Badge tone="index">notify</Badge> : null}40                    <form action={toggleSavedSearchNotifyAction}>41                      <input type="hidden" name="id" value={s.id} />42                      <button className={btnGhost}>{s.notify ? 'Mute' : 'Notify'}</button>43                    </form>44                    <form action={deleteSavedSearchAction}>45                      <input type="hidden" name="id" value={s.id} />46                      <button className={btnDanger}>Delete</button>47                    </form>48                  </div>49                </li>50              ))}51            </ul>52          )}53        </Card>54        <Card>55          <CardHeader title="Save a search" />56          <div className="p-4">57            <SaveSearchForm initialUrl={sp.url ?? ''} initialName={sp.name ?? ''} />58          </div>59        </Card>60      </div>61    </>62  );63}64