fix: HIGH issues #12,#15,#16,#17,#21 — backend persistence for import/layers/blocks + owner filtering

This commit is contained in:
A0 Orchestrator
2026-06-30 12:16:49 +02:00
parent fb5ec8f4ec
commit 5a21fc0bfa
4 changed files with 150 additions and 28 deletions
+138 -21
View File
@@ -534,7 +534,13 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
const handleRenameBlock = useCallback((id: string, name: string) => {
setBlocks((prev) => prev.map((b) => b.id === id ? { ...b, name } : b));
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block umbenannt: ${name}`, type: 'info' }]);
}, []);
// Persist rename to backend
if (token) {
updateBlock(token, id, { name }).catch((err) => {
console.error('Failed to save block rename:', err);
});
}
}, [token]);
const handleDuplicateBlock = useCallback((id: string) => {
setBlocks((prev) => {
@@ -547,10 +553,16 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
name: `${block.name} (Kopie)`,
elements: block.elements.map((el) => ({ ...el, id: `${el.id}_copy_${Date.now()}` })),
};
// Persist duplicate to backend
if (drawingId && token) {
createBlockTyped(token, drawingId, copy).catch((err) => {
console.error('Failed to save duplicated block:', err);
});
}
return [...prev, copy];
});
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Block dupliziert', type: 'info' }]);
}, []);
}, [drawingId, token]);
const handleDeleteBlock = useCallback((id: string) => {
setBlocks((prev) => prev.filter((b) => b.id !== id));
@@ -643,15 +655,42 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
const result = await importFile(file);
if (result.success) {
setElements((prev) => [...prev, ...result.elements]);
// Push imported elements to Yjs and backend
for (const el of result.elements) {
collab.setElement(el);
}
if (drawingId && token) {
setSavedStatus('Speichert…');
Promise.all(result.elements.map(el => createElementTyped(token, drawingId, el)))
.then(() => setSavedStatus('gespeichert'))
.catch((err) => {
console.error('Failed to save imported elements:', err);
setSavedStatus('Fehler beim Speichern');
});
}
if (result.layers && result.layers.length > 0) {
setLayers((prev) => {
const existingIds = new Set(prev.map(l => l.id));
const newLayers = result.layers!.filter(l => !existingIds.has(l.id));
return [...prev, ...newLayers];
});
const existingIds = new Set(layers.map(l => l.id));
const newLayers = result.layers.filter(l => !existingIds.has(l.id));
setLayers((prev) => [...prev, ...newLayers]);
// Push imported layers to Yjs and backend
for (const layer of newLayers) {
collab.setLayer(layer);
}
if (drawingId && token) {
Promise.all(newLayers.map(l => createLayerTyped(token, drawingId, l)))
.catch((err) => console.error('Failed to save imported layers:', err));
}
}
if (result.blocks && result.blocks.length > 0) {
setBlocks((prev) => [...prev, ...result.blocks!]);
// Push imported blocks to Yjs and backend
for (const block of result.blocks) {
collab.setBlock(block);
}
if (drawingId && token) {
Promise.all(result.blocks.map(b => createBlockTyped(token, drawingId, b)))
.catch((err) => console.error('Failed to save imported blocks:', err));
}
}
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Importiert: ${result.elements.length} Elemente aus ${file.name}`, type: 'info' }]);
if (result.warnings.length > 0) {
@@ -660,7 +699,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
} else {
setCommandHistory((prev) => [...prev, { prefix: '·', text: `Import fehlgeschlagen: ${result.error || 'Unbekannter Fehler'}`, type: 'info' }]);
}
}, []);
}, [drawingId, token, collab, layers]);
const handleExport = useCallback(async (format: ExportFormat) => {
const projectData: ProjectData = {
@@ -866,8 +905,20 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
});
}, [drawingId, token]);
const handleToggleLayer = useCallback((id: string) => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, visible: !l.visible } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, visible: !l.visible } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { visible: changed.visible }).catch((err) => {
console.error('Failed to save layer visibility:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleDeleteLayer = useCallback((id: string) => {
setLayers((prev) => prev.filter((l) => l.id !== id));
// Delete from backend
@@ -878,29 +929,95 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
}
}, [token]);
const handleRenameLayer = useCallback((id: string, name: string) => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, name } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, name } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { name }).catch((err) => {
console.error('Failed to save layer rename:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleDuplicateLayer = useCallback((id: string) => {
setLayers((prev) => {
const layer = prev.find((l) => l.id === id);
if (!layer) return prev;
const newId = `layer-${Date.now()}`;
const copy: CADLayer = { ...layer, id: newId, name: `${layer.name} (Kopie)`, sortOrder: prev.length };
collab.setLayer(copy);
if (drawingId && token) {
createLayerTyped(token, drawingId, copy).catch((err) => {
console.error('Failed to save duplicated layer:', err);
});
}
return [...prev, copy];
});
}, []);
}, [drawingId, token, collab]);
const handleToggleLock = useCallback((id: string) => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, locked: !l.locked } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, locked: !l.locked } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { locked: changed.locked }).catch((err) => {
console.error('Failed to save layer lock state:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleUpdateLayerColor = useCallback((id: string, color: string) => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, color } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, color } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { color }).catch((err) => {
console.error('Failed to save layer color:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleUpdateLayerLineType = useCallback((id: string, lineType: 'solid' | 'dashed' | 'dotted') => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, lineType } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, lineType } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { lineType }).catch((err) => {
console.error('Failed to save layer line type:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleUpdateLayerTransparency = useCallback((id: string, transparency: number) => {
setLayers((prev) => prev.map((l) => l.id === id ? { ...l, transparency } : l));
}, []);
setLayers((prev) => {
const updated = prev.map((l) => l.id === id ? { ...l, transparency } : l);
const changed = updated.find((l) => l.id === id);
if (changed) {
collab.setLayer(changed);
if (token) {
updateLayer(token, id, { transparency }).catch((err) => {
console.error('Failed to save layer transparency:', err);
});
}
}
return updated;
});
}, [token, collab]);
const handleZoomIn = useCallback(() => {
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: vergrößert', type: 'info' }]);