import Link from "next/link"; import { asc, eq } from "drizzle-orm"; import { notFound } from "next/navigation"; import { ArrowLeft, MapPin } from "lucide-react"; import { db } from "@/db"; import { annotations, files, toAnnotationDTO, type AnnotationDTO } from "@/db/schema"; import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวมทุก annotation (ทุกผู้ใช้) // จัดกลุ่มตามหน้า พร้อมปุ่มกระโดดเป็น deep link `/annotate/?ann=` (F12) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; export const dynamic = "force-dynamic"; export default async function SummaryPage({ params, }: { params: { id: string }; }) { const { id } = params; if (!UUID_RE.test(id)) notFound(); const [file] = await db.select().from(files).where(eq(files.id, id)); if (!file) notFound(); const rows = await db .select() .from(annotations) .where(eq(annotations.fileId, id)) .orderBy(asc(annotations.pageIndex), asc(annotations.createdAt)); const dtos = rows.map(toAnnotationDTO); // จัดกลุ่มตามหน้า (เรียงหน้ามากไปน้อยไม่เกี่ยว — เดินตามลำดับหน้าเสมอ) const byPage = new Map(); for (const a of dtos) { const list = byPage.get(a.pageIndex); if (list) list.push(a); else byPage.set(a.pageIndex, [a]); } const pages = Array.from(byPage.entries()).sort((x, y) => x[0] - y[0]); return (

สรุปหมายเหตุ — {file.title}

กลับหน้าอ่าน {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} Export CSV
{pages.length === 0 ? (

ยังไม่มี annotation ในไฟล์นี้

) : ( pages.map(([pageIndex, items]) => (

หน้า {pageIndex + 1} {items.length} รายการ

    {items.map((a) => (
  • {annotationTypeLabel(a)} {a.type === "COMMENT" && a.resolved && ( จัดการแล้ว )} {a.userName} {annotationPreviewText(a)} {annotationNote(a) && ( โน้ต: {annotationNote(a)} )} {/* ปุ่มกระโดด = deep link ไปยัง annotation นั้น (F12 รับ ?ann= ตอน mount) */} ไปตำแหน่ง
  • ))}
)) )}
); }