56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
|
|
import { asc, eq } from "drizzle-orm";
|
|||
|
|
|
|||
|
|
import { db } from "@/db";
|
|||
|
|
import { annotations, files, toAnnotationDTO } from "@/db/schema";
|
|||
|
|
import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display";
|
|||
|
|
import { csvField } from "@/utils/csv";
|
|||
|
|
import { encodeRFC5987 } from "@/utils/pdf-export";
|
|||
|
|
|
|||
|
|
// Export สรุปหมายเหตุเป็น CSV (F16/D3) — ข้อมูลเดียวกับหน้าสรุป (F15)
|
|||
|
|
// ขึ้นต้นด้วย UTF-8 BOM เพื่อให้ Excel ภาษาไทยอ่านถูกต้อง (RFC 4180: ครอบ field
|
|||
|
|
// ด้วย double quote, ขึ้นบรรทัดใหม่ด้วย CRLF)
|
|||
|
|
|
|||
|
|
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 async function GET(req: Request) {
|
|||
|
|
const fileId = new URL(req.url).searchParams.get("fileId") ?? "";
|
|||
|
|
if (!UUID_RE.test(fileId)) {
|
|||
|
|
return new Response("fileId ไม่ถูกต้อง", { status: 400 });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const [file] = await db.select().from(files).where(eq(files.id, fileId));
|
|||
|
|
if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 });
|
|||
|
|
|
|||
|
|
const rows = await db
|
|||
|
|
.select()
|
|||
|
|
.from(annotations)
|
|||
|
|
.where(eq(annotations.fileId, fileId))
|
|||
|
|
.orderBy(asc(annotations.pageIndex), asc(annotations.createdAt));
|
|||
|
|
|
|||
|
|
const header = ["หน้า", "ชนิด", "ผู้เขียน", "ข้อความ", "โน้ต", "สร้างเมื่อ"];
|
|||
|
|
const lines = rows.map((row) => {
|
|||
|
|
const a = toAnnotationDTO(row);
|
|||
|
|
return [
|
|||
|
|
String(a.pageIndex + 1),
|
|||
|
|
annotationTypeLabel(a),
|
|||
|
|
a.userName,
|
|||
|
|
annotationPreviewText(a),
|
|||
|
|
annotationNote(a),
|
|||
|
|
new Date(a.createdAt).toLocaleString("th-TH", { timeZone: "Asia/Bangkok" }),
|
|||
|
|
]
|
|||
|
|
.map(csvField)
|
|||
|
|
.join(",");
|
|||
|
|
});
|
|||
|
|
// = UTF-8 BOM — ต้องเป็นตัวแรกสุดของไฟล์เสมอ
|
|||
|
|
const csv = `${[header.map(csvField).join(","), ...lines].join("\r\n")}\r\n`;
|
|||
|
|
|
|||
|
|
const filename = encodeRFC5987(`${file.title}-summary.csv`);
|
|||
|
|
return new Response(csv, {
|
|||
|
|
headers: {
|
|||
|
|
"content-type": "text/csv; charset=utf-8",
|
|||
|
|
"content-disposition": `attachment; filename="summary.csv"; filename*=UTF-8''${filename}`,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|