● กำลังพัฒนา
Reading Log
Static site สำหรับบันทึกหนังสือที่อ่าน พร้อม rating, note และ tags จัดการด้วย Astro Content Collections ไม่ต้องมี backend
สารบัญ
แรงบันดาลใจ
Goodreads มีฟีเจอร์เยอะเกินจำเป็น และ track ข้อมูลผู้ใช้มากเกินไป สิ่งที่ต้องการจริงๆ คือพื้นที่ส่วนตัวสำหรับบันทึกว่า “อ่านอะไร คิดอะไร ได้อะไรไปบ้าง” โดยไม่มีโฆษณาหรือ social pressure
โครงสร้างข้อมูล
แต่ละหนังสือเก็บเป็น markdown file พร้อม frontmatter — Zod validate ทุก field ตั้งแต่ build time:
---
title: "Show Your Work"
author: "Austin Kleon"
status: completed
rating: 5
startDate: 2026-03-01
endDate: 2026-03-15
tags: [creativity, career, non-fiction]
cover: /covers/show-your-work.jpg
---
## Quote ที่ชอบ
> "You don't have to be a genius. Just be yourself."
## สิ่งที่ได้จากหนังสือเล่มนี้
ทำให้กล้า share งานที่ยังไม่สมบูรณ์แบบออกมา
Zod Schema
const books = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/books' }),
schema: z.object({
title: z.string(),
author: z.string(),
status: z.enum(['reading', 'completed', 'want-to-read']).default('want-to-read'),
rating: z.number().min(1).max(5).optional(),
startDate: z.coerce.date().optional(),
endDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
cover: z.string().optional(),
}),
});
ฟีเจอร์
- Shelf view — แสดงหนังสือตามสถานะ: กำลังอ่าน / อ่านแล้ว / อยากอ่าน
- Reading stats — จำนวนหนังสือต่อปี, genres ที่อ่านมากที่สุด, เฉลี่ย rating
- Tag filter — กรองหนังสือตาม topic หรือ genre
- Note per book — บันทึก quote และ key insight ที่ต้องการจำ
Reading Stats ที่คำนวณ build time
const completed = books.filter(b => b.data.status === 'completed');
const booksThisYear = completed.filter(b => {
const year = b.data.endDate?.getFullYear();
return year === new Date().getFullYear();
});
const avgRating = completed
.filter(b => b.data.rating)
.reduce((sum, b) => sum + (b.data.rating ?? 0), 0) / completed.length;
Stats ทั้งหมดคำนวณตอน build ไม่ต้องมี API call — ผลลัพธ์ embed ลงใน HTML ไปเลย
สิ่งที่เรียนรู้
- Optional fields ใน Zod — ใช้
.optional()และ.coerce.date()สำหรับ dates ที่มาจาก frontmatter - Derived data — สร้าง stats จาก collections โดยตรง ไม่ต้องมีฐานข้อมูล
z.coerce.date()— แปลง string จาก YAML เป็น Date object อัตโนมัติ ต่างจากz.date()ที่ต้องการ Date object แล้วเท่านั้น- Progressive enhancement — เพิ่ม JS filter ทีหลัง โดย HTML ยังทำงานได้โดยไม่มี JS