Erweiterungen
This commit is contained in:
Binary file not shown.
@@ -76,7 +76,8 @@ window.initProfileInvoiceGenerator = function() {
|
||||
el.fontSize || 14,
|
||||
el.color || '#333333',
|
||||
el.width || 100,
|
||||
el.height || 30
|
||||
el.height || 30,
|
||||
el.isStatic || false
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -140,36 +141,51 @@ window.initProfileInvoiceGenerator = function() {
|
||||
function drawElement(el) {
|
||||
ctx.save();
|
||||
|
||||
var x = pageX + el.x;
|
||||
var y = pageY + el.y;
|
||||
var w = el.width || 100;
|
||||
var h = el.height || 30;
|
||||
|
||||
if (el.type === 'line') {
|
||||
ctx.strokeStyle = el.color || '#333333';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pageX + el.x, pageY + el.y);
|
||||
ctx.lineTo(pageX + el.x + el.width, pageY + el.y);
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.stroke();
|
||||
} else if (el.type === 'image') {
|
||||
ctx.fillStyle = '#f0f0f0';
|
||||
ctx.fillRect(pageX + el.x, pageY + el.y, el.width, el.height);
|
||||
ctx.fillRect(x, y, w, h);
|
||||
ctx.strokeStyle = '#999999';
|
||||
ctx.strokeRect(pageX + el.x, pageY + el.y, el.width, el.height);
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
ctx.fillStyle = '#666666';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('Bild', pageX + el.x + el.width / 2, pageY + el.y + el.height / 2);
|
||||
ctx.fillText('Bild', x + w / 2, y + h / 2);
|
||||
} else {
|
||||
// Text elements
|
||||
ctx.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
||||
ctx.fillStyle = el.color || '#333333';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
// Draw background highlight for static elements
|
||||
if (el.isStatic) {
|
||||
var textWidth = ctx.measureText(el.text || '').width + 10;
|
||||
ctx.fillStyle = 'rgba(25, 118, 210, 0.1)'; // Light blue background
|
||||
ctx.fillRect(x - 3, y - 2, Math.max(w, textWidth), h);
|
||||
}
|
||||
|
||||
ctx.fillStyle = el.color || '#333333';
|
||||
ctx.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
||||
|
||||
var lines = (el.text || '').split('\n');
|
||||
var lineHeight = (el.fontSize || 14) * 1.2;
|
||||
var y = pageY + el.y;
|
||||
var ty = y;
|
||||
|
||||
lines.forEach(function(line) {
|
||||
ctx.fillText(line, pageX + el.x, y);
|
||||
y += lineHeight;
|
||||
ctx.fillText(line, x, ty);
|
||||
ty += lineHeight;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -342,7 +358,7 @@ window.initProfileInvoiceGenerator = function() {
|
||||
});
|
||||
|
||||
// Add element function
|
||||
window.addProfileElement = function(type, label, dropX, dropY) {
|
||||
window.addProfileElement = function(type, label, dropX, dropY, isStatic, staticText) {
|
||||
elementCounter++;
|
||||
var id = 'element-' + elementCounter;
|
||||
|
||||
@@ -362,9 +378,17 @@ window.initProfileInvoiceGenerator = function() {
|
||||
width: 150,
|
||||
height: 30,
|
||||
fontSize: 14,
|
||||
color: '#333333'
|
||||
color: '#333333',
|
||||
isStatic: isStatic || false
|
||||
};
|
||||
|
||||
// Handle static elements (user data)
|
||||
if (isStatic && staticText) {
|
||||
el.text = staticText;
|
||||
el.color = '#1976d2'; // Blue color for static elements
|
||||
el.fontStyle = 'bold';
|
||||
el.height = 20;
|
||||
} else {
|
||||
switch (type) {
|
||||
case 'text':
|
||||
el.text = 'Text eingeben...';
|
||||
@@ -414,6 +438,7 @@ window.initProfileInvoiceGenerator = function() {
|
||||
el.text = label || 'Neues Element';
|
||||
el.height = 20;
|
||||
}
|
||||
}
|
||||
|
||||
elements.push(el);
|
||||
selectedElement = el;
|
||||
@@ -538,12 +563,14 @@ window.initProfileInvoiceGenerator = function() {
|
||||
|
||||
var templateType = e.dataTransfer.getData('template-type');
|
||||
var templateLabel = e.dataTransfer.getData('template-label');
|
||||
var isStatic = e.dataTransfer.getData('is-static') === 'true';
|
||||
var staticText = e.dataTransfer.getData('static-text');
|
||||
|
||||
if (templateType && window.addProfileElement) {
|
||||
var rect = container.getBoundingClientRect();
|
||||
var x = e.clientX - rect.left;
|
||||
var y = e.clientY - rect.top;
|
||||
window.addProfileElement(templateType, templateLabel, x, y);
|
||||
window.addProfileElement(templateType, templateLabel, x, y, isStatic, staticText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -295,14 +295,11 @@ public class EditProfileView extends HorizontalLayout {
|
||||
|
||||
// Nur die Checkbox "Rechnungslegung über votianLT"
|
||||
billingEnabled = new Checkbox("Rechnungslegung über votianLT");
|
||||
billingEnabled.setValue(false);
|
||||
billingEnabled.addValueChangeListener(e -> {
|
||||
// Nur noch für das Speichern des Status, keine PDF-Vorschau mehr
|
||||
});
|
||||
billingEnabled.setValue(true); // Standardmäßig aktiviert
|
||||
billingTab.add(billingEnabled);
|
||||
|
||||
// Hauptlayout: Links (Templates) | Mitte (Canvas) | Rechts (Eigenschaften)
|
||||
HorizontalLayout mainLayout = new HorizontalLayout();
|
||||
final HorizontalLayout mainLayout = new HorizontalLayout();
|
||||
mainLayout.setWidthFull();
|
||||
mainLayout.setHeight("500px");
|
||||
mainLayout.setSpacing(true);
|
||||
@@ -360,8 +357,11 @@ public class EditProfileView extends HorizontalLayout {
|
||||
billingTab.add(mainLayout);
|
||||
billingTab.expand(mainLayout);
|
||||
|
||||
// Initialen Zustand setzen (sichtbar da checkbox standardmäßig true)
|
||||
mainLayout.setVisible(true);
|
||||
|
||||
// Action Buttons für den Rechnungsgenerator
|
||||
HorizontalLayout actionLayout = new HorizontalLayout();
|
||||
final HorizontalLayout actionLayout = new HorizontalLayout();
|
||||
actionLayout.setWidthFull();
|
||||
actionLayout.setJustifyContentMode(JustifyContentMode.END);
|
||||
actionLayout.setSpacing(true);
|
||||
@@ -401,8 +401,18 @@ public class EditProfileView extends HorizontalLayout {
|
||||
actionLayout.add(clearButton, previewPdfButton, saveTemplateButton);
|
||||
billingTab.add(actionLayout);
|
||||
|
||||
// Initialen Zustand setzen (sichtbar da checkbox standardmäßig true)
|
||||
actionLayout.setVisible(true);
|
||||
|
||||
tabSheet.add("Rechnungserstellung", billingTab);
|
||||
|
||||
// Sichtbarkeit des Invoice Generators an Checkbox binden
|
||||
billingEnabled.addValueChangeListener(e -> {
|
||||
boolean visible = e.getValue();
|
||||
mainLayout.setVisible(visible);
|
||||
actionLayout.setVisible(visible);
|
||||
});
|
||||
|
||||
// Bestehende Rechnungsdaten laden (nur für die Checkbox)
|
||||
loadInvoiceData();
|
||||
|
||||
@@ -416,7 +426,9 @@ public class EditProfileView extends HorizontalLayout {
|
||||
" if (window.initProfileInvoiceGenerator) { " +
|
||||
" window.initProfileInvoiceGenerator(); " +
|
||||
" console.log('Canvas initialized, now loading template...'); " +
|
||||
" setTimeout(function() { " +
|
||||
" $0.$server.onCanvasReady(); " +
|
||||
" }, 200); " +
|
||||
" } else { " +
|
||||
" console.error('initProfileInvoiceGenerator not found'); " +
|
||||
" } " +
|
||||
@@ -848,27 +860,101 @@ public class EditProfileView extends HorizontalLayout {
|
||||
panel.setSpacing(true);
|
||||
panel.setHeightFull();
|
||||
|
||||
Span header = new Span("Textbausteine");
|
||||
header.getStyle()
|
||||
// Bereich 1: Rechnungselemente (Stammdaten)
|
||||
Span invoiceHeader = new Span("Rechnungselemente");
|
||||
invoiceHeader.getStyle()
|
||||
.set("font-weight", "bold")
|
||||
.set("font-size", "var(--lumo-font-size-l)");
|
||||
.set("font-size", "var(--lumo-font-size-m)")
|
||||
.set("margin-top", "var(--lumo-space-s)");
|
||||
|
||||
// Stammdaten des Benutzers als unveränderbare Elemente
|
||||
String company = safe(currentUser.getCompany());
|
||||
String fullName = safe(currentUser.getFirstname()) + " " + safe(currentUser.getName());
|
||||
String street = safe(currentUser.getStreet()) + " " + safe(currentUser.getHouseNumber());
|
||||
String city = safe(currentUser.getZip()) + " " + safe(currentUser.getCity());
|
||||
String email = safe(currentUser.getEmail());
|
||||
String phone = safe(currentUser.getPhone());
|
||||
|
||||
Div senderCompany = createStaticTemplate("Firma", VaadinIcon.OFFICE, "sender_company",
|
||||
company.isEmpty() ? "Ihre Firma" : company);
|
||||
Div senderName = createStaticTemplate("Name", VaadinIcon.USER, "sender_name",
|
||||
fullName.trim().isEmpty() ? "Ihr Name" : fullName.trim());
|
||||
Div senderAddress = createStaticTemplate("Adresse", VaadinIcon.MAP_MARKER, "sender_address",
|
||||
street.trim().isEmpty() ? "Ihre Straße" : street.trim());
|
||||
Div senderCity = createStaticTemplate("Ort", VaadinIcon.BUILDING, "sender_city",
|
||||
city.trim().isEmpty() ? "PLZ Ort" : city.trim());
|
||||
Div senderEmail = createStaticTemplate("E-Mail", VaadinIcon.ENVELOPE, "sender_email",
|
||||
email.isEmpty() ? "ihre@email.de" : email);
|
||||
Div senderPhone = createStaticTemplate("Telefon", VaadinIcon.PHONE, "sender_phone",
|
||||
phone.isEmpty() ? "Ihre Telefonnummer" : phone);
|
||||
|
||||
// Bereich 2: Freie Elemente
|
||||
Span freeHeader = new Span("Freie Elemente");
|
||||
freeHeader.getStyle()
|
||||
.set("font-weight", "bold")
|
||||
.set("font-size", "var(--lumo-font-size-m)")
|
||||
.set("margin-top", "var(--lumo-space-m)");
|
||||
|
||||
// Draggable Templates
|
||||
Div textBlock = createDraggableTemplate("Textfeld", VaadinIcon.TEXT_LABEL, "text");
|
||||
Div headerBlock = createDraggableTemplate("Überschrift", VaadinIcon.HEADER, "header");
|
||||
Div dateBlock = createDraggableTemplate("Datum", VaadinIcon.CALENDAR, "date");
|
||||
Div customerBlock = createDraggableTemplate("Kundeninfo", VaadinIcon.USER, "customer");
|
||||
Div companyBlock = createDraggableTemplate("Firmeninfo", VaadinIcon.OFFICE, "company");
|
||||
Div companyBlock = createDraggableTemplate("Firmeninfo", VaadinIcon.WORKPLACE, "company");
|
||||
Div amountBlock = createDraggableTemplate("Betrag", VaadinIcon.COIN_PILES, "amount");
|
||||
Div lineBlock = createDraggableTemplate("Linie", VaadinIcon.LINE_V, "line");
|
||||
Div imageBlock = createDraggableTemplate("Bild", VaadinIcon.PICTURE, "image");
|
||||
|
||||
panel.add(header, textBlock, headerBlock, dateBlock, customerBlock, companyBlock, amountBlock, lineBlock,
|
||||
imageBlock);
|
||||
panel.add(
|
||||
invoiceHeader,
|
||||
senderCompany, senderName, senderAddress, senderCity, senderEmail, senderPhone,
|
||||
freeHeader,
|
||||
textBlock, headerBlock, dateBlock, customerBlock, companyBlock, amountBlock, lineBlock, imageBlock
|
||||
);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Div createStaticTemplate(String label, VaadinIcon icon, String type, String defaultText) {
|
||||
Div template = new Div();
|
||||
template.setText(label);
|
||||
template.getStyle()
|
||||
.set("padding", "var(--lumo-space-s)")
|
||||
.set("margin", "var(--lumo-space-xs) 0")
|
||||
.set("background-color", "var(--lumo-contrast-10pct)")
|
||||
.set("border", "1px solid var(--lumo-contrast-30pct)")
|
||||
.set("border-radius", "var(--lumo-border-radius-m)")
|
||||
.set("cursor", "grab")
|
||||
.set("display", "flex")
|
||||
.set("align-items", "center")
|
||||
.set("gap", "var(--lumo-space-s)")
|
||||
.set("user-select", "none")
|
||||
.set("font-size", "var(--lumo-font-size-s)");
|
||||
|
||||
Icon templateIcon = icon.create();
|
||||
templateIcon.setSize("var(--lumo-icon-size-s)");
|
||||
template.getElement().insertChild(0, templateIcon.getElement());
|
||||
|
||||
template.getElement().setAttribute("draggable", "true");
|
||||
template.getElement().setAttribute("data-template-type", type);
|
||||
template.getElement().setAttribute("data-template-label", label);
|
||||
template.getElement().setAttribute("data-static-text", defaultText);
|
||||
|
||||
template.getElement().executeJs(
|
||||
"this.addEventListener('dragstart', function(e) {" +
|
||||
" e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));" +
|
||||
" e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));" +
|
||||
" e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));" +
|
||||
" e.dataTransfer.setData('is-static', 'true');" +
|
||||
" this.style.opacity = '0.5';" +
|
||||
"});" +
|
||||
"this.addEventListener('dragend', function(e) {" +
|
||||
" this.style.opacity = '1';" +
|
||||
"});");
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private Div createDraggableTemplate(String label, VaadinIcon icon, String type) {
|
||||
Div template = new Div();
|
||||
template.setText(label);
|
||||
@@ -896,6 +982,7 @@ public class EditProfileView extends HorizontalLayout {
|
||||
"this.addEventListener('dragstart', function(e) {" +
|
||||
" e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));" +
|
||||
" e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));" +
|
||||
" e.dataTransfer.setData('is-static', 'false');" +
|
||||
" this.style.opacity = '0.5';" +
|
||||
"});" +
|
||||
"this.addEventListener('dragend', function(e) {" +
|
||||
@@ -929,7 +1016,7 @@ public class EditProfileView extends HorizontalLayout {
|
||||
|
||||
@ClientCallable
|
||||
public void updatePropertiesPanel(String elementId, String elementType, String text, Double x, Double y,
|
||||
Integer fontSize, String color, Double width, Double height) {
|
||||
Integer fontSize, String color, Double width, Double height, Boolean isStatic) {
|
||||
getUI().ifPresent(ui -> ui.access(() -> {
|
||||
propertiesPanelProfile.removeAll();
|
||||
|
||||
@@ -939,7 +1026,7 @@ public class EditProfileView extends HorizontalLayout {
|
||||
.set("font-size", "var(--lumo-font-size-l)");
|
||||
|
||||
// Element Typ Anzeige
|
||||
Span typeLabel = new Span("Typ: " + elementType);
|
||||
Span typeLabel = new Span("Typ: " + elementType + (Boolean.TRUE.equals(isStatic) ? " (Stammdaten)" : ""));
|
||||
typeLabel.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
||||
|
||||
propertiesPanelProfile.add(header, typeLabel);
|
||||
@@ -949,12 +1036,18 @@ public class EditProfileView extends HorizontalLayout {
|
||||
TextField textField = new TextField("Text");
|
||||
textField.setValue(text != null ? text : "");
|
||||
textField.setWidthFull();
|
||||
// Statische Elemente können nicht editiert werden
|
||||
if (Boolean.TRUE.equals(isStatic)) {
|
||||
textField.setReadOnly(true);
|
||||
textField.setHelperText("Text kommt aus Ihren Stammdaten");
|
||||
} else {
|
||||
textField.addValueChangeListener(e -> {
|
||||
getElement().executeJs(
|
||||
"if (window.updateProfileElementText) { window.updateProfileElementText('" + elementId
|
||||
+ "', $0); }",
|
||||
e.getValue());
|
||||
});
|
||||
}
|
||||
propertiesPanelProfile.add(textField);
|
||||
}
|
||||
|
||||
@@ -1113,21 +1206,16 @@ public class EditProfileView extends HorizontalLayout {
|
||||
if (optionalTemplate.isPresent()) {
|
||||
String templateData = optionalTemplate.get().getTemplateData();
|
||||
if (templateData != null && !templateData.isEmpty()) {
|
||||
// Escape single quotes and newlines for JavaScript
|
||||
String escapedData = templateData
|
||||
.replace("\\", "\\\\")
|
||||
.replace("'", "\\'")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "");
|
||||
System.out.println("Loading template data: " + templateData.substring(0, Math.min(100, templateData.length())) + "...");
|
||||
getElement().executeJs(
|
||||
"setTimeout(function() { " +
|
||||
" if (window.loadProfileTemplate && document.getElementById('invoice-canvas-container-profile')) { " +
|
||||
" console.log('Loading template into canvas...'); " +
|
||||
" window.loadProfileTemplate('" + escapedData + "'); " +
|
||||
" window.loadProfileTemplate($0); " +
|
||||
" } else { " +
|
||||
" console.error('loadProfileTemplate or canvas not available'); " +
|
||||
" } " +
|
||||
"}, 100);");
|
||||
"}, 300);", templateData);
|
||||
}
|
||||
} else {
|
||||
System.out.println("No template found for user: " + currentUser.getId());
|
||||
|
||||
@@ -262,7 +262,7 @@ public class CustomerInvoiceService {
|
||||
htmlBuilder.append("@page { size: A4; margin: 0; }");
|
||||
htmlBuilder.append("body { margin: 0; padding: 0; width: 210mm; height: 297mm; position: relative; font-family: Arial, sans-serif; }");
|
||||
htmlBuilder.append(".element { position: absolute; }");
|
||||
htmlBuilder.append(".text { white-space: pre-wrap; word-wrap: break-word; }");
|
||||
htmlBuilder.append(".text { white-space: pre; }");
|
||||
htmlBuilder.append(".line { border-top: 1px solid #333; }");
|
||||
htmlBuilder.append(".image { max-width: 100%; max-height: 100%; }");
|
||||
htmlBuilder.append("</style>");
|
||||
|
||||
Reference in New Issue
Block a user