Categories page: descriptions, per-category terminal illustrations, full ZH version
Deploy / build (push) Successful in 17s

- 7 categories now listed with name, blurb, post count and a
  per-category terminal-style SVG illustration (CategoryArt)
- empty categories show '0 posts · coming soon' (non-link, no soft-404)
- new zh pages: /zh/categories/ index + /zh/categories/[category]/ detail
- PostList is locale-aware (zh-CN dates, zh category links)
- zh nav '分类' now points to /zh/categories/
- category detail pages get art + description header
- language switch wired both ways (EN <-> ZH)
This commit is contained in:
hoelee committed 2026-09-13 23:50:08 +08:00
1 parent 730a936137
commit 4cf52e04c1
11 files changed
+577 -25

No files matched your search

+24 -4
View File
@@ -1,9 +1,11 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import PostList from '../../components/PostList.astro';
import CategoryArt from '../../components/CategoryArt.astro';
import { getCollection } from 'astro:content';
import { isEn } from '../../lib/lang';
import { sortByUpdated } from '../../lib/sort';
import { categoryMeta } from '../../lib/categories';
export async function getStaticPaths() {
const posts = await getCollection('posts', isEn);
@@ -12,13 +14,31 @@ export async function getStaticPaths() {
}
const { category } = Astro.params;
const meta = categoryMeta(category as string);
const posts = sortByUpdated(
(await getCollection('posts', isEn)).filter((p) => p.data.category === category),
);
const name = meta?.name.en ?? category;
const blurb = meta?.blurb.en;
---
<BaseLayout title={`${category} — Mr Hoelee`} description={`Posts in the ${category} category.`}>
<h1>Category: {category}</h1>
<PostList posts={posts} />
</BaseLayout>
<BaseLayout
title={`${name} — Mr Hoelee`}
description={blurb ?? `Posts in the ${category} category.`}
switchHref={`/zh/categories/${category}/`}
>
<h1>{name}</h1>
{blurb && <p class="lede">{blurb}</p>}
<div class="cat-art-wrap">
<CategoryArt slug={category} />
</div>
<p class="cat-count">
{posts.length} post{posts.length === 1 ? '' : 's'} in this category
</p>
<PostList posts={posts} lang="en" />
</BaseLayout>
+22 -14
View File
@@ -1,23 +1,31 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import CategoryGrid from '../../components/CategoryGrid.astro';
import { getCollection } from 'astro:content';
import { isEn } from '../../lib/lang';
const posts = await getCollection('posts', isEn);
const categories = [...new Set(posts.map((p) => p.data.category))].sort();
const count = (cat: string) => posts.filter((p) => p.data.category === cat).length;
const counts: Record<string, number> = {};
for (const p of posts) {
const cat = p.data.category as string;
counts[cat] = (counts[cat] ?? 0) + 1;
}
---
<BaseLayout title="Categories — Mr Hoelee" description="Browse posts by category.">
<h1>Categories</h1>
<ul class="post-list">
{categories.map((cat) => (
<li>
<a href={`/categories/${cat}/`}><h2>{cat}</h2></a>
<p>{count(cat)} post{count(cat) === 1 ? '' : 's'}</p>
</li>
))}
</ul>
</BaseLayout>
<BaseLayout
title="Categories — Mr Hoelee"
description={`Browse ${posts.length} technical posts by category: case studies, DevOps, AI, engineering, tutorials and quick notes.`}
switchHref="/zh/categories/"
>
<div class="page-head">
<h1>Categories</h1>
<p class="lede">
Every post belongs to one of seven themes. The <strong>Case Studies</strong> are my
working portfolio — end-to-end build logs with the debugging included. The{' '}
<strong>Notes</strong> are the quick fixes. Start wherever matches what you're building.
</p>
</div>
<CategoryGrid counts={counts} lang="en" />
</BaseLayout>
+43
View File
@@ -0,0 +1,43 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import PostList from '../../../components/PostList.astro';
import CategoryArt from '../../../components/CategoryArt.astro';
import { getCollection } from 'astro:content';
import { isZh } from '../../../lib/lang';
import { sortByUpdated } from '../../../lib/sort';
import { categoryMeta } from '../../../lib/categories';
export async function getStaticPaths() {
const posts = await getCollection('posts', isZh);
const cats = [...new Set(posts.map((p) => p.data.category))];
return cats.map((cat) => ({ params: { category: cat } }));
}
const { category } = Astro.params;
const meta = categoryMeta(category as string);
const posts = sortByUpdated(
(await getCollection('posts', isZh)).filter((p) => p.data.category === category),
);
const name = meta?.name.zh ?? category;
const blurb = meta?.blurb.zh;
---
<BaseLayout
title={`${name} — Mr Hoelee`}
description={blurb ?? `「${category}」分类下的文章。`}
lang="zh"
switchHref={`/categories/${category}/`}
>
<h1>{name}</h1>
{blurb && <p class="lede">{blurb}</p>}
<div class="cat-art-wrap">
<CategoryArt slug={category} />
</div>
<p class="cat-count">本分类共 {posts.length} 篇文章</p>
<PostList posts={posts} lang="zh" />
</BaseLayout>
+31
View File
@@ -0,0 +1,31 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import CategoryGrid from '../../../components/CategoryGrid.astro';
import { getCollection } from 'astro:content';
import { isZh } from '../../../lib/lang';
const posts = await getCollection('posts', isZh);
const counts: Record<string, number> = {};
for (const p of posts) {
const cat = p.data.category as string;
counts[cat] = (counts[cat] ?? 0) + 1;
}
---
<BaseLayout
title="分类 — Mr Hoelee"
description={`按分类浏览 ${posts.length} 篇中文技术文章:案例研究、DevOps、AI、工程开发、教程与随记。`}
lang="zh"
switchHref="/categories/"
>
<div class="page-head">
<h1>分类</h1>
<p class="lede">
每篇文章都属于七大主题之一。<strong>案例研究</strong>是我的实战作品集——包含完整踩坑过程的
端到端构建实录;<strong>随记</strong>则是简短速记。从你正在做的事情开始看吧。
</p>
</div>
<CategoryGrid counts={counts} lang="zh" />
</BaseLayout>
+1 -1
View File
@@ -34,7 +34,7 @@ const posts = sortByUpdated(await getCollection('posts', isZh));
{posts.length === 0 ? (
<p><em>中文文章即将发布,敬请期待。</em></p>
) : (
<PostList posts={posts} />
<PostList posts={posts} lang="zh" />
)}
</section>
</BaseLayout>