All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 2s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 40s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
ส่วนที่ 1 — แก้ popup ตาม bug report ("กดแก้ไขแล้วไปกดที่ tool popup ของแก้ไขไม่ปิด/แสดงซ้อนกัน"):
(1) ปุ่ม tool แบบ dropdown ทั้ง 5 (highlight/draw/shape/stamp/textbox) เรียก switchTool เสมอแม้เป็น tool เดิม — เดิมกดปุ่ม tool ที่ active อยู่จะ toggle เมนูอย่างเดียวไม่เรียก switchTool (ซึ่งเป็นที่เดียวที่เคลียร์ popup บนหน้า: editor/แผง selection/popover) ผลคือ popup แก้ไขค้างเปิด + dropdown เปิดซ้อนขึ้นมาอีกชั้น — ตอนนี้จำ intent เปิด/พับเมนูไว้ก่อนเรียก (switchTool ปิดเมนูทุกตัว) แล้วเปิดคืนเฉพาะที่ตั้งใจ; toggle พับเมนูและกลไกกลืนคลิก M4 เท่าเดิม (mousedown บนปุ่ม mark อยู่แล้ว)
(2) switchTool ปิด popup bookmark ด้วย + openBookmarkComposer ปิดเมนูทั้ง 5 ก่อนเปิด popup (เดิมคีย์ลัด B ระหว่างเมนูเปิด = สอง popup ซ้อนกัน)
(3) คลิกหน้าว่าง = ปิด popover ของ comment pin + เลือกรายการอื่น/goToAnnotation ปิด popover เดิมทิ้ง (เดิมปิด popover ได้แค่กด pin เดิม/Escape/สลับ tool)
(4) COMMENT ที่เปิด popover อยู่ไม่ render กรอบ selection/แผงเครื่องมือ — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม selectedId ไฮไลต์เหมือนเดิม)
(5) แก้แท็บ sidebar ตาม bug report ("เลือก Annotation แล้วจะกลับไป tabs bookmarks ไม่ได้"): effect เดิม dep รวม tab และบังคับ setTab ไปแท็บ annotations ทุกครั้งที่รันตราบ selectedId ไม่ null ทำให้กดแท็บที่คั่นหน้าแล้วถูกดึงกลับทันที — ใช้ ref จำ id ที่ประกาศแล้ว บังคับสลับเฉพาะตอน selectedId เพิ่งเปลี่ยน เคลียร์เป็น null ตอน deselect ให้เลือกรายการเดิมซ้ำภายหลังยังสลับแท็บให้ ส่วน scroll แถวตอนเปิดแท็บหมายเหตุคงเดิม
- sync rules/pdf-viewer.md: กติกาใหม่ popup บนหน้าเปิดได้ทีละชุดห้ามค้างเปิดซ้อนกัน (ปุ่ม dropdown เรียก switchTool เสมอ + วงจรปิด popover/แผง) และยกเว้น markMenuClosed ของคีย์ B ในกติกา M4
- verify: tsc ผ่าน, vitest 120/120, Playwright 18 เคสผ่าน (repro ตรงตามรายงาน: สร้าง textbox → แก้ไขข้อความ → กดปุ่ม tool เดิม → editor+แผงปิด+เมนูใช้ได้; popover pin ไม่ซ้อนแผง/ปิดด้วยคลิกหน้าว่าง; popup bookmark ปิดตอนสลับ tool; แท็บที่คั่นหน้ากลับได้)
ส่วนที่ 2 — งานที่ค้างใน working tree ก่อนหน้า (ตามแผน docs/PLAN-IMAGE.md):
- N3 รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด (viewer render img แทน canvas ไม่มี textLayer, scale contract เดียวกับ export) + TIFF หลายเฟรม = หลายหน้า (decode ผ่าน utif2 dynamic import → PNG blob URL ต่อเฟรม ฝั่ง client) + export PDF ฝังรูป (utils/image-to-pdf.ts: embedJpg/embedPng + EXIF rotate) + utils/file-kind.ts derive ชนิดไฟล์ + tests ของทั้งสอง utils
- N4 อัปโหลดไฟล์ PDF/รูป ≤100MB ผ่านหน้ารายการ (components/UploadFileForm.tsx + app/actions/files.ts: validate + เขียนไฟล์ใหม่ที่ public/uploads/ เท่านั้น — invariant #1)
- เอกสาร: docs/PLAN-IMAGE.md เพิ่มใหม่ + sync PLAN.md/PLAN-FEAT.md/CLAUDE.md/README/gap-analysis/data-layer rule
Co-Authored-By: Claude Code <noreply@anthropic.com>
103 lines
3.7 KiB
TypeScript
103 lines
3.7 KiB
TypeScript
import Link from "next/link";
|
|
import { asc } from "drizzle-orm";
|
|
import { FileImage, FileText } from "lucide-react";
|
|
|
|
import { db } from "@/db";
|
|
import { files } from "@/db/schema";
|
|
import UserPicker from "@/components/UserPicker";
|
|
import UploadFileForm from "@/components/UploadFileForm";
|
|
import { imageFormatFromUrl } from "@/utils/file-kind";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/** badge ชนิดไฟล์ต่อรายการ (N3) */
|
|
function kindBadge(pdfUrl: string): string {
|
|
switch (imageFormatFromUrl(pdfUrl)) {
|
|
case "jpeg":
|
|
return "JPG";
|
|
case "png":
|
|
return "PNG";
|
|
case "tiff":
|
|
return "TIFF";
|
|
default:
|
|
return "PDF";
|
|
}
|
|
}
|
|
|
|
export default async function HomePage() {
|
|
const rows = await db.select().from(files).orderBy(asc(files.createdAt));
|
|
|
|
return (
|
|
<main className="mx-auto max-w-2xl px-4 py-10 sm:px-8">
|
|
<header className="flex items-center gap-3">
|
|
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-base font-bold text-white shadow-sm">
|
|
JN
|
|
</div>
|
|
<div>
|
|
<h1 className="text-2xl font-bold leading-tight text-primary">
|
|
Judge's Note
|
|
</h1>
|
|
<p className="text-sm text-slate-500">
|
|
ระบบ PDF Annotation สำหรับ e-Court
|
|
</p>
|
|
</div>
|
|
</header>
|
|
|
|
<UserPicker />
|
|
|
|
<div className="mt-8 flex items-baseline justify-between">
|
|
<h2 className="text-sm font-semibold text-slate-700">เอกสาร</h2>
|
|
{rows.length > 0 && (
|
|
<span className="text-xs text-slate-400">{rows.length} ไฟล์</span>
|
|
)}
|
|
</div>
|
|
<UploadFileForm />
|
|
|
|
{rows.length === 0 ? (
|
|
<div className="mt-3 rounded-xl border border-dashed border-slate-300 bg-white/60 p-10 text-center">
|
|
<FileText className="mx-auto h-8 w-8 text-slate-300" />
|
|
<p className="mt-3 text-sm font-medium text-slate-500">
|
|
ยังไม่มีไฟล์ในระบบ
|
|
</p>
|
|
<p className="mt-1 text-xs text-slate-400">
|
|
รัน /sync-db เพื่อ seed ไฟล์ตัวอย่าง
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<ul className="mt-3 space-y-3">
|
|
{rows.map((f) => (
|
|
<li
|
|
key={f.id}
|
|
className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md"
|
|
>
|
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10">
|
|
{imageFormatFromUrl(f.pdfUrl) ? (
|
|
<FileImage className="h-5 w-5 text-primary" />
|
|
) : (
|
|
<FileText className="h-5 w-5 text-primary" />
|
|
)}
|
|
</div>
|
|
<span className="min-w-0 flex-1">
|
|
<span className="block break-all line-clamp-2 text-sm font-medium text-slate-900">
|
|
{f.title}
|
|
</span>
|
|
<span className="mt-0.5 block text-xs text-slate-400">
|
|
<span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500">
|
|
{kindBadge(f.pdfUrl)}
|
|
</span>
|
|
{new Date(f.createdAt).toLocaleString("th-TH")}
|
|
</span>
|
|
</span>
|
|
<Link
|
|
href={`/annotate/${f.id}`}
|
|
className="shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90"
|
|
>
|
|
Annotate
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</main>
|
|
);
|
|
}
|