SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
6.1 KB · 134 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import Link from "next/link";3import { Empty, Page, PageHeader } from "@/components/ui/section";4import { apiEnvelope } from "@/lib/api";5import { cx, formatDateTime, instrumentHref } from "@/lib/format";6import type { Filing } from "@/lib/types";78export const metadata: Metadata = { title: "Filings", description: "Regulatory filings observed on SEC EDGAR, linked to instruments by CIK.", alternates: { canonical: "/filings" } };9export const dynamic = "force-dynamic";1011const FORMS = ["8-K", "10-K", "10-Q", "6-K", "S-1", "SC 13D", "SC 13G", "DEF 14A", "4", "424B4", "13F-HR"];1213export default async function FilingsPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {14  const sp = await searchParams;15  const form = typeof sp.form === "string" ? sp.form : "";16  const q = typeof sp.q === "string" ? sp.q : "";17  const cik = typeof sp.cik === "string" ? sp.cik : "";18  const page = Math.max(1, Number(sp.page ?? 1) || 1);19  const params = new URLSearchParams({ limit: "100", offset: String((page - 1) * 100) });20  if (form) params.set("form", form);21  if (q) params.set("q", q);22  if (cik) params.set("cik", cik);23  const env = await apiEnvelope<Filing[]>(`/v1/filings?${params}`);24  const rows = env.data;25  const link = (patch: Record<string, string | number>) => {26    const u = new URLSearchParams();27    for (const [k, v] of Object.entries({ form, q, cik, page: page > 1 ? page : "", ...patch })) if (v !== "" && v !== 0) u.set(k, String(v));28    const s = u.toString();29    return s ? `/filings?${s}` : "/filings";30  };31  return (32    <Page wide>33      <PageHeader kicker="Regulatory" title="Filings" lead={`Documents filed on SEC EDGAR as Market Atlas observes them (every 2 minutes). ${Number(env.meta.last_7_days ?? 0).toLocaleString("en-US")} filings in the last 7 days. Material forms (8-K, 10-K/Q, 6-K, S-1, 13D/G…) also appear as events.`} />34      <div className="mb-3 flex flex-wrap items-center gap-1.5">35        <Link href={link({ form: "", page: "" })} className={cx("inline-flex h-8 items-center rounded-full border px-3 text-xs", !form ? "border-ink bg-ink text-canvas" : "border-rule text-ink-2 hover:border-rule-strong")}>36          All forms37        </Link>38        {FORMS.map((f) => (39          <Link key={f} href={link({ form: f, page: "" })} className={cx("mono inline-flex h-8 items-center rounded-full border px-3 text-xs", form === f ? "border-ink bg-ink text-canvas" : "border-rule text-ink-2 hover:border-rule-strong")}>40            {f}41          </Link>42        ))}43        <form action="/filings" className="ml-auto flex gap-2">44          {form && <input type="hidden" name="form" value={form} />}45          <input name="q" defaultValue={q} placeholder="Company name" className="h-9 w-48 rounded-md border border-rule bg-surface px-2 text-sm outline-none focus:border-rule-strong" />46        </form>47      </div>48      {cik && (49        <p className="mb-2 text-xs text-ink-3">50          Filtered by CIK {cik} ·{" "}51          <Link href={link({ cik: "", page: "" })} className="text-accent hover:underline">52            clear53          </Link>54        </p>55      )}56      {rows.length ? (57        <div className="overflow-x-auto rounded-md border border-rule bg-surface">58          <table className="table-dense">59            <thead>60              <tr>61                <th>Filed (ET)</th>62                <th>Form</th>63                <th>Filer</th>64                <th className="hidden md:table-cell">CIK</th>65                <th className="hidden md:table-cell">Role</th>66                <th>Instruments</th>67                <th>Document</th>68              </tr>69            </thead>70            <tbody>71              {rows.map((f) => (72                <tr key={f.id}>73                  <td className="mono text-ink-2">{formatDateTime(f.filed_at, { tz: "America/New_York" })}</td>74                  <td className="mono font-medium">75                    {f.form_type}76                    {f.metadata.material === true && <span className="ml-1 text-[10px] uppercase text-accent">material</span>}77                  </td>78                  <td>79                    <Link href={link({ q: f.company_name, page: "" })} className="hover:underline">80                      {f.company_name}81                    </Link>82                  </td>83                  <td className="mono hidden text-ink-3 md:table-cell">84                    {f.cik ? (85                      <Link href={link({ cik: f.cik, page: "" })} className="hover:underline">86                        {f.cik}87                      </Link>88                    ) : (89                      "—"90                    )}91                  </td>92                  <td className="hidden text-xs text-ink-3 md:table-cell">{String(f.metadata.role ?? "—")}</td>93                  <td>94                    {f.instrument_ids.length ? (95                      f.instrument_ids.slice(0, 3).map((id) => (96                        <Link key={id} href={instrumentHref(id)} className="mono mr-1 text-xs text-accent hover:underline">97                          {id.split("_").pop()?.toUpperCase()}98                        </Link>99                      ))100                    ) : (101                      <span className="text-xs text-ink-3">—</span>102                    )}103                  </td>104                  <td>105                    <a href={f.url} target="_blank" rel="noopener noreferrer" className="text-accent hover:underline">106                      EDGAR ↗107                    </a>108                  </td>109                </tr>110              ))}111            </tbody>112          </table>113        </div>114      ) : (115        <Empty>No filings match.</Empty>116      )}117      <div className="mt-3 flex justify-between text-xs">118        {page > 1 ? (119          <Link href={link({ page: page - 1 })} className="text-accent hover:underline">120            ← Newer121          </Link>122        ) : (123          <span />124        )}125        {rows.length === 100 && (126          <Link href={link({ page: page + 1 })} className="text-accent hover:underline">127            Older →128          </Link>129        )}130      </div>131    </Page>132  );133}134