Erweiterungen
This commit is contained in:
Binary file not shown.
@@ -89,7 +89,8 @@ window.initProfileInvoiceGenerator = function() {
|
||||
el.color || '#333333',
|
||||
el.width || 100,
|
||||
el.height || 30,
|
||||
el.isStatic || false
|
||||
el.isStatic || false,
|
||||
el.variable || null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -102,8 +103,10 @@ window.initProfileInvoiceGenerator = function() {
|
||||
|
||||
// Draw function
|
||||
function draw() {
|
||||
console.log('draw() called, elements:', elements.length);
|
||||
var w = canvas.width;
|
||||
var h = canvas.height;
|
||||
console.log('Canvas size:', w, 'x', h);
|
||||
|
||||
// Clear background
|
||||
ctx.fillStyle = '#e8e8e8';
|
||||
@@ -141,7 +144,9 @@ window.initProfileInvoiceGenerator = function() {
|
||||
}
|
||||
|
||||
// Draw elements
|
||||
console.log('Drawing', elements.length, 'elements');
|
||||
elements.forEach(function(el) {
|
||||
console.log('Drawing element:', el.id, 'at', el.x, el.y, 'text:', el.text);
|
||||
drawElement(el);
|
||||
});
|
||||
|
||||
@@ -236,9 +241,6 @@ window.initProfileInvoiceGenerator = function() {
|
||||
}
|
||||
} else {
|
||||
// Text elements
|
||||
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||
ctx.fillStyle = el.color || '#333333';
|
||||
|
||||
var lines = (el.text || '').split('\n');
|
||||
var lineHeight = fontSize * 1.2;
|
||||
var totalTextHeight = lines.length * lineHeight;
|
||||
@@ -249,11 +251,17 @@ window.initProfileInvoiceGenerator = function() {
|
||||
// Draw background highlight for static elements
|
||||
if (el.isStatic) {
|
||||
var textWidth = ctx.measureText(el.text || '').width + (10 * zoomFactor);
|
||||
ctx.fillStyle = 'rgba(25, 118, 210, 0.1)'; // Light blue background
|
||||
// Different background colors: green for customer, blue for masterdata
|
||||
if (el.isCustomer) {
|
||||
ctx.fillStyle = 'rgba(46, 204, 113, 0.15)'; // Light green for customer
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(25, 118, 210, 0.1)'; // Light blue for masterdata
|
||||
}
|
||||
ctx.fillRect(x - (3 * zoomFactor), y - (2 * zoomFactor), Math.max(w, textWidth), h);
|
||||
}
|
||||
|
||||
ctx.fillStyle = el.color || '#333333';
|
||||
// Always use black text for static elements, otherwise use element color
|
||||
ctx.fillStyle = el.isStatic ? '#000000' : (el.color || '#333333');
|
||||
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
@@ -612,7 +620,7 @@ window.initProfileInvoiceGenerator = function() {
|
||||
});
|
||||
|
||||
// Add element function
|
||||
window.addProfileElement = function(type, label, dropX, dropY, isStatic, staticText) {
|
||||
window.addProfileElement = function(type, label, dropX, dropY, isStatic, staticText, variable, isCustomer) {
|
||||
elementCounter++;
|
||||
var id = 'element-' + elementCounter;
|
||||
|
||||
@@ -633,7 +641,9 @@ window.initProfileInvoiceGenerator = function() {
|
||||
height: 30,
|
||||
fontSize: 14,
|
||||
color: '#333333',
|
||||
isStatic: isStatic || false
|
||||
isStatic: isStatic || false,
|
||||
variable: variable || null,
|
||||
isCustomer: isCustomer || false
|
||||
};
|
||||
|
||||
// Helper function to calculate height based on font size and lines
|
||||
@@ -642,10 +652,10 @@ window.initProfileInvoiceGenerator = function() {
|
||||
return Math.round(lineCount * lineHeight + 6);
|
||||
}
|
||||
|
||||
// Handle static elements (user data)
|
||||
// Handle static elements (user data or customer data)
|
||||
if (isStatic && staticText) {
|
||||
el.text = staticText;
|
||||
el.color = '#1976d2'; // Blue color for static elements
|
||||
el.color = '#000000'; // Black text for static elements
|
||||
el.fontStyle = 'bold';
|
||||
el.height = calculateHeight(el.fontSize, 1);
|
||||
} else {
|
||||
@@ -843,10 +853,22 @@ window.initProfileInvoiceGenerator = function() {
|
||||
saveState();
|
||||
// Convert pixel values to percentages for storage
|
||||
var elementsWithPercent = elements.map(function(el) {
|
||||
// For static elements with variables, handle text storage differently:
|
||||
// - masterdata variables: text is resolved from user data, don't store
|
||||
// - customer variables: store the placeholder text from canvas
|
||||
var textToStore = el.text;
|
||||
if (el.isStatic && el.variable) {
|
||||
if (el.variable.startsWith('masterdata.')) {
|
||||
// Don't store the text for masterdata variables - will be resolved from user data
|
||||
textToStore = null;
|
||||
}
|
||||
// For customer variables, keep the text (placeholder) as is
|
||||
}
|
||||
|
||||
return {
|
||||
id: el.id,
|
||||
type: el.type,
|
||||
text: el.text,
|
||||
text: textToStore,
|
||||
xPercent: toPercentX(el.x),
|
||||
yPercent: toPercentY(el.y),
|
||||
widthPercent: toPercentX(el.width),
|
||||
@@ -855,6 +877,8 @@ window.initProfileInvoiceGenerator = function() {
|
||||
fontStyle: el.fontStyle,
|
||||
color: el.color,
|
||||
isStatic: el.isStatic,
|
||||
isCustomer: el.isCustomer,
|
||||
variable: el.variable,
|
||||
imageData: el.imageData
|
||||
};
|
||||
});
|
||||
@@ -899,27 +923,37 @@ window.initProfileInvoiceGenerator = function() {
|
||||
var templateLabel = e.dataTransfer.getData('template-label');
|
||||
var isStatic = e.dataTransfer.getData('is-static') === 'true';
|
||||
var staticText = e.dataTransfer.getData('static-text');
|
||||
var variable = e.dataTransfer.getData('variable');
|
||||
var isCustomer = e.dataTransfer.getData('is-customer') === 'true';
|
||||
|
||||
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, isStatic, staticText);
|
||||
window.addProfileElement(templateType, templateLabel, x, y, isStatic, staticText, variable, isCustomer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load template function
|
||||
// Load template function - defined INSIDE initProfileInvoiceGenerator to access closure variables
|
||||
window.loadProfileTemplate = function(templateData) {
|
||||
try {
|
||||
var data = JSON.parse(templateData);
|
||||
console.log('loadProfileTemplate called with data:', JSON.stringify(templateData).substring(0, 200));
|
||||
var data = (typeof templateData === 'string') ? JSON.parse(templateData) : templateData;
|
||||
if (data.elements && Array.isArray(data.elements)) {
|
||||
// Clear existing elements
|
||||
elements = [];
|
||||
console.log('Loading ' + data.elements.length + ' elements');
|
||||
console.log('Current elements array before clear:', elements.length);
|
||||
// Clear existing elements - use length = 0 to keep the same array reference
|
||||
elements.length = 0;
|
||||
selectedElement = null;
|
||||
console.log('Elements array after clear:', elements.length);
|
||||
|
||||
// Master data mapping - these should be filled from user data
|
||||
var masterdata = window.masterdataValues || {};
|
||||
|
||||
// Load new elements
|
||||
data.elements.forEach(function(el) {
|
||||
console.log('Loading element:', el.id, 'type:', el.type, 'variable:', el.variable);
|
||||
// Ensure element has an ID
|
||||
if (!el.id) {
|
||||
elementCounter++;
|
||||
@@ -940,7 +974,69 @@ window.initProfileInvoiceGenerator = function() {
|
||||
el.height = fromPercentY(el.heightPercent);
|
||||
}
|
||||
|
||||
// Handle elements with variables
|
||||
if (el.variable) {
|
||||
el.isStatic = true;
|
||||
if (el.variable.startsWith('customer.')) {
|
||||
el.isCustomer = true;
|
||||
// Customer variables - use saved text if available, otherwise use placeholder
|
||||
if (!el.text || el.text.trim() === '') {
|
||||
el.text = '[' + el.variable.replace('customer.', '') + ']';
|
||||
}
|
||||
// If text is already set (from saved template), keep it
|
||||
} else if (el.variable.startsWith('masterdata.')) {
|
||||
// Masterdata variables - use actual value from masterdata or placeholder
|
||||
var value = masterdata[el.variable];
|
||||
if (value && value.trim() !== '') {
|
||||
el.text = value;
|
||||
} else {
|
||||
el.text = '[' + el.variable.replace('masterdata.', '') + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Legacy: If text contains a variable placeholder {{variable}}|Display Text, extract it
|
||||
else if (el.text && el.text.startsWith('{{')) {
|
||||
var pipeIndex = el.text.indexOf('|');
|
||||
if (pipeIndex > -1) {
|
||||
// Format: {{variable}}|Display Text
|
||||
var placeholderPart = el.text.substring(0, pipeIndex);
|
||||
var displayText = el.text.substring(pipeIndex + 1);
|
||||
if (placeholderPart.startsWith('{{') && placeholderPart.endsWith('}}')) {
|
||||
var varName = placeholderPart.substring(2, placeholderPart.length - 2);
|
||||
el.variable = varName;
|
||||
if (varName.startsWith('masterdata.')) {
|
||||
el.isStatic = true;
|
||||
// Use masterdata value or fallback to display text
|
||||
var value = masterdata[varName];
|
||||
el.text = (value && value.trim() !== '') ? value : displayText;
|
||||
} else if (varName.startsWith('customer.')) {
|
||||
el.isStatic = true;
|
||||
el.isCustomer = true;
|
||||
// Use saved text if available, otherwise use placeholder
|
||||
if (!el.text || el.text.trim() === '' || el.text.startsWith('{{')) {
|
||||
el.text = '[' + varName.replace('customer.', '') + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (el.text.endsWith('}}')) {
|
||||
// Legacy format: {{variable}} without display text
|
||||
var varName = el.text.substring(2, el.text.length - 2);
|
||||
el.variable = varName;
|
||||
if (varName.startsWith('masterdata.')) {
|
||||
el.isStatic = true;
|
||||
var value = masterdata[varName];
|
||||
el.text = (value && value.trim() !== '') ? value : '[' + varName.replace('masterdata.', '') + ']';
|
||||
} else if (varName.startsWith('customer.')) {
|
||||
el.isStatic = true;
|
||||
el.isCustomer = true;
|
||||
el.text = '[' + varName.replace('customer.', '') + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Element processed:', el.id, 'x:', el.x, 'y:', el.y, 'text:', el.text);
|
||||
elements.push(el);
|
||||
console.log('Element pushed, elements count now:', elements.length);
|
||||
});
|
||||
|
||||
// Update counter to be higher than any loaded element
|
||||
@@ -956,9 +1052,14 @@ window.initProfileInvoiceGenerator = function() {
|
||||
// Save to global state
|
||||
saveState();
|
||||
|
||||
console.log('Calling draw(), elements count:', elements.length);
|
||||
console.log('Canvas dimensions:', canvas.width, 'x', canvas.height);
|
||||
draw();
|
||||
console.log('draw() completed');
|
||||
notifyElementDeselected();
|
||||
console.log('Template loaded with ' + elements.length + ' elements');
|
||||
} else {
|
||||
console.log('No elements array found in template data');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error loading template:', e);
|
||||
|
||||
@@ -100,7 +100,7 @@ public class AuthenticatedStartView extends VerticalLayout {
|
||||
featuresGrid.getStyle().set("display", "flex");
|
||||
featuresGrid.getStyle().set("flex-wrap", "wrap");
|
||||
featuresGrid.getStyle().set("justify-content", "center");
|
||||
featuresGrid.getStyle().set("align-items", "flex-start");
|
||||
featuresGrid.getStyle().set("align-items", "stretch");
|
||||
featuresGrid.getStyle().set("gap", "var(--lumo-space-l)");
|
||||
featuresGrid.getStyle().set("width", "100%");
|
||||
|
||||
@@ -128,6 +128,7 @@ public class AuthenticatedStartView extends VerticalLayout {
|
||||
card.setWidth("300px");
|
||||
card.setMinWidth("300px");
|
||||
card.setMaxWidth("300px");
|
||||
card.setHeight("100%");
|
||||
card.getStyle().set("flex-grow", "0");
|
||||
card.getStyle().set("flex-shrink", "0");
|
||||
|
||||
|
||||
@@ -800,7 +800,7 @@ public class EditProfileView extends HorizontalLayout {
|
||||
} else {
|
||||
templateData = result.toString();
|
||||
}
|
||||
byte[] pdfBytes = customerInvoiceService.generatePdfFromCanvasTemplate(templateData);
|
||||
byte[] pdfBytes = customerInvoiceService.generatePdfFromCanvasTemplate(templateData, currentUser);
|
||||
showPdfInDialog(pdfBytes);
|
||||
} catch (Exception ex) {
|
||||
Notification.show("Fehler beim Generieren der Vorschau: " + ex.getMessage(), 3000, Notification.Position.BOTTOM_CENTER);
|
||||
@@ -868,34 +868,49 @@ public class EditProfileView extends HorizontalLayout {
|
||||
panel.setSpacing(true);
|
||||
panel.setHeightFull();
|
||||
|
||||
// Bereich 1: Rechnungselemente (Stammdaten)
|
||||
Span invoiceHeader = new Span("Rechnungselemente");
|
||||
// Bereich 1: Meine Stammdaten (Variablen)
|
||||
Span invoiceHeader = new Span("Meine Stammdaten (Variablen)");
|
||||
invoiceHeader.getStyle()
|
||||
.set("font-weight", "bold")
|
||||
.set("font-size", "var(--lumo-font-size-m)")
|
||||
.set("margin-top", "var(--lumo-space-s)");
|
||||
|
||||
// Stammdaten des Benutzers als unveränderbare Elemente
|
||||
// Stammdaten als Variablen - mit tatsächlichen Werten aus currentUser
|
||||
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",
|
||||
|
||||
Div senderCompany = createVariableTemplate("Firma", VaadinIcon.OFFICE, "masterdata.company_name",
|
||||
company.isEmpty() ? "Ihre Firma" : company);
|
||||
Div senderName = createStaticTemplate("Name", VaadinIcon.USER, "sender_name",
|
||||
Div senderName = createVariableTemplate("Name", VaadinIcon.USER, "masterdata.contact_name",
|
||||
fullName.trim().isEmpty() ? "Ihr Name" : fullName.trim());
|
||||
Div senderAddress = createStaticTemplate("Adresse", VaadinIcon.MAP_MARKER, "sender_address",
|
||||
Div senderAddress = createVariableTemplate("Straße", VaadinIcon.MAP_MARKER, "masterdata.street",
|
||||
street.trim().isEmpty() ? "Ihre Straße" : street.trim());
|
||||
Div senderCity = createStaticTemplate("Ort", VaadinIcon.BUILDING, "sender_city",
|
||||
Div senderCity = createVariableTemplate("Ort", VaadinIcon.BUILDING, "masterdata.city",
|
||||
city.trim().isEmpty() ? "PLZ Ort" : city.trim());
|
||||
Div senderEmail = createStaticTemplate("E-Mail", VaadinIcon.ENVELOPE, "sender_email",
|
||||
Div senderEmail = createVariableTemplate("E-Mail", VaadinIcon.ENVELOPE, "masterdata.email",
|
||||
email.isEmpty() ? "ihre@email.de" : email);
|
||||
Div senderPhone = createStaticTemplate("Telefon", VaadinIcon.PHONE, "sender_phone",
|
||||
Div senderPhone = createVariableTemplate("Telefon", VaadinIcon.PHONE, "masterdata.phone",
|
||||
phone.isEmpty() ? "Ihre Telefonnummer" : phone);
|
||||
|
||||
// Bereich 2: Kundendaten (Variablen)
|
||||
Span customerHeader = new Span("Kundendaten (Variablen)");
|
||||
customerHeader.getStyle()
|
||||
.set("font-weight", "bold")
|
||||
.set("font-size", "var(--lumo-font-size-m)")
|
||||
.set("margin-top", "var(--lumo-space-m)");
|
||||
|
||||
// Kundendaten als Variablen (grün hinterlegt)
|
||||
Div customerCompany = createCustomerVariableTemplate("Kunde Firma", VaadinIcon.OFFICE, "customer.company_name", "Kundenfirma GmbH");
|
||||
Div customerName = createCustomerVariableTemplate("Kunde Name", VaadinIcon.USER, "customer.contact_name", "Erika Mustermann");
|
||||
Div customerAddress = createCustomerVariableTemplate("Kunde Straße", VaadinIcon.MAP_MARKER, "customer.street", "Kundenstraße 456");
|
||||
Div customerCity = createCustomerVariableTemplate("Kunde Ort", VaadinIcon.BUILDING, "customer.city", "54321 Kundenstadt");
|
||||
Div customerEmail = createCustomerVariableTemplate("Kunde E-Mail", VaadinIcon.ENVELOPE, "customer.email", "kunde@beispiel.de");
|
||||
Div customerPhone = createCustomerVariableTemplate("Kunde Telefon", VaadinIcon.PHONE, "customer.phone", "0987 654321");
|
||||
|
||||
// Bereich 2: Freie Elemente
|
||||
Span freeHeader = new Span("Freie Elemente");
|
||||
freeHeader.getStyle()
|
||||
@@ -916,6 +931,8 @@ public class EditProfileView extends HorizontalLayout {
|
||||
panel.add(
|
||||
invoiceHeader,
|
||||
senderCompany, senderName, senderAddress, senderCity, senderEmail, senderPhone,
|
||||
customerHeader,
|
||||
customerCompany, customerName, customerAddress, customerCity, customerEmail, customerPhone,
|
||||
freeHeader,
|
||||
textBlock, headerBlock, dateBlock, customerBlock, companyBlock, amountBlock, lineBlock, imageBlock
|
||||
);
|
||||
@@ -923,37 +940,87 @@ public class EditProfileView extends HorizontalLayout {
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Div createStaticTemplate(String label, VaadinIcon icon, String type, String defaultText) {
|
||||
private Div createVariableTemplate(String label, VaadinIcon icon, String variable, 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("background-color", "rgba(25, 118, 210, 0.1)")
|
||||
.set("border", "1px solid rgba(25, 118, 210, 0.3)")
|
||||
.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)");
|
||||
.set("font-size", "var(--lumo-font-size-s)")
|
||||
.set("color", "#1976d2");
|
||||
|
||||
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-type", "variable");
|
||||
template.getElement().setAttribute("data-template-label", label);
|
||||
template.getElement().setAttribute("data-variable", variable);
|
||||
template.getElement().setAttribute("data-static-text", defaultText);
|
||||
template.getElement().setAttribute("data-is-customer", "false");
|
||||
|
||||
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('variable', this.getAttribute('data-variable'));" +
|
||||
" e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));" +
|
||||
" e.dataTransfer.setData('is-static', 'true');" +
|
||||
" e.dataTransfer.setData('is-customer', this.getAttribute('data-is-customer'));" +
|
||||
" this.style.opacity = '0.5';" +
|
||||
"});" +
|
||||
"this.addEventListener('dragend', function(e) {" +
|
||||
" this.style.opacity = '1';" +
|
||||
"});");
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private Div createCustomerVariableTemplate(String label, VaadinIcon icon, String variable, 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", "rgba(46, 204, 113, 0.15)") // Friendly green
|
||||
.set("border", "1px solid rgba(46, 204, 113, 0.4)")
|
||||
.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)")
|
||||
.set("color", "#27ae60"); // Dark green text
|
||||
|
||||
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", "variable");
|
||||
template.getElement().setAttribute("data-template-label", label);
|
||||
template.getElement().setAttribute("data-variable", variable);
|
||||
template.getElement().setAttribute("data-static-text", defaultText);
|
||||
template.getElement().setAttribute("data-is-customer", "true");
|
||||
|
||||
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('variable', this.getAttribute('data-variable'));" +
|
||||
" e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));" +
|
||||
" e.dataTransfer.setData('is-static', 'true');" +
|
||||
" e.dataTransfer.setData('is-customer', this.getAttribute('data-is-customer'));" +
|
||||
" this.style.opacity = '0.5';" +
|
||||
"});" +
|
||||
"this.addEventListener('dragend', function(e) {" +
|
||||
@@ -1024,7 +1091,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, Boolean isStatic) {
|
||||
Integer fontSize, String color, Double width, Double height, Boolean isStatic, String variable) {
|
||||
getUI().ifPresent(ui -> ui.access(() -> {
|
||||
propertiesPanelProfile.removeAll();
|
||||
|
||||
@@ -1034,10 +1101,30 @@ public class EditProfileView extends HorizontalLayout {
|
||||
.set("font-size", "var(--lumo-font-size-l)");
|
||||
|
||||
// Element Typ Anzeige
|
||||
Span typeLabel = new Span("Typ: " + elementType + (Boolean.TRUE.equals(isStatic) ? " (Stammdaten)" : ""));
|
||||
String typeDisplay = "Typ: " + elementType;
|
||||
if (variable != null && !variable.isEmpty()) {
|
||||
typeDisplay += " (Variable)";
|
||||
} else if (Boolean.TRUE.equals(isStatic)) {
|
||||
typeDisplay += " (Stammdaten)";
|
||||
}
|
||||
Span typeLabel = new Span(typeDisplay);
|
||||
typeLabel.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
||||
|
||||
|
||||
propertiesPanelProfile.add(header, typeLabel);
|
||||
|
||||
// Variable anzeigen wenn vorhanden
|
||||
if (variable != null && !variable.isEmpty()) {
|
||||
TextField variableField = new TextField("Variable");
|
||||
variableField.setValue(variable);
|
||||
variableField.setReadOnly(true);
|
||||
variableField.setWidthFull();
|
||||
|
||||
// Beschreibung basierend auf der Variable
|
||||
String description = getVariableDescription(variable);
|
||||
variableField.setHelperText(description);
|
||||
|
||||
propertiesPanelProfile.add(variableField);
|
||||
}
|
||||
|
||||
// Für Bildelemente: Upload-Button anzeigen
|
||||
if ("image".equals(elementType)) {
|
||||
@@ -1250,15 +1337,38 @@ public class EditProfileView extends HorizontalLayout {
|
||||
String templateData = optionalTemplate.get().getTemplateData();
|
||||
if (templateData != null && !templateData.isEmpty()) {
|
||||
System.out.println("Loading template data: " + templateData.substring(0, Math.min(100, templateData.length())) + "...");
|
||||
// Escape the JSON string for JavaScript
|
||||
String escapedJson = templateData
|
||||
.replace("\\", "\\\\")
|
||||
.replace("'", "\\'")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r");
|
||||
// Build masterdata values JSON
|
||||
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());
|
||||
String masterdataJson = "{" +
|
||||
"'masterdata.company_name': '" + company.replace("'", "\\'") + "'," +
|
||||
"'masterdata.contact_name': '" + fullName.replace("'", "\\'") + "'," +
|
||||
"'masterdata.street': '" + street.replace("'", "\\'") + "'," +
|
||||
"'masterdata.city': '" + city.replace("'", "\\'") + "'," +
|
||||
"'masterdata.email': '" + email.replace("'", "\\'") + "'," +
|
||||
"'masterdata.phone': '" + phone.replace("'", "\\'") + "'" +
|
||||
"}";
|
||||
getElement().executeJs(
|
||||
"setTimeout(function() { " +
|
||||
" if (window.loadProfileTemplate && document.getElementById('invoice-canvas-container-profile')) { " +
|
||||
" console.log('Loading template into canvas...'); " +
|
||||
" window.loadProfileTemplate($0); " +
|
||||
" window.masterdataValues = " + masterdataJson + "; " +
|
||||
" var templateData = JSON.parse('" + escapedJson + "'); " +
|
||||
" window.loadProfileTemplate(templateData); " +
|
||||
" } else { " +
|
||||
" console.error('loadProfileTemplate or canvas not available'); " +
|
||||
" } " +
|
||||
"}, 300);", templateData);
|
||||
"}, 300);");
|
||||
}
|
||||
} else {
|
||||
System.out.println("No template found for user: " + currentUser.getId());
|
||||
@@ -1269,4 +1379,25 @@ public class EditProfileView extends HorizontalLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human-readable description for a variable
|
||||
*/
|
||||
private String getVariableDescription(String variable) {
|
||||
return switch (variable) {
|
||||
case "masterdata.company_name" -> "Name Ihrer Firma";
|
||||
case "masterdata.contact_name" -> "Name des Ansprechpartners";
|
||||
case "masterdata.street" -> "Straße und Hausnummer";
|
||||
case "masterdata.city" -> "PLZ und Ort";
|
||||
case "masterdata.email" -> "E-Mail-Adresse";
|
||||
case "masterdata.phone" -> "Telefonnummer";
|
||||
case "customer.company_name" -> "Firmenname des Kunden";
|
||||
case "customer.contact_name" -> "Name des Kundenansprechpartners";
|
||||
case "customer.street" -> "Straße des Kunden";
|
||||
case "customer.city" -> "PLZ und Ort des Kunden";
|
||||
case "customer.email" -> "E-Mail des Kunden";
|
||||
case "customer.phone" -> "Telefon des Kunden";
|
||||
default -> "Variable: " + variable;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -248,6 +248,10 @@ public class CustomerInvoiceService {
|
||||
* Creates an HTML representation of the canvas elements and converts it to PDF.
|
||||
*/
|
||||
public byte[] generatePdfFromCanvasTemplate(String jsonTemplateData) throws Exception {
|
||||
return generatePdfFromCanvasTemplate(jsonTemplateData, null);
|
||||
}
|
||||
|
||||
public byte[] generatePdfFromCanvasTemplate(String jsonTemplateData, de.assecutor.votianlt.model.User user) throws Exception {
|
||||
// Parse the JSON template data
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
com.fasterxml.jackson.databind.JsonNode rootNode = mapper.readTree(jsonTemplateData);
|
||||
@@ -269,10 +273,48 @@ public class CustomerInvoiceService {
|
||||
htmlBuilder.append("</style>");
|
||||
htmlBuilder.append("</head><body>");
|
||||
|
||||
// Prepare variable substitution map
|
||||
java.util.Map<String, String> variables = new java.util.HashMap<>();
|
||||
|
||||
if (user != null) {
|
||||
// Use actual user data
|
||||
String company = user.getCompany();
|
||||
variables.put("masterdata.company_name", (company != null && !company.isEmpty()) ? company : "Ihre Firma");
|
||||
variables.put("masterdata.contact_name", safe(user.getFirstname()) + " " + safe(user.getName()));
|
||||
variables.put("masterdata.street", safe(user.getStreet()) + " " + safe(user.getHouseNumber()));
|
||||
variables.put("masterdata.city", safe(user.getZip()) + " " + safe(user.getCity()));
|
||||
variables.put("masterdata.email", safe(user.getEmail()));
|
||||
variables.put("masterdata.phone", safe(user.getPhone()));
|
||||
} else {
|
||||
// Default values for preview without user
|
||||
variables.put("masterdata.company_name", "Meine Firma GmbH");
|
||||
variables.put("masterdata.contact_name", "Max Mustermann");
|
||||
variables.put("masterdata.street", "Musterstraße 123");
|
||||
variables.put("masterdata.city", "12345 Musterstadt");
|
||||
variables.put("masterdata.email", "kontakt@firma.de");
|
||||
variables.put("masterdata.phone", "0123 456789");
|
||||
}
|
||||
|
||||
// Customer data (placeholder for now - would come from job/customer selection)
|
||||
variables.put("customer.company_name", "Kundenfirma GmbH");
|
||||
variables.put("customer.contact_name", "Erika Mustermann");
|
||||
variables.put("customer.street", "Kundenstraße 456");
|
||||
variables.put("customer.city", "54321 Kundenstadt");
|
||||
variables.put("customer.email", "kunde@beispiel.de");
|
||||
variables.put("customer.phone", "0987 654321");
|
||||
|
||||
if (elements != null && elements.isArray()) {
|
||||
for (com.fasterxml.jackson.databind.JsonNode element : elements) {
|
||||
String type = element.has("type") ? element.get("type").asText("text") : "text";
|
||||
String text = element.has("text") ? element.get("text").asText("") : "";
|
||||
String variable = element.has("variable") ? element.get("variable").asText(null) : null;
|
||||
// For static elements with variables, use the variable to get the text
|
||||
// Otherwise use the stored text
|
||||
String text;
|
||||
if (variable != null && !variable.isEmpty()) {
|
||||
text = ""; // Will be replaced by variable value below
|
||||
} else {
|
||||
text = element.has("text") ? element.get("text").asText("") : "";
|
||||
}
|
||||
|
||||
// Use percentage values if available, otherwise fall back to legacy pixel values
|
||||
double xPercent, yPercent, widthPercent, heightPercent;
|
||||
@@ -329,12 +371,36 @@ public class CustomerInvoiceService {
|
||||
htmlBuilder.append("'");
|
||||
htmlBuilder.append(">");
|
||||
|
||||
// Escape HTML special characters in text
|
||||
text = text.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("\"", """)
|
||||
.replace("'", "'");
|
||||
// Replace variables with actual values FIRST
|
||||
if (variable != null && variables.containsKey(variable)) {
|
||||
text = variables.get(variable);
|
||||
} else if (text != null && text.startsWith("{{")) {
|
||||
// Handle variable placeholder format {{variable}}|Display Text or {{variable}}
|
||||
int pipeIndex = text.indexOf('|');
|
||||
String placeholderPart;
|
||||
if (pipeIndex > -1) {
|
||||
placeholderPart = text.substring(0, pipeIndex);
|
||||
} else {
|
||||
placeholderPart = text;
|
||||
}
|
||||
if (placeholderPart.startsWith("{{") && placeholderPart.endsWith("}}")) {
|
||||
String varName = placeholderPart.substring(2, placeholderPart.length() - 2);
|
||||
if (variables.containsKey(varName)) {
|
||||
text = variables.get(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Escape HTML special characters in text AFTER variable replacement
|
||||
if (text != null) {
|
||||
text = text.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("\"", """)
|
||||
.replace("'", "'");
|
||||
} else {
|
||||
text = "";
|
||||
}
|
||||
|
||||
if ("line".equals(type)) {
|
||||
htmlBuilder.append("<hr style='margin:0;border:none;border-top:1px solid #333;height:0;width:100%;'/>");
|
||||
@@ -366,4 +432,8 @@ public class CustomerInvoiceService {
|
||||
// Generate PDF from HTML
|
||||
return generatePdfFromHtmlString(htmlBuilder.toString());
|
||||
}
|
||||
|
||||
private String safe(String value) {
|
||||
return value != null ? value : "";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user