ui-nyla/src/pages/views/workspace/useWorkspaceEditor.ts
2026-03-17 22:51:36 +01:00

127 lines
4 KiB
TypeScript

/**
* 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<void>;
rejectEdit: (editId: string) => Promise<void>;
acceptAll: () => Promise<void>;
rejectAll: () => Promise<void>;
refresh: () => void;
pendingCount: number;
}
export function useWorkspaceEditor(instanceId: string): UseWorkspaceEditorReturn {
const [edits, setEdits] = useState<EditorFileEdit[]>([]);
const [activeEditId, setActiveEditId] = useState<string | null>(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,
};
}