import { useCallback, useEffect, useState } from 'react'; import { kanbanApi, type Board, type Column, type DocPage, type ReferenceType, } from '../../lib/kanbanApi'; interface UseKanbanBoard { board: Board | null; pages: DocPage[]; loading: boolean; /** Last mutation error, if any (cleared on next successful action/reload). */ error: string | null; reload: () => Promise; moveCard: (id: string, status: Column) => Promise; addComment: (cardId: string, text: string, author: string) => Promise; deleteComment: (commentId: string, cardId: string) => Promise; addTag: (cardId: string, tag: string) => Promise; removeTag: (cardId: string, tag: string) => Promise; addReference: ( cardId: string, ref: { label: string; type: ReferenceType; href: string }, ) => Promise; removeReference: (cardId: string, href: string) => Promise; } export function useKanbanBoard(): UseKanbanBoard { const [board, setBoard] = useState(null); const [pages, setPages] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const reload = useCallback(async () => { setLoading(true); setError(null); try { const [b, p] = await Promise.all([kanbanApi.getBoard(), kanbanApi.getPages()]); setBoard(b); setPages(p); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to load board'); } finally { setLoading(false); } }, []); useEffect(() => { void reload(); }, [reload]); /** Run a mutation, optimistic-local-updating via `apply`, then reconcile. */ const run = useCallback( async ( apply: () => void, remote: () => Promise, ): Promise => { setError(null); apply(); // optimistic local update try { await remote(); } catch (e) { setError(e instanceof Error ? e.message : 'Mutation failed'); await reload(); // reconcile on failure } }, [reload], ); const moveCard = useCallback( (id: string, status: Column) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => (c.id === id ? { ...c, status } : c)) } : prev, ), () => kanbanApi.moveCard(id, status), ), [run], ); const addComment = useCallback( async (cardId: string, text: string, author: string) => { setError(null); try { const comment = await kanbanApi.addComment(cardId, text, author); setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId ? { ...c, comments: [...c.comments, comment] } : c, ), } : prev, ); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to add comment'); } }, [], ); const deleteComment = useCallback( (commentId: string, cardId: string) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId ? { ...c, comments: c.comments.filter((cm) => cm.id !== commentId) } : c, ), } : prev, ), () => kanbanApi.deleteComment(commentId), ), [run], ); const addTag = useCallback( (cardId: string, tag: string) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId && !c.tags.includes(tag) ? { ...c, tags: [...c.tags, tag] } : c, ), } : prev, ), () => kanbanApi.addTag(cardId, tag), ), [run], ); const removeTag = useCallback( (cardId: string, tag: string) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId ? { ...c, tags: c.tags.filter((t) => t !== tag) } : c, ), } : prev, ), () => kanbanApi.removeTag(cardId, tag), ), [run], ); const addReference = useCallback( (cardId: string, ref: { label: string; type: ReferenceType; href: string }) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId && !c.references.some((r) => r.href === ref.href) ? { ...c, references: [...c.references, { ...ref, removable: true }] } : c, ), } : prev, ), () => kanbanApi.addReference(cardId, ref), ), [run], ); const removeReference = useCallback( (cardId: string, href: string) => run( () => setBoard((prev) => prev ? { ...prev, cards: prev.cards.map((c) => c.id === cardId ? { ...c, references: c.references.filter((r) => r.href !== href) } : c, ), } : prev, ), () => kanbanApi.removeReference(cardId, href), ), [run], ); return { board, pages, loading, error, reload, moveCard, addComment, deleteComment, addTag, removeTag, addReference, removeReference, }; }