Phase 12: draft generation (FR-16)
ไฟล์ร่าง (draft) — generate docx จาก template แบบ stateless ไม่สร้าง
Document/version; stamp เฉพาะ custom property TemplateId เพื่อให้ add-in
เห็นเป็น untracked; Start Versioning ส่ง form field templateId ให้ backend
ผูก template ตอน auto-create — เอกสารเริ่มที่ v1 (ไม่มี v0) + field sync เต็มระบบ
- Backend: POST /api/templates/{id}/generate-draft (200 คืนไฟล์) +
DocxGenerationService.GenerateDraft + แยก TemplateFieldValidation ใช้ร่วมกับ
generate + UploadVersion รับ templateId (non-GUID/unknown → 400,
deactivated → ผูกได้, document เดิม → เฉย) + CORS expose Content-Disposition
- Add-in: readTrackedProperties อ่าน TemplateId + ส่ง templateId เฉพาะ
startVersioning + hint ไฟล์ร่างใน task pane
- Web: ปุ่ม "สร้างไฟล์ร่าง" บนฟอร์มเดิม + downloadFile รับ init (POST → blob)
- Docs: spec v8 (FR-16, AC-19) + Phase 12 + CONTEXT.md + ADR 0002 +
acceptance-review AC-19
- Tests: backend 82 / add-in 66 / web 59 + build ครบ; QA reviewer PASS
(2 rounds); ค้าง manual บน Word จริง
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
0edb70fd38
commit
b3c1c667f7
30 changed files with 879 additions and 93 deletions
|
|
@ -76,6 +76,8 @@ const App: React.FC = () => {
|
|||
const [username, setUsername] = React.useState<string | null>(null);
|
||||
|
||||
const [documentId, setDocumentId] = React.useState<string | null>(null);
|
||||
/** FR-16 — template ต้นทางของไฟล์ร่าง (มีเฉพาะไฟล์ที่เว็บสร้างแบบ draft และยังไม่ได้ track) */
|
||||
const [documentTemplateId, setDocumentTemplateId] = React.useState<string | null>(null);
|
||||
const [versions, setVersions] = React.useState<VersionRecord[]>([]);
|
||||
const [historyLoading, setHistoryLoading] = React.useState(false);
|
||||
const [historyError, setHistoryError] = React.useState<string | null>(null);
|
||||
|
|
@ -101,6 +103,7 @@ const App: React.FC = () => {
|
|||
if (error instanceof AuthRequiredError) {
|
||||
setPhase("signed-out");
|
||||
setDocumentId(null);
|
||||
setDocumentTemplateId(null);
|
||||
setVersions([]);
|
||||
setLoginError("เซสชันหมดอายุ — กรุณา login ใหม่");
|
||||
return true;
|
||||
|
|
@ -125,7 +128,9 @@ const App: React.FC = () => {
|
|||
[handleAuthRequired]
|
||||
);
|
||||
|
||||
/** ตรวจตอนเปิด task pane (FR-1): token → SavedByAddIn → history */
|
||||
/** ตรวจตอนเปิด task pane (FR-1): token → SavedByAddIn → history
|
||||
* FR-16 — อ่าน properties ครั้งเดียวทั้งชุดแล้ว derive tracked เอง เพราะไฟล์ร่าง (untracked)
|
||||
* ต้องได้อ่าน TemplateId ไปใช้ตอน Start Versioning ด้วย */
|
||||
const detect = React.useCallback(async () => {
|
||||
setPhase("checking");
|
||||
setBootError(null);
|
||||
|
|
@ -138,19 +143,17 @@ const App: React.FC = () => {
|
|||
authService.startAutoRefresh();
|
||||
setUsername(authService.getUsername() ?? null);
|
||||
|
||||
const tracked = await metadataService.isTrackedDocument();
|
||||
if (!tracked) {
|
||||
setPhase("untracked");
|
||||
return;
|
||||
}
|
||||
const properties = await metadataService.readTrackedProperties();
|
||||
const id = properties.documentTrackingId;
|
||||
const tracked = properties.savedByAddIn === metadataService.ADDIN_SIGNATURE_GUID;
|
||||
const id = tracked ? properties.documentTrackingId : undefined;
|
||||
if (!id) {
|
||||
// mark ว่า track แต่ไม่มี id (ไฟล์เสียหาย) — ให้เริ่มใหม่แบบ untracked
|
||||
// ไม่เคย track / track แต่ไม่มี id (ไฟล์เสียหาย) — ไฟล์ร่างจาก template อยู่กลุ่มนี้ (FR-16)
|
||||
setDocumentTemplateId(properties.templateId ?? null);
|
||||
setPhase("untracked");
|
||||
return;
|
||||
}
|
||||
setDocumentId(id);
|
||||
setDocumentTemplateId(null);
|
||||
setPhase("tracking");
|
||||
await refreshHistory(id);
|
||||
} catch (error) {
|
||||
|
|
@ -207,20 +210,23 @@ const App: React.FC = () => {
|
|||
authService.signOut();
|
||||
setUsername(null);
|
||||
setDocumentId(null);
|
||||
setDocumentTemplateId(null);
|
||||
setVersions([]);
|
||||
setLoginError(null);
|
||||
setPhase("signed-out");
|
||||
};
|
||||
|
||||
/** Start Versioning — stamp รอบแรก + upload v1 (FR-1/AC-5) · message คือ commit message จาก dialog (FR-13) */
|
||||
/** Start Versioning — stamp รอบแรก + upload v1 (FR-1/AC-5) · message คือ commit message จาก dialog (FR-13)
|
||||
* FR-16 — ไฟล์ร่าง: แนบ templateId ไปให้ backend ผูก Document กับ template ต้นทาง */
|
||||
const handleStartVersioning = async (message: string) => {
|
||||
setStarting(true);
|
||||
setFlowError(null);
|
||||
setFlowSuccess(null);
|
||||
setFlowWarning(null);
|
||||
try {
|
||||
const result = await startVersioning(message, setProgressMessage);
|
||||
const result = await startVersioning(message, documentTemplateId ?? undefined, setProgressMessage);
|
||||
setDocumentId(result.documentId);
|
||||
setDocumentTemplateId(null);
|
||||
setPhase("tracking");
|
||||
setFlowSuccess(`เริ่มติดตามเอกสารนี้แล้ว — สร้างเป็น version 1`);
|
||||
if (!result.hasPdf)
|
||||
|
|
@ -361,6 +367,12 @@ const App: React.FC = () => {
|
|||
กดปุ่มด้านล่างเพื่อเริ่ม track — ระบบจะ stamp metadata ลงไฟล์และอัปโหลดเป็น version 1
|
||||
(ไฟล์ที่สร้างจากเว็บ portal จะถูกตรวจพบและข้ามขั้นตอนนี้อัตโนมัติ)
|
||||
</Text>
|
||||
{documentTemplateId ? (
|
||||
<Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
|
||||
ไฟล์นี้เป็นไฟล์ร่างจาก template — กดเริ่มติดตามแล้วระบบจะผูกเอกสารกับ template ต้นทาง
|
||||
และ sync ค่า fields ให้อัตโนมัติ
|
||||
</Text>
|
||||
) : null}
|
||||
<Button
|
||||
appearance="primary"
|
||||
icon={<DocumentArrowUp24Regular />}
|
||||
|
|
@ -454,7 +466,11 @@ const App: React.FC = () => {
|
|||
open={startConfirmOpen}
|
||||
busy={starting}
|
||||
title="เริ่มติดตามเอกสารนี้?"
|
||||
description="ระบบจะ stamp metadata ลงไฟล์และบันทึกไฟล์ปัจจุบันเป็น version 1 ของเอกสารนี้"
|
||||
description={
|
||||
documentTemplateId
|
||||
? "ระบบจะ stamp metadata ลงไฟล์ บันทึกไฟล์ปัจจุบันเป็น version 1 และผูกเอกสารกับ template ต้นทาง (ไฟล์ร่าง)"
|
||||
: "ระบบจะ stamp metadata ลงไฟล์และบันทึกไฟล์ปัจจุบันเป็น version 1 ของเอกสารนี้"
|
||||
}
|
||||
confirmLabel="เริ่มติดตาม (version 1)"
|
||||
onConfirm={(message) => void handleStartVersioning(message)}
|
||||
onDismiss={() => setStartConfirmOpen(false)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue