Contents: table of contents page; each entry opens its own page (/contents/[node])
Deploy / deploy (push) Successful in 6m36s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FbMbJPadPzVwvt9rU9GBNo
This commit is contained in:
2026-10-23 05:23:13 +00:00
parent 15cbe714e0
commit d4af89c7f7
2 changed files with 40 additions and 30 deletions
@@ -1,49 +1,28 @@
import type { GetServerSideProps } from 'next';
import Link from 'next/link';
import Layout from '../components/Layout';
import { getContent } from '../lib/content';
import { CONTENT_TREE, ContentNode, findNode } from '../lib/content-tree';
import { typeSlug } from '../lib/slug';
import Layout from '../../components/Layout';
import { getContent } from '../../lib/content';
import { findNode } from '../../lib/content-tree';
import { typeSlug } from '../../lib/slug';
type Content = Awaited<ReturnType<typeof getContent>>;
interface Props { node: { id: string; title: string; blurb: string; areas: string[]; taxonomy: string[] }; content: Content }
export const getServerSideProps: GetServerSideProps<Props> = async ({ query }) => {
const want = String(query.node ?? CONTENT_TREE.children[0].id);
const node = findNode(want);
export const getServerSideProps: GetServerSideProps<Props> = async ({ params }) => {
const node = findNode(String(params?.node));
if (!node || node.id === 'root') return { notFound: true };
const { children: _c, ...plain } = node;
return { props: { node: plain, content: await getContent(node) } };
};
function Tree({ node, selected }: { node: ContentNode; selected: string }) {
return (
<ul className="tree">
{node.children.map((c) => (
<li key={c.id}>
<Link href={`/contents?node=${c.id}`} className={c.id === selected ? 'sel' : ''}>{c.title}</Link>
{c.children.length > 0 && <Tree node={c} selected={selected} />}
</li>
))}
</ul>
);
}
export default function Contents({ node, content }: Props) {
const area = content.areas[0];
return (
<Layout title={node.title}>
<h1>Contents</h1>
<p className="sub">The tree of content for the write-up. Each entry draws its numbers from the survey data.</p>
<div className="layout">
<aside className="side">
<div className="card">
<b>{CONTENT_TREE.title}</b>
<Tree node={CONTENT_TREE} selected={node.id} />
</div>
</aside>
<p><Link href="/contents">← Contents</Link></p>
<h1>{node.title}</h1>
<div>
<section>
<h2 style={{ marginTop: 0 }}>{node.title}</h2>
<p className="sub">{node.blurb}</p>
<div className="cards">
<div className="card"><div className="stat">{content.total.toLocaleString()}</div>papers in this area{area ? ` (${area.label.split(',')[0]})` : ''}</div>
+31
View File
@@ -0,0 +1,31 @@
import Link from 'next/link';
import Layout from '../../components/Layout';
import { CONTENT_TREE, ContentNode } from '../../lib/content-tree';
function Toc({ nodes, prefix }: { nodes: ContentNode[]; prefix: string }) {
return (
<ol style={{ listStyle: 'none', paddingLeft: prefix ? 24 : 0 }}>
{nodes.map((n, i) => {
const num = `${prefix}${i + 1}`;
return (
<li key={n.id} style={{ margin: '10px 0' }}>
<span className="sub" style={{ margin: 0, marginRight: 8 }}>{num}</span>
<Link href={`/contents/${n.id}`}><b>{n.title}</b></Link>
<div className="sub" style={{ margin: '2px 0 0 28px', fontSize: 13 }}>{n.blurb}</div>
{n.children.length > 0 && <Toc nodes={n.children} prefix={`${num}.`} />}
</li>
);
})}
</ol>
);
}
export default function Contents() {
return (
<Layout title="Contents">
<h1>Contents</h1>
<p className="sub">{CONTENT_TREE.blurb} Each entry opens its own page.</p>
<div className="card"><Toc nodes={CONTENT_TREE.children} prefix="" /></div>
</Layout>
);
}