Phase 10: field editor (FR-14)

เพิ่ม editor ต่อ template field — ผู้ดูแลเลือกรูปแบบช่องกรอกในฟอร์มสร้างเอกสารได้:
text → textbox/textarea, number/date → control ตาม type (presentation metadata
ล้วน — validation ยังยึด Type, ค่า field เป็น string ทั้ง flow, ไม่แตะ generate/sync/add-in)

- Backend: TemplateField.Editor (nullable, null = default ของ type) +
  migration AddTemplateFieldEditor + validate ตาม type ใน PUT templates
  (ค่าไม่รู้จัก/ไม่เข้ากับ type → 400, normalize-on-write, API คืน resolved เสมอ)
- Web: util fieldEditor.ts + editor select หน้าแก้ template (desktop+mobile,
  ซ่อนเมื่อ type มีทางเดียว) + ฟอร์มสร้างเอกสาร render ตาม editor +
  แก้ prefill re-generate ของ field date จาก ISO timestamp เต็ม (input[type=date]
  รับเฉพาะ yyyy-MM-dd เดิมแสดงว่าง)
- Docs: spec v6 (FR-14/AC-17) + plan Phase 10 + sub-plan field-editor.md +
  acceptance-review AC-17 + README/CLAUDE.md
- Tests: backend 60/60 (FieldEditorTests ใหม่ 8 กรณี) · web 46/46 + build ผ่าน

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Suphonchai Phoonsawat 2026-08-22 09:17:15 +07:00
parent 7b20f12ede
commit 9bb4a48c00
23 changed files with 865 additions and 45 deletions

View file

@ -1,7 +1,7 @@
# Implementation Plan: Word Versioning Add-in (Office.js + .NET 8)
> แผนงานสร้างระบบจาก spec: [`specs/spec-officejs.md`](../../specs/spec-officejs.md)
> สถานะ: **Phase 0–9 เสร็จ** (backend + add-in + web portal + tests ครบชุด + ไล่ AC ครบ — รายละเอียดผล AC ทุกข้อที่ [`docs/acceptance-review.md`](../acceptance-review.md)) · เหลือยืนยันด้วยมือเฉพาะบน platform ที่ทำแทนไม่ได้: Word Online/Windows, click-through บน browser จริง, silent refresh จริง
> สถานะ: **Phase 0–10 เสร็จ** (backend + add-in + web portal + tests ครบชุด + ไล่ AC ครบ — รายละเอียดผล AC ทุกข้อที่ [`docs/acceptance-review.md`](../acceptance-review.md)) · เหลือยืนยันด้วยมือเฉพาะบน platform ที่ทำแทนไม่ได้: Word Online/Windows, click-through บน browser จริง, silent refresh จริง
> วันที่แผน: 2026-08-18 (ฉบับแก้ไข v2: เพิ่ม FR-9 ถึง FR-11 — ระบบ template, สร้างเอกสาร v1 ผ่านเว็บ และ field sync)
---
@ -23,7 +23,7 @@
- หน้าจอสร้างเอกสารครั้งแรก = **เว็บแยก (standalone)** — กรอกนอก Word → ดาวน์โหลด .docx ที่ backend stamp metadata + สร้าง v1 ไว้แล้ว → เปิดใน Word แล้ว add-in รู้จักทันที
- Template = **หลาย template + หน้าจัดการเต็มรูปแบบ** (CRUD + นิยาม field ต่อ template)
- Field sync = **เฉพาะ fields จาก template** (ผูกกับ content controls) ไม่เก็บ full text
- **FR-9 ถึง FR-13 เป็น requirement ที่เพิ่มภายหลัง spec เดิม** — spec ปัจจุบันเป็นฉบับ v5 (ครอบ FR-1–13 + AC-1–16: v2 เพิ่ม template/portal/field sync, v3 เพิ่มข้อจำกัดแพลตฟอร์มจากการทดสอบจริง, v4 เพิ่มรายการเอกสารต่อ template + detail + re-generate, v5 เพิ่ม commit message ต่อ version) ให้ถือ spec เป็น source of truth ของ requirements
- **FR-9 ถึง FR-14 เป็น requirement ที่เพิ่มภายหลัง spec เดิม** — spec ปัจจุบันเป็นฉบับ v6 (ครอบ FR-1–14 + AC-1–17: v2 เพิ่ม template/portal/field sync, v3 เพิ่มข้อจำกัดแพลตฟอร์มจากการทดสอบจริง, v4 เพิ่มรายการเอกสารต่อ template + detail + re-generate, v5 เพิ่ม commit message ต่อ version, v6 เพิ่ม editor ต่อ field) ให้ถือ spec เป็น source of truth ของ requirements
---
@ -523,6 +523,27 @@ POST /api/documents/generate { templateId, values: { <fieldKey>: <value> } }
---
## Phase 10: Field editor — FR-14 (เพิ่มทีหลัง, v6)
**เป้า:** ผู้ดูแลเลือกรูปแบบช่องกรอก (editor) ต่อ field ได้ — `text` เลือก `textbox`/`textarea` ได้, `number`/`date` ใช้ control ตาม type — เป็น presentation metadata ล้วน (ค่า field เป็น string ทั้ง flow, validation ยึด `Type` เท่านั้น) — แผนเต็มที่ [`docs/plans/field-editor.md`](field-editor.md)
- [x] Backend: `TemplateField.Editor` (string?, varchar 16 — null = default ของ Type) + `EditorsByType`/`DefaultEditor` + `AppDbContext` config + migration `AddTemplateFieldEditor` (nullable column เดี่ยว ไม่ backfill)
- [x] Backend: `TemplateFieldDefinition.Editor` (ทางเลือก — ไม่ส่ง/ว่าง = default) + validate ตาม type (ไม่รู้จัก/ไม่เข้ากับ type → 400) + normalize-on-write (ค่า = default → เก็บ null) + `TemplateFieldDto.Editor` คืนค่า resolved เสมอ
- [x] Web: util `fieldEditor.ts` (`EDITORS_BY_TYPE`/`defaultEditor`/`editorOptions`/`resolveEditor`/`dateInputValue`) + `FieldEditor` type ใน `api.ts`
- [x] Web: หน้าแก้ field definitions เพิ่ม editor select (แสดงเฉพาะ type `text` ที่มีทางเลือก >1, number/date แสดง `—`) + reset editor เป็น default เมื่อเปลี่ยน type + payload `editor`
- [x] Web: ฟอร์มสร้างเอกสาร render ตาม `resolveEditor` (textarea/number/date/textbox — preserve number/date control เดิม) + prefill re-generate ของ field date ผ่าน `dateInputValue` (input[type=date] รับเฉพาะ yyyy-MM-dd)
- [x] Tests: `FieldEditorTests` (backend 6 กรณี) + `fieldEditor.spec.ts` (web) + extend `TemplateTests` default/echo
หมายเหตุจากการทำจริง (2026-08-22):
- `applyDetail` ของ edit.vue resolve editor ตอนโหลด (`{ ...f, editor: resolveEditor(f) }`) — ฟอร์มถือค่า valid เสมอแม้ API เก่า/ค่าหลุด
- `@change` ของ type select อ่านค่าจาก `e.target` ตรงๆ (`onTypeChange`) ไม่พึ่งลำดับ handler ของ v-model
**DoD:** `dotnet test` + web `npm test`/`npm run build` เขียวทั้งชุด · generate/field sync path ไม่เปลี่ยนพฤติกรรม
> ✅ ตรวจผ่านจริง (2026-08-22): `dotnet test` **60/60** · web `npm test` **46/46** + build ผ่าน · addin ไม่ถูกแตะ (50/50 คงเดิม)
---
## 4. ตาราง mapping: Phase ↔ FR ↔ Acceptance Criteria
| Phase | FR ที่ implement | Acceptance criteria ที่ตรวจได้ |
@ -538,6 +559,7 @@ POST /api/documents/generate { templateId, values: { <fieldKey>: <value> } }
| 7 | — | คุ้มกัน regression ทุก AC (รวม AC-13, 14) |
| 8 | — | ทบทวน AC-1 ถึง AC-14 ทั้งหมด |
| 9 (ทีหลัง, v5) | FR-13 | AC-16 |
| 10 (ทีหลัง, v6) | FR-14 | AC-17 |
**AC เพิ่มเติม (นอก spec เดิม):**
- **AC-11:** upload template .docx ที่มี content controls → ระบบ detect fields อัตโนมัติ + บันทึก field definitions ได้
@ -546,6 +568,7 @@ POST /api/documents/generate { templateId, values: { <fieldKey>: <value> } }
- **AC-14:** ลบ content control ออกจาก Word แล้วกด Save & Version → flow ยังสำเร็จ ค่าเดิมของ field นั้นคงอยู่ (mark missing) ไม่ error
- **AC-15:** (v4) หน้ารายการเอกสารของ template + หน้า detail (ค่าทุก field + version history ขยายเห็นค่าต่อ version + ดาวน์โหลด) + re-generate ได้เอกสารใหม่ v1 โดยเอกสารต้นทางไม่มี version ใหม่ — รายละเอียดตาม FR-12
- **AC-16:** (v5) พิมพ์คำอธิบายตอนสร้าง version ได้ทุก flow (Save & Version, Start Versioning, สร้างจากเว็บ) → ข้อความถูกเก็บกับ version นั้นใน DB และแสดงใน version history ทุกหน้า · เว้นว่างได้ — รายละเอียดตาม FR-13
- **AC-17:** (v6) admin เลือก editor ของ field type `text` เป็น `textarea` ได้ → ฟอร์มสร้างเอกสารแสดงเป็น textarea · number/date ใช้ control ตาม type · editor ไม่ถูกต้อง → 400 · ไม่ส่ง → default ของ type · ค่า field และการ generate ไม่เปลี่ยน — รายละเอียดตาม FR-14
---
@ -578,7 +601,7 @@ docker compose up -d
cd backend/WordVersioning.Api
dotnet run # รัน + swagger ที่ http://localhost:5xxx
dotnet ef migrations add <Name>
cd .. && dotnet test # integration tests (52 ตัว) — ต้องมี Docker รันอยู่ (Testcontainers ยก infra เอง ~1 นาที)
cd .. && dotnet test # integration tests (60 ตัว) — ต้องมี Docker รันอยู่ (Testcontainers ยก infra เอง ~1 นาที)
# add-in (https://localhost:3000)
cd addin
@ -592,5 +615,5 @@ npx office-addin-dev-certs install # ครั้งแรกครั้ง
cd web
npm install
npm run dev
npm test # vitest (36 ตัว)
npm test # vitest (46 ตัว)
```