diff --git a/src/main/bundles/dev.bundle b/src/main/bundles/dev.bundle index fc16546..0a446ca 100644 Binary files a/src/main/bundles/dev.bundle and b/src/main/bundles/dev.bundle differ diff --git a/src/main/frontend/invoice-generator/profile-invoice-generator.js b/src/main/frontend/invoice-generator/profile-invoice-generator.js index 258aa30..2bb3fa1 100644 --- a/src/main/frontend/invoice-generator/profile-invoice-generator.js +++ b/src/main/frontend/invoice-generator/profile-invoice-generator.js @@ -166,15 +166,29 @@ window.initProfileInvoiceGenerator = function() { ctx.lineTo(x + w, y); ctx.stroke(); } else if (el.type === 'image') { - ctx.fillStyle = '#f0f0f0'; - ctx.fillRect(x, y, w, h); - ctx.strokeStyle = '#999999'; - ctx.strokeRect(x, y, w, h); - ctx.fillStyle = '#666666'; - ctx.font = Math.max(8, 12 * zoomFactor) + 'px Arial'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText('Bild', x + w / 2, y + h / 2); + if (el.imageData) { + // Draw the uploaded image + var img = new Image(); + img.onload = function() { + ctx.drawImage(img, x, y, w, h); + // Redraw selection if this element is selected + if (selectedElement && selectedElement.id === el.id) { + drawSelection(el); + } + }; + img.src = el.imageData; + } else { + // Draw placeholder + ctx.fillStyle = '#f0f0f0'; + ctx.fillRect(x, y, w, h); + ctx.strokeStyle = '#999999'; + ctx.strokeRect(x, y, w, h); + ctx.fillStyle = '#666666'; + ctx.font = Math.max(8, 12 * zoomFactor) + 'px Arial'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('Bild', x + w / 2, y + h / 2); + } } else { // Text elements ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial'; @@ -567,6 +581,15 @@ window.initProfileInvoiceGenerator = function() { } }; + window.updateProfileElementImage = function(id, imageData) { + var el = elements.find(function(e) { return e.id === id; }); + if (el) { + el.imageData = imageData; + el.text = 'Bild'; + draw(); + } + }; + window.deleteProfileElement = function(id) { var index = elements.findIndex(function(e) { return e.id === id; }); if (index > -1) { diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java index 0ec816d..4b549a6 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java @@ -25,6 +25,8 @@ import java.util.List; import com.vaadin.flow.component.textfield.TextArea; import com.vaadin.flow.component.tabs.TabSheet; +import com.vaadin.flow.component.upload.Upload; +import com.vaadin.flow.component.upload.receivers.MemoryBuffer; import com.vaadin.flow.data.binder.Binder; import com.vaadin.flow.data.validator.EmailValidator; import com.vaadin.flow.router.PageTitle; @@ -1037,6 +1039,41 @@ public class EditProfileView extends HorizontalLayout { propertiesPanelProfile.add(header, typeLabel); + // Für Bildelemente: Upload-Button anzeigen + if ("image".equals(elementType)) { + MemoryBuffer buffer = new MemoryBuffer(); + Upload upload = new Upload(buffer); + upload.setAcceptedFileTypes("image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"); + upload.setMaxFileSize(5 * 1024 * 1024); // 5 MB + upload.setDropLabel(new Span("Bild hierher ziehen oder klicken")); + upload.setWidthFull(); + + upload.addSucceededListener(event -> { + try { + // Bild als Base64 kodieren + byte[] bytes = buffer.getInputStream().readAllBytes(); + String base64 = java.util.Base64.getEncoder().encodeToString(bytes); + String mimeType = event.getMIMEType(); + String dataUrl = "data:" + mimeType + ";base64," + base64; + + // An JavaScript übergeben + getElement().executeJs( + "if (window.updateProfileElementImage) { window.updateProfileElementImage('" + + elementId + "', $0); }", + dataUrl); + Notification.show("Bild erfolgreich hochgeladen", 3000, Notification.Position.BOTTOM_CENTER); + } catch (Exception ex) { + Notification.show("Fehler beim Hochladen: " + ex.getMessage(), 3000, Notification.Position.BOTTOM_CENTER); + } + }); + + upload.addFileRejectedListener(event -> { + Notification.show("Datei abgelehnt: " + event.getErrorMessage(), 3000, Notification.Position.BOTTOM_CENTER); + }); + + propertiesPanelProfile.add(upload); + } + // Text Feld (nur für Text-Elemente) if (!"line".equals(elementType) && !"image".equals(elementType)) { TextField textField = new TextField("Text");