/** * useWorkspaceEditor Hook * * State management for the workspace editor page. * Loads pending file edit proposals from the API, * provides accept/reject actions, and tracks the active tab. */ import { useState, useCallback, useEffect } from 'react'; import api from '../../../api'; export interface EditorFileEdit { id: string; fileId: string; fileName: string; mimeType: string; oldContent: string; newContent: string; status: 'pending' | 'accepted' | 'rejected'; workflowId: string; } interface UseWorkspaceEditorReturn { edits: EditorFileEdit[]; activeEditId: string | null; isLoading: boolean; setActiveEditId: (id: string | null) => void; acceptEdit: (editId: string) => Promise; rejectEdit: (editId: string) => Promise; acceptAll: () => Promise; rejectAll: () => Promise; refresh: () => void; pendingCount: number; } export function useWorkspaceEditor(instanceId: string): UseWorkspaceEditorReturn { const [edits, setEdits] = useState([]); const [activeEditId, setActiveEditId] = useState(null); const [isLoading, setIsLoading] = useState(false); const refresh = useCallback(() => { if (!instanceId) return; setIsLoading(true); api.get(`/api/workspace/${instanceId}/pending-edits`) .then(res => { const loadedEdits: EditorFileEdit[] = (res.data.edits || []).map((e: any) => ({ id: e.id, fileId: e.fileId || '', fileName: e.fileName || '', mimeType: e.mimeType || '', oldContent: e.oldContent || '', newContent: e.newContent || '', status: e.status || 'pending', workflowId: e.workflowId || '', })); setEdits(loadedEdits); if (loadedEdits.length > 0 && !activeEditId) { setActiveEditId(loadedEdits[0].id); } }) .catch(err => console.error('Failed to load pending edits:', err)) .finally(() => setIsLoading(false)); }, [instanceId, activeEditId]); useEffect(() => { refresh(); }, [instanceId]); // eslint-disable-line react-hooks/exhaustive-deps const acceptEdit = useCallback(async (editId: string) => { if (!instanceId) return; setEdits(prev => prev.map(e => (e.id === editId ? { ...e, status: 'accepted' as const } : e))); try { await api.post(`/api/workspace/${instanceId}/edit/${editId}/accept`); } catch (err) { console.error('Failed to accept edit:', err); setEdits(prev => prev.map(e => (e.id === editId ? { ...e, status: 'pending' as const } : e))); } }, [instanceId]); const rejectEdit = useCallback(async (editId: string) => { if (!instanceId) return; setEdits(prev => prev.map(e => (e.id === editId ? { ...e, status: 'rejected' as const } : e))); try { await api.post(`/api/workspace/${instanceId}/edit/${editId}/reject`); } catch (err) { console.error('Failed to reject edit:', err); setEdits(prev => prev.map(e => (e.id === editId ? { ...e, status: 'pending' as const } : e))); } }, [instanceId]); const acceptAll = useCallback(async () => { if (!instanceId) return; setEdits(prev => prev.map(e => (e.status === 'pending' ? { ...e, status: 'accepted' as const } : e))); try { await api.post(`/api/workspace/${instanceId}/edit/accept-all`); } catch (err) { console.error('Failed to accept all edits:', err); refresh(); } }, [instanceId, refresh]); const rejectAll = useCallback(async () => { if (!instanceId) return; setEdits(prev => prev.map(e => (e.status === 'pending' ? { ...e, status: 'rejected' as const } : e))); try { await api.post(`/api/workspace/${instanceId}/edit/reject-all`); } catch (err) { console.error('Failed to reject all edits:', err); refresh(); } }, [instanceId, refresh]); const pendingCount = edits.filter(e => e.status === 'pending').length; return { edits, activeEditId, isLoading, setActiveEditId, acceptEdit, rejectEdit, acceptAll, rejectAll, refresh, pendingCount, }; }