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; String textClass;
if (email.isConfirmed()) { if (email.isConfirmed()) {
statusText = "Bestätigt"; statusText = "Bearbeitet";
bgClass = LumoUtility.Background.SUCCESS_10; bgClass = LumoUtility.Background.SUCCESS_10;
textClass = LumoUtility.TextColor.SUCCESS; textClass = LumoUtility.TextColor.SUCCESS;
} else if (email.isProcessed()) { } 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.DropEffect;
import com.vaadin.flow.component.dnd.DropTarget; import com.vaadin.flow.component.dnd.DropTarget;
import com.vaadin.flow.component.formlayout.FormLayout; 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.Div;
import com.vaadin.flow.component.html.H4; import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Pre; 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> onProcessed;
private final Consumer<OrderEmail> onDelete; private final Consumer<OrderEmail> onDelete;
private final String googleMapsApiKey; private final String googleMapsApiKey;
private final boolean readOnly;
private TextArea offerTextArea; private TextArea offerTextArea;
private TextArea confirmationTextArea; private TextArea confirmationTextArea;
private EmailField recipientField; private EmailField recipientField;
private HorizontalLayout contentLayout; private HorizontalLayout contentLayout;
private Binder<OrderSummary> binder;
public OrderDetailDialog( public OrderDetailDialog(
OrderEmail orderEmail, OrderEmail orderEmail,
@@ -66,8 +69,10 @@ public class OrderDetailDialog extends Dialog {
this.googleMapsApiKey = googleMapsApiKey; this.googleMapsApiKey = googleMapsApiKey;
this.onProcessed = onProcessed; this.onProcessed = onProcessed;
this.onDelete = onDelete; 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); setCloseOnOutsideClick(false);
setWidth("80vw"); setWidth("80vw");
setHeight("85vh"); setHeight("85vh");
@@ -258,25 +263,28 @@ public class OrderDetailDialog extends Dialog {
typeComboBox.setItemLabelGenerator(EmailType::getDisplayName); typeComboBox.setItemLabelGenerator(EmailType::getDisplayName);
typeComboBox.setValue(orderEmail.getType()); typeComboBox.setValue(orderEmail.getType());
typeComboBox.setWidth("200px"); typeComboBox.setWidth("200px");
typeComboBox.setReadOnly(readOnly);
typeComboBox.addValueChangeListener(event -> { if (!readOnly) {
if (event.getValue() != null && event.getValue() != event.getOldValue()) { typeComboBox.addValueChangeListener(event -> {
EmailType newType = event.getValue(); if (event.getValue() != null && event.getValue() != event.getOldValue()) {
orderEmail.setType(newType); EmailType newType = event.getValue();
summary.setOrderType(newType); orderEmail.setType(newType);
onProcessed.accept(orderEmail); summary.setOrderType(newType);
onProcessed.accept(orderEmail);
// Refresh the dialog content // Refresh the dialog content
remove(contentLayout); remove(contentLayout);
getFooter().removeAll(); getFooter().removeAll();
contentLayout = createContent(); contentLayout = createContent();
add(contentLayout); add(contentLayout);
createFooter(); createFooter();
Notification.show("Klassifizierung geändert zu: " + newType.getDisplayName(), Notification.show("Klassifizierung geändert zu: " + newType.getDisplayName(),
3000, Notification.Position.BOTTOM_START); 3000, Notification.Position.BOTTOM_START);
} }
}); });
}
layout.add(label, typeComboBox); layout.add(label, typeComboBox);
return layout; return layout;
@@ -300,19 +308,36 @@ public class OrderDetailDialog extends Dialog {
// Geschlecht ComboBox direkt unter Anforderer // Geschlecht ComboBox direkt unter Anforderer
ComboBox<Gender> genderComboBox = new ComboBox<>(); ComboBox<Gender> genderComboBox = new ComboBox<>();
genderComboBox.setItems(Gender.MALE, Gender.FEMALE, Gender.UNKNOWN); genderComboBox.setItems(Gender.MALE, Gender.FEMALE);
genderComboBox.setItemLabelGenerator(Gender::getDisplayName); genderComboBox.setItemLabelGenerator(Gender::getDisplayName);
genderComboBox.setValue(summary.getRequesterGender() != null ? summary.getRequesterGender() : Gender.UNKNOWN);
genderComboBox.setWidth("150px"); genderComboBox.setWidth("150px");
genderComboBox.addValueChangeListener(event -> { genderComboBox.setReadOnly(readOnly);
if (event.getValue() != null) {
summary.setRequesterGender(event.getValue()); // Binder für Validierung
orderEmail.setSummaryJson(llmService.serializeSummary(summary)); binder = new Binder<>(OrderSummary.class);
onProcessed.accept(orderEmail); binder.forField(genderComboBox)
// Aktualisiere die Mail-Templates .asRequired("Bitte wählen Sie eine Anrede")
updateMailTemplates(); .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());
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
onProcessed.accept(orderEmail);
// Aktualisiere die Mail-Templates
updateMailTemplates();
// Validierung aktualisieren
genderComboBox.setInvalid(false);
}
});
}
form.addFormItem(genderComboBox, "Anrede"); form.addFormItem(genderComboBox, "Anrede");
} }
@@ -365,7 +390,9 @@ public class OrderDetailDialog extends Dialog {
cardWrapper.setAlignItems(FlexComponent.Alignment.CENTER); cardWrapper.setAlignItems(FlexComponent.Alignment.CENTER);
cardWrapper.setSpacing(true); cardWrapper.setSpacing(true);
cardWrapper.setWidthFull(); cardWrapper.setWidthFull();
cardWrapper.getStyle().set("cursor", "grab"); if (!readOnly) {
cardWrapper.getStyle().set("cursor", "grab");
}
// Numbered badge (circular, light blue) // Numbered badge (circular, light blue)
Span numberBadge = new Span(String.valueOf(i + 1)); Span numberBadge = new Span(String.valueOf(i + 1));
@@ -438,41 +465,46 @@ public class OrderDetailDialog extends Dialog {
stationInfo.add(address); stationInfo.add(address);
} }
// Edit button in top-right corner
Button editButton = new Button(new Icon(VaadinIcon.EDIT));
editButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE, ButtonVariant.LUMO_SMALL);
editButton.getElement().setAttribute("title", "Station bearbeiten");
editButton.getStyle()
.set("position", "absolute")
.set("top", "var(--lumo-space-xs)")
.set("right", "var(--lumo-space-xs)");
editButton.addClickListener(e -> openStationEditDialog(station, section));
stationContent.add(actionBadge, stationInfo); stationContent.add(actionBadge, stationInfo);
stationContent.setFlexGrow(1, stationInfo); stationContent.setFlexGrow(1, stationInfo);
stationCard.add(stationContent, editButton); 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");
editButton.getStyle()
.set("position", "absolute")
.set("top", "var(--lumo-space-xs)")
.set("right", "var(--lumo-space-xs)");
editButton.addClickListener(e -> openStationEditDialog(station, section));
stationCard.add(editButton);
}
cardWrapper.add(numberBadge, stationCard); cardWrapper.add(numberBadge, stationCard);
// Drag and Drop setup // Drag and Drop setup only in edit mode
DragSource<HorizontalLayout> dragSource = DragSource.create(cardWrapper); if (!readOnly) {
dragSource.setDragData(index); DragSource<HorizontalLayout> dragSource = DragSource.create(cardWrapper);
dragSource.addDragStartListener(e -> cardWrapper.getStyle().set("opacity", "0.5")); dragSource.setDragData(index);
dragSource.addDragEndListener(e -> cardWrapper.getStyle().remove("opacity")); dragSource.addDragStartListener(e -> cardWrapper.getStyle().set("opacity", "0.5"));
dragSource.addDragEndListener(e -> cardWrapper.getStyle().remove("opacity"));
DropTarget<HorizontalLayout> dropTarget = DropTarget.create(cardWrapper); DropTarget<HorizontalLayout> dropTarget = DropTarget.create(cardWrapper);
dropTarget.setDropEffect(DropEffect.MOVE); dropTarget.setDropEffect(DropEffect.MOVE);
dropTarget.addDropListener(e -> { dropTarget.addDropListener(e -> {
e.getDragSourceComponent().ifPresent(source -> { e.getDragSourceComponent().ifPresent(source -> {
if (source instanceof HorizontalLayout) { if (source instanceof HorizontalLayout) {
e.getDragData().ifPresent(data -> { e.getDragData().ifPresent(data -> {
if (data instanceof Integer sourceIndex) { if (data instanceof Integer sourceIndex) {
reorderStations(sourceIndex, index, section); reorderStations(sourceIndex, index, section);
} }
}); });
} }
});
}); });
}); }
section.add(cardWrapper); section.add(cardWrapper);
} }
@@ -596,7 +628,7 @@ public class OrderDetailDialog extends Dialog {
section.setSpacing(false); section.setSpacing(false);
section.setWidthFull(); 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); title.addClassNames(LumoUtility.Margin.Bottom.SMALL, LumoUtility.Margin.Top.SMALL);
section.add(title); section.add(title);
@@ -604,12 +636,18 @@ public class OrderDetailDialog extends Dialog {
offerTextArea.setWidthFull(); offerTextArea.setWidthFull();
offerTextArea.setMinHeight("120px"); offerTextArea.setMinHeight("120px");
offerTextArea.setValue(smtpEmailService.getDefaultOfferTemplate(summary)); offerTextArea.setValue(smtpEmailService.getDefaultOfferTemplate(summary));
offerTextArea.setHelperText("Bearbeiten Sie den Angebotstext vor dem Versenden"); offerTextArea.setReadOnly(readOnly);
if (!readOnly) {
offerTextArea.setHelperText("Bearbeiten Sie den Angebotstext vor dem Versenden");
}
recipientField = new EmailField("Empfänger"); recipientField = new EmailField("Empfänger");
recipientField.setWidthFull(); recipientField.setWidthFull();
recipientField.setValue(orderEmail.getFromAddress()); recipientField.setValue(orderEmail.getFromAddress());
recipientField.setHelperText("Email-Adresse des Empfängers"); recipientField.setReadOnly(readOnly);
if (!readOnly) {
recipientField.setHelperText("Email-Adresse des Empfängers");
}
section.add(offerTextArea, recipientField); section.add(offerTextArea, recipientField);
return section; return section;
@@ -621,7 +659,7 @@ public class OrderDetailDialog extends Dialog {
section.setSpacing(false); section.setSpacing(false);
section.setWidthFull(); 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); title.addClassNames(LumoUtility.Margin.Bottom.SMALL, LumoUtility.Margin.Top.SMALL);
section.add(title); section.add(title);
@@ -629,38 +667,49 @@ public class OrderDetailDialog extends Dialog {
confirmationTextArea.setWidthFull(); confirmationTextArea.setWidthFull();
confirmationTextArea.setMinHeight("120px"); confirmationTextArea.setMinHeight("120px");
confirmationTextArea.setValue(smtpEmailService.getDefaultConfirmationTemplate(summary)); confirmationTextArea.setValue(smtpEmailService.getDefaultConfirmationTemplate(summary));
confirmationTextArea.setHelperText("Bearbeiten Sie den Bestätigungstext vor dem Versenden"); confirmationTextArea.setReadOnly(readOnly);
if (!readOnly) {
confirmationTextArea.setHelperText("Bearbeiten Sie den Bestätigungstext vor dem Versenden");
}
recipientField = new EmailField("Empfänger"); recipientField = new EmailField("Empfänger");
recipientField.setWidthFull(); recipientField.setWidthFull();
recipientField.setValue(orderEmail.getFromAddress()); recipientField.setValue(orderEmail.getFromAddress());
recipientField.setHelperText("Email-Adresse des Empfängers"); recipientField.setReadOnly(readOnly);
if (!readOnly) {
recipientField.setHelperText("Email-Adresse des Empfängers");
}
section.add(confirmationTextArea, recipientField); section.add(confirmationTextArea, recipientField);
return section; return section;
} }
private void createFooter() { 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()); Button deleteButton = new Button("Löschen", e -> confirmDelete());
deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY); 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 // Spacer to push action buttons to the right
Div spacer = new Div(); Div spacer = new Div();
spacer.getStyle().set("flex-grow", "1"); spacer.getStyle().set("flex-grow", "1");
if (orderEmail.getType() == EmailType.QUOTE_REQUEST) { if (readOnly) {
Button sendOfferButton = new Button("Angebot senden", e -> sendOffer()); // Read-only mode: only show close and delete buttons
sendOfferButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); getFooter().add(deleteButton, spacer, closeButton);
getFooter().add(deleteButton, spacer, cancelButton, reprocessButton, sendOfferButton);
} else { } else {
Button acceptButton = new Button("Auftrag annehmen", e -> acceptOrder()); Button reprocessButton = new Button("Neu analysieren", e -> reprocessEmail());
acceptButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); reprocessButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
getFooter().add(deleteButton, spacer, cancelButton, reprocessButton, acceptButton);
if (orderEmail.getType() == EmailType.QUOTE_REQUEST) {
Button sendOfferButton = new Button("Angebot senden", e -> sendOffer());
sendOfferButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
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, closeButton, reprocessButton, acceptButton);
}
} }
} }
@@ -681,6 +730,12 @@ public class OrderDetailDialog extends Dialog {
} }
private void acceptOrder() { 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 { try {
String confirmationText = confirmationTextArea != null ? confirmationTextArea.getValue() : null; String confirmationText = confirmationTextArea != null ? confirmationTextArea.getValue() : null;
String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress(); String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress();
@@ -699,6 +754,12 @@ public class OrderDetailDialog extends Dialog {
} }
private void sendOffer() { 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 { try {
String offerText = offerTextArea != null ? offerTextArea.getValue() : null; String offerText = offerTextArea != null ? offerTextArea.getValue() : null;
String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress(); String recipient = recipientField != null ? recipientField.getValue() : orderEmail.getFromAddress();

View File

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