- ใส่คำอธิบายแบบ commit message ได้ทุก flow ที่สร้าง version (Save & Version / Start Versioning / generate จากเว็บ) + แสดงใน version history ทุกหน้า - backend: DocumentVersion.Message (varchar 2048) + migration AddVersionMessage + BadRequest ตรวจความยาวก่อนแตะ MinIO (กัน 500 + orphan object) - add-in: ConfirmVersionDialog เพิ่ม textarea (Start Versioning ใช้ dialog ด้วย) + แสดง message ใน VersionHistoryPanel - web: ฟอร์มสร้างเอกสาร + หน้า detail แสดง message (desktop truncate + tooltip, mobile เต็มบรรทัด) - sync เอกสารทั้งหมด: spec v5 (FR-13/AC-16) + plan Phase 9 + acceptance-review 1–16 + README + CLAUDE.md + web/README - tests: backend 52 / addin 50 / web 36 — เขียวทั้งชุด Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
Version Message (commit message) — FR-13 / AC-16 / Spec v5
Context
ผู้ใช้ต้องการให้ใส่ "ข้อความอธิบาย" ตอนสร้าง version ใหม่ได้ เหมือน git commit message — เก็บกับแต่ละ version และแสดงใน version history ทุกหน้า ขอบเขต (ยืนยันกับผู้ใช้แล้ว): ทุก flow ที่สร้าง version = add-in Save & Version + add-in Start Versioning (เพิ่ม dialog ใหม่) + ฟอร์ม generate บน web
ข้อตกลง fixed:
- Field ชื่อ
message— nullable string, max 2048, normalizeIsNullOrWhiteSpace → null+.Trim()(นโยบายเดียวกับTemplate.Descriptionเป๊ะ) - Optional ทุกที่ ไม่บังคับกรอก
- Non-goal: ไม่ฝังลง docx (custom properties / Custom XML part) — backend คือ source of truth (กติกา #5) และเลี่ยง parse-compat กับไฟล์เก่า (
XmlVersionEntryไม่แตะ) - ตามกติกา repo: แก้ spec ก่อน แล้ว sync plan
ขั้นที่ 0 — บันทึกแผน + แก้เอกสาร requirement ก่อน
- บันทึกแผนนี้ที่
docs/plans/version-message.md(ธรรมเนียมเดิม) - specs/spec-officejs.md: เพิ่ม changelog v5 บรรทัดบนสุด ·
### FR-13: คำอธิบายต่อ version — commit message (เพิ่มเติม v5)ต่อท้าย section 6 (หลัง FR-12) ตาม format เดิม — ระบุ fieldmessage+ ทั้ง 2 endpoint + นโยบาย null/trim + แสดงผลทุก history + Start Versioning ได้ dialog · section 7DocumentVersionPOCO เพิ่มpublic string? Message { get; set; }· section 10 เพิ่ม AC-16 (v5) · section 11 เพิ่ม task items(v5)ทั้ง 3 list - docs/plans/officejs-implementation-plan.md: เพิ่ม
## Phase 9: Version message — FR-13 (เพิ่มทีหลัง, v5)+ checklist + แถวตาราง mapping| 9 (v5) | FR-13 | AC-16 | - docs/acceptance-review.md: 1–15 → 1–16 + section AC-16 (🔶 ส่วน dialog ใน Word ต้องยืนยันมือ)
ขั้นที่ 1 — Backend (backend/WordVersioning.Api)
- Models/DocumentVersion.cs:
public string? Message { get; set; }(+ doc comment FR-13) - Data/AppDbContext.cs: ใน block DocumentVersion เพิ่ม
entity.Property(x => x.Message).HasMaxLength(2048);(มิเรอร์ Template.Description) - Migration:
dotnet ef migrations add AddVersionMessage→ expectAddColumn<string>("Message", "DocumentVersions", nullable, maxLength 2048)(tests รัน MigrateAsync อยู่แล้ว = migration ถูก exercise) - Models/VersionDtos.cs:
UploadVersionRequest.Message(string? หลัง Fields) ·GenerateDocumentRequest.Message(string? หลัง Values) ·VersionListItemเพิ่มstring? Messageท้าย record - Controllers/DocumentsController.cs:
- เพิ่ม
MaxVersionMessageLength = 2048+NormalizeMessage()helper (static private — มี patternParseSnapshot/IsUniqueViolationอยู่แล้ว) - ตัดสินใจ: ใส่ BadRequest check ความยาว (ต่างจาก Template.Description ที่ไม่มี) เพราะ: ถ้าไม่เช็ค = 500 จาก DB และใน Generate จะเกิด 500 หลัง MinIO upload แล้ว = orphan object ใน bucket โดยเปล่า + UploadVersion เป็น action ที่ validate เองด้วยข้อความไทยอยู่แล้ว
- UploadVersion: normalize + check หลัง validation ของ fields →
Message = messageในnew DocumentVersionใน retry loop (ทุกรอบได้ค่าเดียวกัน) - Generate: normalize + check ก่อน ทำ MinIO/template IO →
Message = messageใน v1 initializer - GetVersions: เพิ่ม
v.Messageใน projection +, r.Messageตอน constructVersionListItem - Rollback path ไม่ต้องแตะเพิ่ม (MinIO fail → Remove ทั้ง row อยู่แล้ว / Generate detach ก่อน SaveChanges)
- เพิ่ม
ขั้นที่ 2 — Add-in (addin) — Fluent UI v9
- ConfirmVersionDialog.tsx — generalize ด้วย optional props (ไม่สร้าง component ใหม่):
title?,description?,confirmLabel?+onConfirm: (message: string) => void; เพิ่มField+Textarea(label "คำอธิบาย version (ไม่บังคับ)", placeholder "เช่น แก้ไขข้อ 3 สัญญาจ้าง", maxLength 2048, rows 3, resize vertical, disabled={busy}); local statemessage+useEffectเคลียร์เมื่อ!open(FC ถูก mount ค้างไว้ — ห้ามพึ่ง remount) - versionFlow.ts — optional positional param วางก่อน callback (callback-last):
uploadCurrentFile(documentId, clientVersionId, fields, message?)·startVersioning(message?, onProgress?)·saveAndVersion(documentId, latestVersionNumber, message?, onProgress?)— test เดิม 5 ตัวเรียก 2 args / 0 args คอมไพล์ผ่านไม่ต้องแก้ - versioningService.ts
uploadVersion— เพิ่มmessage?ท้าย signature +const trimmed = message?.trim(); if (trimmed) form.append("message", trimmed)(มิเรอร์ guard ของ fields) - versionRecord.ts —
VersionRecordเพิ่มmessage: string | null - VersionHistoryPanel.tsx — แสดง
version.messageใต้บรรทัด "โดย …" (แบบเต็ม,pre-wrap, muted เล็ก) — เพิ่ม stylemessageRow - App.tsx —
handleSaveAndVersion(message)/handleStartVersioning(message)ส่งต่อ; statestartConfirmOpen; ปุ่ม Start Versioning เปลี่ยนเป็นเปิด dialog; renderConfirmVersionDialogตัวที่สอง (title "เริ่มติดตามเอกสารนี้?", confirmLabel "เริ่มติดตาม (version 1)")
ขั้นที่ 3 — Web (web)
- documents/create/[templateId].vue —
const message = ref(''); textarea ทางเลือก (label/inputclass, rows 2, maxlength 2048) หลัง field loop; payload เพิ่มmessage: message.value.trim() || null;resetFormเคลียร์; re-generate ไม่ prefill message (เอกสารใหม่ได้ข้อความใหม่ — ใส่ comment กำกับ) - types/api.ts —
VersionListItemเพิ่มmessage: string | null - documents/[documentId].vue — ไม่เพิ่มคอลัมน์ใหม่ (colspan="4" ใน expanded row + คอลัมน์ action จะบีบ): desktop แสดงใต้
v{N}เป็นtruncate text-xs text-muted+:titletooltip; mobile card แสดงเต็มbreak-words
ขั้นที่ 4 — Tests
Backend — ไฟล์ใหม่ VersionMessageTests.cs (ตาม pattern UploadVersionTests — VersionForm, fixture.CreateDb(), AsNoTracking):
Upload_WithMessage_PersistsRow_AndEchoesInHistory[Theory] Upload_BlankOrMissingMessage_StoredAsNull(null / "" / " ")Upload_TrimsSurroundingWhitespaceUpload_OversizeMessage_Returns400_NoSideEffect(2049 ตัว, ไม่มี row หลงเหลือ)Upload_MessageExactly2048_Succeeds(boundary)SecondUpload_KeepsPerVersionMessages(v1=A, v2=B → history [B,A])Generate_WithMessage_CreatesV1WithMessageGenerate_OversizeMessage_Returns400(เช็คว่า 400 เกิดก่อน MinIO)
แก้ helper: TestDtos.cs — VersionItem เพิ่ม string? Message, VersionForm(..., string? message = null); TestFlow.GenerateDocumentAsync(..., string? message = null)
Add-in — versionFlow.test.ts: เพิ่ม 3 test — form.get("message") หลัง saveAndVersion(id, 1, "…"), whitespace → null, startVersioning("…") แนบ v1 (test เดิมไม่ต้องแก้)
Web: type-only — ไม่มี component test ตาม convention (เช็คด้วย build)
Verification
# ลำดับ: docs → backend → add-in → web
cd backend/WordVersioning.Api && dotnet ef migrations add AddVersionMessage
cd backend && dotnet test --filter VersionMessageTests # ต้องมี Docker (Testcontainers)
cd backend && dotnet test # ทั้งชุด
cd addin && npm run build && npm test
cd web && npm run build && npm test
Manual E2E (หลัง docker compose up -d + backend + npm run dev/npm start):
- Web generate พร้อม message → หน้า detail แสดงใต้ v1 (desktop + mobile card); ไม่กรอก → ไม่แสดง
- Re-generate จากเอกสารที่มี message → textarea ว่าง
- Add-in: untracked → Start Versioning เปิด dialog → พิมพ์ message → v1 + panel แสดง
- Add-in: Save & Version พร้อม message → history refresh แสดง
- เปิด dialog → พิมพ์ → ยกเลิก → เปิดใหม่ → textarea ต้องว่าง
- Word sideload caveat: ห้าม
cpทับ manifest ใน wef (symlink), kill dev server เก่าก่อนnpm start
Risks / gotchas
- Param เพิ่มท้าย + callback-last = test เดิมคอมไพล์ผ่าน (verify แล้วว่าเรียก 2/0 args)
- เคลียร์ dialog state ด้วย useEffect ตาม
open— อย่าพึ่ง unmount - Postgres varchar นับเป็น characters (ไทย 3 bytes/char ไม่มีปัญหา); JS maxLength กับ C#
.Lengthนับ UTF-16 code units เหมือนกัน GenerateDocumentRequest.Messageเป็น plain string — JSON ส่ง type ผิดได้ 400 อัตโนมัติจาก [ApiController] (body อังกฤษ — ยอมรับได้)- AC-16 ส่วน dialog/textarea ใน Word webview ต้องยืนยันมือ (🔶 ใน acceptance-review)
- อย่าลืมอัปเดตตัวเลข test count ใน docs (47/42/36) หลังรันจริง
สถานะ: ✅ implement เสร็จ (2026-08-20)
ผลรันจริงทั้งชุด: backend dotnet test 52/52 · addin npm test 50/50 + build + lint + manifest validate ผ่าน · web npm test 36/36 + build ผ่าน — รายละเอียด checkbox Phase 9 + หมายเหตุจากการทำจริงอยู่ที่ officejs-implementation-plan.md · ผล AC-16 ที่ acceptance-review.md · เหลือยืนยันด้วยมือเฉพาะ dialog/textarea ใน Word จริง