"use client"; import { useCallback, useRef, useState } from "react"; // Undo/Redo (PLAN-FEAT.md §3 F2, D10) — history ฝั่ง client ต่อ session ต่อผู้ใช้ // ไม่เก็บใน DB; push เฉพาะ action ของตัวเองที่สำเร็จเท่านั้น (event ของคนอื่นไม่เกี่ยว) // ตัว entry เก็บ closure ที่เรียก Server Action + sync state เอง — id ของ annotation // ที่ "สร้างใหม่" หลัง undo ต้องส่งต่อให้ redo ได้ จึงให้ entry เป็น object ที่แก้ field ได้ export type HistoryEntry = { undo: () => Promise; redo: () => Promise; }; export function useHistory() { // source of truth เป็น ref — state ที่เปลี่ยนแค่ไว้ trigger re-render ปุ่ม disabled const undoRef = useRef([]); const redoRef = useRef([]); const [, bump] = useState(0); /** บันทึก action ที่สำเร็จ — action ใหม่เคลียร์ redo stack เสมอ */ const push = useCallback((entry: HistoryEntry) => { undoRef.current.push(entry); redoRef.current = []; bump((n) => n + 1); }, []); const undo = useCallback(async () => { const entry = undoRef.current.pop(); if (!entry) return; try { await entry.undo(); redoRef.current.push(entry); } catch (err) { // เช่น undo ของ create แต่ row ถูกลบไปแล้วโดยแท็บอื่น → ข้าม ไม่ crash (§5) console.error("undo ไม่สำเร็จ — ข้ามรายการนี้", err); } bump((n) => n + 1); }, []); const redo = useCallback(async () => { const entry = redoRef.current.pop(); if (!entry) return; try { await entry.redo(); undoRef.current.push(entry); } catch (err) { console.error("redo ไม่สำเร็จ — ข้ามรายการนี้", err); } bump((n) => n + 1); }, []); return { push, undo, redo, canUndo: undoRef.current.length > 0, canRedo: redoRef.current.length > 0, }; }