diff --git a/frontend/src/components/BlockLibrary.jsx b/frontend/src/components/BlockLibrary.jsx index 617bbb8..d7dc031 100644 --- a/frontend/src/components/BlockLibrary.jsx +++ b/frontend/src/components/BlockLibrary.jsx @@ -1,32 +1,112 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import api from '../services/api'; -const BlockLibrary = ({ canvasRef }) => { +const BlockLibrary = ({ canvasRef, yjsDoc }) => { const [blocks, setBlocks] = useState([]); const [categories, setCategories] = useState([]); const [activeCategory, setActiveCategory] = useState('Alle'); const [loading, setLoading] = useState(true); - + const [selectedElements, setSelectedElements] = useState([]); + const [newBlockName, setNewBlockName] = useState(''); + const [newBlockCategory, setNewBlockCategory] = useState(''); + const [showCreateDialog, setShowCreateDialog] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + + // Load blocks on component mount useEffect(() => { - api.get('/blocks') - .then(res => { - const data = res.data || []; - setBlocks(data); - const cats = ['Alle', ...new Set(data.map(b => b.category || 'Allgemein'))]; - setCategories(cats); - setLoading(false); - }) - .catch(() => setLoading(false)); + loadBlocks(); }, []); - - const filteredBlocks = activeCategory === 'Alle' - ? blocks - : blocks.filter(b => (b.category || 'Allgemein') === activeCategory); - + + const loadBlocks = async () => { + try { + const res = await api.get('/blocks'); + const data = res.data || []; + setBlocks(data); + + // Extract unique categories + const cats = ['Alle', ...new Set(data.map(b => b.category || 'Allgemein'))]; + setCategories(cats); + } catch (error) { + console.error('Failed to load blocks:', error); + } finally { + setLoading(false); + } + }; + + // Filter blocks by category and search query + const filteredBlocks = blocks.filter(block => { + const matchesCategory = activeCategory === 'Alle' || (block.category || 'Allgemein') === activeCategory; + const matchesSearch = block.name.toLowerCase().includes(searchQuery.toLowerCase()) || + (block.category || '').toLowerCase().includes(searchQuery.toLowerCase()); + return matchesCategory && matchesSearch; + }); + + // Handle creating a new block from selected elements + const handleCreateBlock = async () => { + if (!newBlockName.trim() || selectedElements.length === 0) return; + + try { + const response = await api.post('/blocks', { + name: newBlockName, + category: newBlockCategory || 'Allgemein', + elements: selectedElements + }); + + // Add to Yjs document + if (yjsDoc) { + const blockId = response.data.id; + yjsDoc.blocks.set(blockId, response.data); + } + + // Refresh blocks list + loadBlocks(); + + // Reset form + setNewBlockName(''); + setNewBlockCategory(''); + setShowCreateDialog(false); + setSelectedElements([]); + } catch (error) { + console.error('Failed to create block:', error); + alert('Fehler beim Erstellen des Blocks'); + } + }; + + // Handle deleting a block + const handleDeleteBlock = async (blockId, blockName) => { + if (!window.confirm(`Sind Sie sicher, dass Sie den Block "${blockName}" löschen möchten?`)) { + return; + } + + try { + await api.delete(`/blocks/${blockId}`); + + // Remove from Yjs document + if (yjsDoc) { + yjsDoc.blocks.delete(blockId); + } + + // Refresh blocks list + loadBlocks(); + } catch (error) { + console.error('Failed to delete block:', error); + alert('Fehler beim Löschen des Blocks'); + } + }; + + // Handle inserting a block instance on canvas + const handleInsertBlock = (block) => { + if (canvasRef.current) { + canvasRef.current.addSvgObject(block.svg_data); + } + }; + + // Handle drag start for block insertion const handleDragStart = (e, block) => { e.dataTransfer.setData('text/plain', block.svg_data); }; - + + // Handle drop on canvas const handleDrop = (e) => { e.preventDefault(); const svgData = e.dataTransfer.getData('text/plain'); @@ -34,19 +114,36 @@ const BlockLibrary = ({ canvasRef }) => { canvasRef.current.addSvgObject(svgData); } }; - + const handleDragOver = (e) => { e.preventDefault(); }; - + if (loading) return
Keine Blöcke in dieser Kategorie.
+Keine Blöcke gefunden.
+ )} + + {/* Create Block Dialog */} + {showCreateDialog && ( +