● Zion Boggan
repos/Docket/app/schedule/page.tsx
zionboggan.com ↗
86 lines · tsx
History for this file →
1
import type { Metadata } from "next";
2
import Link from "next/link";
3
import { PageHeader } from "@/components/ui";
4
import { getMeetings, getMeetingCommittees } from "@/lib/feed";
5
import { MeetingsBrowser } from "@/components/meetings-browser";
6
 
7
export const revalidate = 300;
8
 
9
export const metadata: Metadata = {
10
  title: "Congressional Schedule",
11
  description:
12
    "When Congress meets: committee hearings, markups, business meetings, and floor action. Each meeting links to its official schedule.",
13
};
14
 
15
 
16
function isoTodayUtc(): string {
17
  const d = new Date();
18
  const pad = (n: number) => (n < 10 ? `0${n}` : String(n));
19
  return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
20
}
21
 
22
export default function SchedulePage() {
23
  const all = getMeetings();
24
  const committees = getMeetingCommittees();
25
  const todayIso = isoTodayUtc();
26
 
27
  const upcoming = all.filter((m) => !m.date || m.date >= todayIso).length;
28
  const past = all.length - upcoming;
29
  const hearings = all.filter((m) => (m.type || "").toLowerCase() === "hearing").length;
30
  const floor = all.filter((m) => (m.type || "").toLowerCase() === "floor").length;
31
 
32
  return (
33
    <div className="mx-auto w-full max-w-register px-4 py-10 sm:px-6">
34
      <Link
35
        href="/register"
36
        className="focus-ring tap label mb-6 gap-1 text-ash hover:text-paper"
37
      >
38
        ← Register
39
      </Link>
40
 
41
      <PageHeader
42
        eyebrow={"Schedule · what Congress is meeting about"}
43
        title="Congressional meetings"
44
        lead="Committee hearings, markups, business meetings, and floor action. Browse upcoming sessions or look back at past meetings. Each entry links to its official schedule."
45
      />
46
 
47
      {}
48
      <div className="mt-8 grid gap-4 sm:grid-cols-4">
49
        <SummaryCard label="Total on record" count={all.length} />
50
        <SummaryCard label="Upcoming" count={upcoming} />
51
        <SummaryCard label="Past" count={past} />
52
        <SummaryCard label="Hearings" count={hearings} />
53
      </div>
54
      {floor > 0 ? (
55
        <p className="label mt-3 text-ash-2">
56
          {floor} floor action{floor === 1 ? "" : "s"} in the record.
57
        </p>
58
      ) : null}
59
 
60
      <div className="mt-10">
61
        <MeetingsBrowser seed={all} committees={committees} todayIso={todayIso} />
62
      </div>
63
 
64
      <div className="mt-12 border-t border-line pt-6">
65
        <p className="label text-ash-2">
66
          Meeting schedules from clerk.house.gov, senate.gov, and committee .gov
67
          sites. Each meeting links to its source. No commentary appears here.
68
        </p>
69
      </div>
70
    </div>
71
  );
72
}
73
 
74
function SummaryCard({ label, count }: { label: string; count: number }) {
75
  return (
76
    <div className="rounded-[3px] border border-line bg-surface p-5">
77
      <p className="label mb-2">{label}</p>
78
      <div className="flex items-baseline gap-2">
79
        <span className="display text-3xl text-paper">{count}</span>
80
        <span className="label text-ash-2">
81
          {count === 1 ? "meeting" : "meetings"}
82
        </span>
83
      </div>
84
    </div>
85
  );
86
}