eCourt-note/components/wordaddin/SaveControls.tsx

66 lines
2.7 KiB
TypeScript
Raw Normal View History

2026-10-16 12:07:25 +07:00
import { CircleCheck, Save, X } from "lucide-react";
import { MessageBar } from "./MessageBar";
import { Spinner } from "./Spinner";
/**
* FR-17/FR-18 — ปุ่ม [บันทึก] / [บันทึกและปิดเอกสาร] + สถานะการบันทึก (ใช้ทุก phase ตาม spec v10)
feat(wordaddin): add template tools tab แท็บที่สองของ taskpane "เครื่องมือ Template" (TemplateToolsPanel + templateService — Office.js ล้วน ไม่ยิง backend ใช้ได้ทั้ง phase untracked/tracking) สำหรับคนออกแบบ template: - สแกน Content Controls ทั้งเอกสาร + เตือน tag ว่าง/ซ้ำ + ปุ่มดูช่อง (select เลื่อน Word ไปให้เอง) - กรอกค่าทดสอบเขียนกลับเอกสารจริงเพื่อดูผลจัดหน้า — ช่องว่างไม่ถูกแตะ, เขียนแยก Word.run ราย tag ให้ fail เฉพาะตัวพร้อมสาเหตุจาก debugInfo - แทรกย่อหน้า/ตัวแบ่งบรรทัด/ตัวแบ่งหน้าที่ cursor — page break มี fallback OOXML block-level กัน E_FAIL ในบางบริบท (เช่น cursor ในตาราง) - มองเห็น CC ทุกชนิดผ่าน feature-detect WordApi 1.5 (Document.getContentControls) — Office 2019 เห็นเฉพาะ rich text พร้อม warning แยก state ไม่โดน banner อื่นล้าง, ห้ามยก MinVersion ของ manifest จาก code review 2026-10-20: - ปุ่ม toggle แท็บปิดระหว่าง flow ที่เขียนเอกสารครบทุกตัว (saving/starting/saveBusy/templateBusy — panel รายงาน busy ผ่าน onBusyChange พร้อม cleanup ตอน unmount) กัน flow versioning มา stamp/capture ซ้อน operation ของแท็บ template ที่ยังรันอยู่ - banner auto-save error ยกจาก SaveControls มาไว้เหนือ toggle ให้เห็นทั้งสองแท็บ - สแกน merge หลังเขียนค่าตัด tag ที่หายไปจากเอกสารทิ้ง (ไม่ค้าง ไปโดน missingTags รอบถัดไป) - รวม describeWordError เป็น getDetailedErrorMessage ที่ errors.ts Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 09:49:17 +07:00
* banner autoSaveError ย้ายไปอยู่เหนือ toggle แท็บของ TaskpaneApp (2026-10-20) — ให้เห็นทั้งสองแท็บ
2026-10-16 12:07:25 +07:00
* พอร์ตจาก SaveControls ใน App.tsx ต้นฉบับ (D:\word-addin-poc\addin) — logic เดิม เปลี่ยนแค่ชั้นภาพ
*/
export function SaveControls({
disabled,
busy,
status,
error,
closeSupported,
onSave,
onSaveAndClose,
}: {
/** กำลังอยู่ใน flow อื่น (versioning) — ปิดปุ่มกันกดซ้อน */
disabled: boolean;
busy: boolean;
status: "saving" | "saved" | null;
error: string | null;
closeSupported: boolean;
onSave: () => void;
onSaveAndClose: () => void;
}) {
return (
<div className="flex flex-col gap-2">
<div className="flex flex-wrap gap-2">
<button
type="button"
className="focus-ring inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink transition hover:border-primary hover:text-primary disabled:pointer-events-none disabled:opacity-50"
disabled={disabled || busy}
onClick={onSave}
>
<Save className="size-4" aria-hidden />
บันทึก
</button>
{closeSupported ? (
<button
type="button"
className="focus-ring inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink transition hover:border-primary hover:text-primary disabled:pointer-events-none disabled:opacity-50"
disabled={disabled || busy}
onClick={onSaveAndClose}
>
<X className="size-4" aria-hidden />
บันทึกและปิดเอกสาร
</button>
) : null}
</div>
{busy && status === "saving" ? <Spinner label="กำลังบันทึก…" /> : null}
{!busy && status === "saved" ? (
<p className="inline-flex items-center gap-1.5 text-sm text-ink-soft">
<CircleCheck className="size-4 text-success-ink" aria-hidden />
บันทึกแล้ว
</p>
) : null}
{error ? <MessageBar intent="error">{error}</MessageBar> : null}
</div>
);
}
export default SaveControls;