SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
4.1 KB · 91 lines tsx
Raw Blame History
1import Link from "next/link";2import { ExternalLink, FileCheck2 } from "lucide-react";3import { getWorkspace } from "@/lib/session";4import { listLegalAcceptances } from "@/lib/queries/account";5import { formatDate } from "@/lib/format";6import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";7import { EmptyState } from "@/components/ui/empty-state";8import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";910export const dynamic = "force-dynamic";1112const DOCUMENTS: Record<string, { label: string; href: string }> = {13  terms: { label: "Terms of Service", href: "/legal/terms" },14  privacy: { label: "Privacy Policy", href: "/legal/privacy" },15  acceptable_use: { label: "Acceptable Use Policy", href: "/legal/acceptable-use" },16};1718export default async function LegalPage() {19  const ws = await getWorkspace();20  const rows = await listLegalAcceptances(ws.user.id);2122  return (23    <div className="grid gap-6">24      <div className="grid gap-4">25        <div>26          <h2 className="text-[15px] font-semibold tracking-tight">Accepted documents</h2>27          <p className="text-[13px] text-fg-muted">Every version you agreed to, with the time and IP address recorded at acceptance.</p>28        </div>29        {rows.length === 0 ? (30          <EmptyState icon={FileCheck2} title="No acceptances recorded" description="Acceptances are recorded when you sign up. If your account predates this record, they will be requested the next time terms change." />31        ) : (32          <div className="overflow-hidden rounded-lg border border-border bg-bg-elevated shadow-xs">33            <Table>34              <TableHeader>35                <TableRow className="hover:bg-transparent">36                  <TableHead>Document</TableHead>37                  <TableHead>Version</TableHead>38                  <TableHead>Accepted</TableHead>39                  <TableHead>IP address</TableHead>40                </TableRow>41              </TableHeader>42              <TableBody>43                {rows.map((r) => {44                  const doc = DOCUMENTS[r.document];45                  return (46                    <TableRow key={r.id}>47                      <TableCell>48                        {doc ? (49                          <Link href={doc.href} className="inline-flex items-center gap-1 font-medium underline-offset-4 hover:underline">50                            {doc.label} <ExternalLink className="size-3 text-fg-subtle" aria-hidden />51                          </Link>52                        ) : (53                          <span className="font-medium">{r.document}</span>54                        )}55                      </TableCell>56                      <TableCell className="font-mono text-[12.5px] tabular">{r.version}</TableCell>57                      <TableCell className="whitespace-nowrap tabular text-fg-muted">{formatDate(r.acceptedAt)}</TableCell>58                      <TableCell className="font-mono text-[12.5px] tabular">{r.ipAddress ?? "—"}</TableCell>59                    </TableRow>60                  );61                })}62              </TableBody>63            </Table>64          </div>65        )}66      </div>6768      <Card>69        <CardHeader>70          <CardTitle>Current documents</CardTitle>71          <CardDescription>The versions in force today. We email you before material changes take effect.</CardDescription>72        </CardHeader>73        <CardContent>74          <ul className="grid gap-2 text-[13.5px] sm:grid-cols-3">75            {Object.values(DOCUMENTS).map((d) => (76              <li key={d.href}>77                <Link href={d.href} className="inline-flex items-center gap-1.5 text-fg underline-offset-4 hover:underline">78                  {d.label} <ExternalLink className="size-3 text-fg-subtle" aria-hidden />79                </Link>80              </li>81            ))}82          </ul>83          <p className="mt-4 text-[12.5px] text-fg-subtle">84            Data processing questions: <a href="mailto:privacy@fetcha.co" className="underline-offset-4 hover:underline">privacy@fetcha.co</a>85          </p>86        </CardContent>87      </Card>88    </div>89  );90}91