Erweiterungen

This commit is contained in:
2026-01-23 10:32:44 +01:00
parent 4e5bd439bd
commit e5e1af70fb
3 changed files with 140 additions and 76 deletions

View File

@@ -183,7 +183,7 @@ public class MainView extends VerticalLayout {
String textClass;
if (email.isConfirmed()) {
statusText = "Bestätigt";
statusText = "Bearbeitet";
bgClass = LumoUtility.Background.SUCCESS_10;
textClass = LumoUtility.TextColor.SUCCESS;
} else if (email.isProcessed()) {

View File

@@ -9,6 +9,7 @@ import com.vaadin.flow.component.dnd.DragSource;
import com.vaadin.flow.component.dnd.DropEffect;
import com.vaadin.flow.component.dnd.DropTarget;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Pre;
@@ -47,10 +48,12 @@ public class OrderDetailDialog extends Dialog {
private final Consumer<OrderEmail> onProcessed;
private final Consumer<OrderEmail> onDelete;
private final String googleMapsApiKey;
private final boolean readOnly;
private TextArea offerTextArea;
private TextArea confirmationTextArea;
private EmailField recipientField;
private HorizontalLayout contentLayout;
private Binder<OrderSummary> binder;
public OrderDetailDialog(
OrderEmail orderEmail,
@@ -66,8 +69,10 @@ public class OrderDetailDialog extends Dialog {
this.googleMapsApiKey = googleMapsApiKey;
this.onProcessed = onProcessed;
this.onDelete = onDelete;
this.readOnly = orderEmail.isProcessed();
setHeaderTitle("Email-Details: " + orderEmail.getSubject());
String titleSuffix = readOnly ? " (Bereits bearbeitet)" : "";
setHeaderTitle("Email-Details: " + orderEmail.getSubject() + titleSuffix);
setCloseOnOutsideClick(false);
setWidth("80vw");
setHeight("85vh");
@@ -258,7 +263,9 @@ public class OrderDetailDialog extends Dialog {
typeComboBox.setItemLabelGenerator(EmailType::getDisplayName);
typeComboBox.setValue(orderEmail.getType());
typeComboBox.setWidth("200px");
typeComboBox.setReadOnly(readOnly);
if (!readOnly) {
typeComboBox.addValueChangeListener(event -> {
if (event.getValue() != null && event.getValue() != event.getOldValue()) {
EmailType newType = event.getValue();
@@ -277,6 +284,7 @@ public class OrderDetailDialog extends Dialog {
3000, Notification.Position.BOTTOM_START);
}
});
}
layout.add(label, typeComboBox);
return layout;
@@ -300,10 +308,24 @@ public class OrderDetailDialog extends Dialog {
// Geschlecht ComboBox direkt unter Anforderer
ComboBox<Gender> genderComboBox = new ComboBox<>();
genderComboBox.setItems(Gender.MALE, Gender.FEMALE, Gender.UNKNOWN);
genderComboBox.setItems(Gender.MALE, Gender.FEMALE);
genderComboBox.setItemLabelGenerator(Gender::getDisplayName);
genderComboBox.setValue(summary.getRequesterGender() != null ? summary.getRequesterGender() : Gender.UNKNOWN);
genderComboBox.setWidth("150px");
genderComboBox.setReadOnly(readOnly);
// Binder für Validierung
binder = new Binder<>(OrderSummary.class);
binder.forField(genderComboBox)
.asRequired("Bitte wählen Sie eine Anrede")
.bind(OrderSummary::getRequesterGender, OrderSummary::setRequesterGender);
binder.setBean(summary);
// Initiale Validierung auslösen, um roten Rahmen zu zeigen wenn keine Auswahl
if (!readOnly && (summary.getRequesterGender() == null || summary.getRequesterGender() == Gender.UNKNOWN)) {
genderComboBox.setInvalid(true);
}
if (!readOnly) {
genderComboBox.addValueChangeListener(event -> {
if (event.getValue() != null) {
summary.setRequesterGender(event.getValue());
@@ -311,8 +333,11 @@ public class OrderDetailDialog extends Dialog {
onProcessed.accept(orderEmail);
// Aktualisiere die Mail-Templates
updateMailTemplates();
// Validierung aktualisieren
genderComboBox.setInvalid(false);
}
});
}
form.addFormItem(genderComboBox, "Anrede");
}
@@ -365,7 +390,9 @@ public class OrderDetailDialog extends Dialog {
cardWrapper.setAlignItems(FlexComponent.Alignment.CENTER);
cardWrapper.setSpacing(true);
cardWrapper.setWidthFull();
if (!readOnly) {
cardWrapper.getStyle().set("cursor", "grab");
}
// Numbered badge (circular, light blue)
Span numberBadge = new Span(String.valueOf(i + 1));
@@ -438,7 +465,12 @@ public class OrderDetailDialog extends Dialog {
stationInfo.add(address);
}
// Edit button in top-right corner
stationContent.add(actionBadge, stationInfo);
stationContent.setFlexGrow(1, stationInfo);
stationCard.add(stationContent);
// Edit button only in edit mode
if (!readOnly) {
Button editButton = new Button(new Icon(VaadinIcon.EDIT));
editButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE, ButtonVariant.LUMO_SMALL);
editButton.getElement().setAttribute("title", "Station bearbeiten");
@@ -447,14 +479,13 @@ public class OrderDetailDialog extends Dialog {
.set("top", "var(--lumo-space-xs)")
.set("right", "var(--lumo-space-xs)");
editButton.addClickListener(e -> openStationEditDialog(station, section));
stationContent.add(actionBadge, stationInfo);
stationContent.setFlexGrow(1, stationInfo);
stationCard.add(stationContent, editButton);
stationCard.add(editButton);
}
cardWrapper.add(numberBadge, stationCard);
// Drag and Drop setup
// Drag and Drop setup only in edit mode
if (!readOnly) {
DragSource<HorizontalLayout> dragSource = DragSource.create(cardWrapper);
dragSource.setDragData(index);
dragSource.addDragStartListener(e -> cardWrapper.getStyle().set("opacity", "0.5"));
@@ -473,6 +504,7 @@ public class OrderDetailDialog extends Dialog {
}
});
});
}
section.add(cardWrapper);
}
@@ -596,7 +628,7 @@ public class OrderDetailDialog extends Dialog {
section.setSpacing(false);
section.setWidthFull();
H4 title = new H4("Angebotstext");
H4 title = new H4(readOnly ? "Gesendetes Angebot" : "Angebotstext");
title.addClassNames(LumoUtility.Margin.Bottom.SMALL, LumoUtility.Margin.Top.SMALL);
section.add(title);
@@ -604,12 +636,18 @@ public class OrderDetailDialog extends Dialog {
offerTextArea.setWidthFull();
offerTextArea.setMinHeight("120px");
offerTextArea.setValue(smtpEmailService.getDefaultOfferTemplate(summary));
offerTextArea.setReadOnly(readOnly);
if (!readOnly) {
offerTextArea.setHelperText("Bearbeiten Sie den Angebotstext vor dem Versenden");
}
recipientField = new EmailField("Empfänger");
recipientField.setWidthFull();
recipientField.setValue(orderEmail.getFromAddress());
recipientField.setReadOnly(readOnly);
if (!readOnly) {
recipientField.setHelperText("Email-Adresse des Empfängers");
}
section.add(offerTextArea, recipientField);
return section;
@@ -621,7 +659,7 @@ public class OrderDetailDialog extends Dialog {
section.setSpacing(false);
section.setWidthFull();
H4 title = new H4("Bestätigungstext");
H4 title = new H4(readOnly ? "Gesendete Bestätigung" : "Bestätigungstext");
title.addClassNames(LumoUtility.Margin.Bottom.SMALL, LumoUtility.Margin.Top.SMALL);
section.add(title);
@@ -629,38 +667,49 @@ public class OrderDetailDialog extends Dialog {
confirmationTextArea.setWidthFull();
confirmationTextArea.setMinHeight("120px");
confirmationTextArea.setValue(smtpEmailService.getDefaultConfirmationTemplate(summary));
confirmationTextArea.setReadOnly(readOnly);
if (!readOnly) {
confirmationTextArea.setHelperText("Bearbeiten Sie den Bestätigungstext vor dem Versenden");
}
recipientField = new EmailField("Empfänger");
recipientField.setWidthFull();
recipientField.setValue(orderEmail.getFromAddress());
recipientField.setReadOnly(readOnly);
if (!readOnly) {
recipientField.setHelperText("Email-Adresse des Empfängers");
}
section.add(confirmationTextArea, recipientField);
return section;
}
private void createFooter() {
Button cancelButton = new Button("Abbrechen", e -> close());
Button closeButton = new Button("Schließen", e -> close());
Button deleteButton = new Button("Löschen", e -> confirmDelete());
deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
Button reprocessButton = new Button("Neu analysieren", e -> reprocessEmail());
reprocessButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
// Spacer to push action buttons to the right
Div spacer = new Div();
spacer.getStyle().set("flex-grow", "1");
if (readOnly) {
// Read-only mode: only show close and delete buttons
getFooter().add(deleteButton, spacer, closeButton);
} else {
Button reprocessButton = new Button("Neu analysieren", e -> reprocessEmail());
reprocessButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
if (orderEmail.getType() == EmailType.QUOTE_REQUEST) {
Button sendOfferButton = new Button("Angebot senden", e -> sendOffer());
sendOfferButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
getFooter().add(deleteButton, spacer, cancelButton, reprocessButton, sendOfferButton);
getFooter().add(deleteButton, spacer, closeButton, reprocessButton, sendOfferButton);
} else {
Button acceptButton = new Button("Auftrag annehmen", e -> acceptOrder());
acceptButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
getFooter().add(deleteButton, spacer, cancelButton, reprocessButton, acceptButton);
getFooter().add(deleteButton, spacer, closeButton, reprocessButton, acceptButton);
}
}
}
@@ -681,6 +730,12 @@ public class OrderDetailDialog extends Dialog {
}
private void acceptOrder() {
// Validierung prüfen
if (binder != null && !binder.isValid()) {
Notification.show("Bitte wählen Sie eine Anrede aus", 3000, Notification.Position.MIDDLE);
return;
}
try {
String confirmationText = confirmationTextArea != null ? confirmationTextArea.getValue() : null;
String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress();
@@ -699,6 +754,12 @@ public class OrderDetailDialog extends Dialog {
}
private void sendOffer() {
// Validierung prüfen
if (binder != null && !binder.isValid()) {
Notification.show("Bitte wählen Sie eine Anrede aus", 3000, Notification.Position.MIDDLE);
return;
}
try {
String offerText = offerTextArea != null ? offerTextArea.getValue() : null;
String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress();

View File

@@ -43,7 +43,10 @@ public class SuccessDialog extends Dialog {
checklist.setWidth("100%");
checklist.add(createChecklistItem("Bestätigungsmail versendet", true));
if (isOrder) {
checklist.add(createChecklistItem("Auftrag in Votian angelegt", true));
}
content.add(checklist);