Erweiterungen
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,6 +8,9 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# H2 Database files
|
||||||
|
/data/
|
||||||
|
|
||||||
# The following files are generated/updated by vaadin-maven-plugin
|
# The following files are generated/updated by vaadin-maven-plugin
|
||||||
node_modules/
|
node_modules/
|
||||||
src/main/frontend/generated/
|
src/main/frontend/generated/
|
||||||
|
|||||||
@@ -94,22 +94,38 @@ public class LlmService {
|
|||||||
- Format: "Herr Vorname Nachname" oder "Frau Vorname Nachname"
|
- Format: "Herr Vorname Nachname" oder "Frau Vorname Nachname"
|
||||||
- Wenn kein Geschlecht erkennbar, nur den Namen extrahieren
|
- Wenn kein Geschlecht erkennbar, nur den Namen extrahieren
|
||||||
|
|
||||||
WICHTIG - Unterscheidung zwischen ORDER und QUOTE_REQUEST:
|
KRITISCH - Unterscheidung zwischen ORDER und QUOTE_REQUEST anhand von Stichwörtern:
|
||||||
|
|
||||||
Setze "QUOTE_REQUEST" wenn:
|
===== QUOTE_REQUEST (Angebotsanfrage) =====
|
||||||
- Nach einem Preis, Kosten oder Angebot gefragt wird
|
Setze "QUOTE_REQUEST" wenn EINES dieser Stichwörter vorkommt:
|
||||||
- Formulierungen wie "Was würde es kosten...", "Können Sie mir ein Angebot machen...", "Preisanfrage", "Kostenvoranschlag"
|
- "Angebot" (z.B. "bitte um Angebot", "Angebot erstellen")
|
||||||
|
- "Angebotsanfrage"
|
||||||
|
- "Anfrage für ein Angebot"
|
||||||
|
- "Preisanfrage"
|
||||||
|
- "Kostenvoranschlag"
|
||||||
|
- "Was kostet...", "Was würde es kosten..."
|
||||||
|
- "Können Sie mir ein Angebot machen..."
|
||||||
|
- "Preis", "Kosten" (in fragendem Kontext)
|
||||||
|
- "möchte gerne wissen", "interessiert an"
|
||||||
|
- "würde", "könnte", "eventuell", "vielleicht"
|
||||||
- Unverbindliche Anfragen ohne konkreten Auftrag
|
- Unverbindliche Anfragen ohne konkreten Auftrag
|
||||||
- Der Absender noch keine Entscheidung getroffen hat
|
|
||||||
- Worte wie "würde", "könnte", "möchte wissen", "interessiert an"
|
|
||||||
|
|
||||||
Setze "ORDER" NUR wenn:
|
===== ORDER (Auftrag) =====
|
||||||
- Ein konkreter, verbindlicher Auftrag erteilt wird
|
Setze "ORDER" NUR wenn EINES dieser Stichwörter vorkommt UND es sich um einen verbindlichen Auftrag handelt:
|
||||||
- Feste Termine und Daten genannt werden
|
- "Auftrag" (z.B. "hiermit erteilen wir den Auftrag", "Auftragserteilung")
|
||||||
- Formulierungen wie "Bitte holen Sie ab...", "Wir beauftragen Sie...", "Hiermit bestellen wir..."
|
- "beauftragen", "Beauftragung"
|
||||||
- Der Kunde klar eine Leistung beauftragt (nicht nur anfragt)
|
- "bestellen", "Bestellung"
|
||||||
|
- "bitte abholen", "bitte zustellen" (als direkte Anweisung, nicht als Frage)
|
||||||
|
- "hiermit bestellen wir..."
|
||||||
|
- "wir beauftragen Sie..."
|
||||||
|
- Feste, verbindliche Termine werden genannt
|
||||||
|
- Der Kunde hat BEREITS eine Entscheidung getroffen
|
||||||
|
|
||||||
Im Zweifel: Wähle "QUOTE_REQUEST"
|
===== ENTSCHEIDUNGSLOGIK =====
|
||||||
|
1. Prüfe ZUERST auf Stichwörter für QUOTE_REQUEST
|
||||||
|
2. Wenn "Angebot", "Angebotsanfrage", "Preisanfrage", "Kostenvoranschlag" vorkommt → QUOTE_REQUEST
|
||||||
|
3. Wenn "Auftrag", "beauftragen", "bestellen" vorkommt UND verbindlich formuliert → ORDER
|
||||||
|
4. Im Zweifel oder bei unklarer Formulierung → QUOTE_REQUEST
|
||||||
|
|
||||||
Weitere Regeln:
|
Weitere Regeln:
|
||||||
- Extrahiere alle Stationen (Abholung und Zustellung)
|
- Extrahiere alle Stationen (Abholung und Zustellung)
|
||||||
@@ -118,6 +134,8 @@ public class LlmService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildPrompt(OrderEmail email) {
|
private String buildPrompt(OrderEmail email) {
|
||||||
|
String keywordHint = detectKeywords(email);
|
||||||
|
|
||||||
return String.format("""
|
return String.format("""
|
||||||
Analysiere die folgende Email und extrahiere die relevanten Informationen:
|
Analysiere die folgende Email und extrahiere die relevanten Informationen:
|
||||||
|
|
||||||
@@ -125,15 +143,63 @@ public class LlmService {
|
|||||||
Betreff: %s
|
Betreff: %s
|
||||||
|
|
||||||
Inhalt:
|
Inhalt:
|
||||||
|
%s
|
||||||
|
|
||||||
%s
|
%s
|
||||||
""",
|
""",
|
||||||
email.getFromName() != null ? email.getFromName() : "",
|
email.getFromName() != null ? email.getFromName() : "",
|
||||||
email.getFromAddress(),
|
email.getFromAddress(),
|
||||||
email.getSubject(),
|
email.getSubject(),
|
||||||
email.getContent()
|
email.getContent(),
|
||||||
|
keywordHint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String detectKeywords(OrderEmail email) {
|
||||||
|
String fullText = (email.getSubject() + " " + email.getContent()).toLowerCase();
|
||||||
|
|
||||||
|
// Keywords für Angebotsanfrage (QUOTE_REQUEST)
|
||||||
|
boolean hasQuoteKeywords =
|
||||||
|
fullText.contains("angebot") ||
|
||||||
|
fullText.contains("angebotsanfrage") ||
|
||||||
|
fullText.contains("preisanfrage") ||
|
||||||
|
fullText.contains("kostenvoranschlag") ||
|
||||||
|
fullText.contains("was kostet") ||
|
||||||
|
fullText.contains("was würde") ||
|
||||||
|
fullText.contains("könnten sie mir") ||
|
||||||
|
fullText.contains("preis für") ||
|
||||||
|
fullText.contains("kosten für");
|
||||||
|
|
||||||
|
// Keywords für Auftrag (ORDER)
|
||||||
|
boolean hasOrderKeywords =
|
||||||
|
fullText.contains("auftrag") ||
|
||||||
|
fullText.contains("beauftrag") ||
|
||||||
|
fullText.contains("bestellung") ||
|
||||||
|
fullText.contains("bestellen") ||
|
||||||
|
fullText.contains("hiermit bestell") ||
|
||||||
|
fullText.contains("bitte abholen") ||
|
||||||
|
fullText.contains("bitte zustellen") ||
|
||||||
|
fullText.contains("abholung am") ||
|
||||||
|
fullText.contains("zustellung am");
|
||||||
|
|
||||||
|
StringBuilder hint = new StringBuilder("HINWEIS zur Klassifizierung: ");
|
||||||
|
|
||||||
|
if (hasQuoteKeywords && !hasOrderKeywords) {
|
||||||
|
hint.append("Die Email enthält Stichwörter für eine ANGEBOTSANFRAGE (z.B. 'Angebot', 'Preisanfrage'). ");
|
||||||
|
hint.append("Klassifiziere als QUOTE_REQUEST, es sei denn, es handelt sich eindeutig um einen verbindlichen Auftrag.");
|
||||||
|
} else if (hasOrderKeywords && !hasQuoteKeywords) {
|
||||||
|
hint.append("Die Email enthält Stichwörter für einen AUFTRAG (z.B. 'Auftrag', 'beauftragen', 'bestellen'). ");
|
||||||
|
hint.append("Klassifiziere als ORDER, wenn die Formulierung verbindlich ist.");
|
||||||
|
} else if (hasQuoteKeywords && hasOrderKeywords) {
|
||||||
|
hint.append("Die Email enthält sowohl Angebots- als auch Auftrags-Stichwörter. ");
|
||||||
|
hint.append("Prüfe sorgfältig: Wird ein Angebot ANGEFRAGT oder ein Auftrag ERTEILT? Im Zweifel: QUOTE_REQUEST.");
|
||||||
|
} else {
|
||||||
|
hint.append("Keine eindeutigen Stichwörter gefunden. Analysiere den Kontext sorgfältig. Im Zweifel: QUOTE_REQUEST.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hint.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private OrderSummary parseResponse(String response) {
|
private OrderSummary parseResponse(String response) {
|
||||||
try {
|
try {
|
||||||
JsonNode root = objectMapper.readTree(response);
|
JsonNode root = objectMapper.readTree(response);
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import com.vaadin.flow.component.button.ButtonVariant;
|
|||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
|
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.component.formlayout.FormLayout;
|
||||||
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;
|
||||||
import com.vaadin.flow.component.html.Span;
|
import com.vaadin.flow.component.html.Span;
|
||||||
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
@@ -17,12 +22,14 @@ import com.vaadin.flow.component.orderedlayout.Scroller;
|
|||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.component.textfield.EmailField;
|
import com.vaadin.flow.component.textfield.EmailField;
|
||||||
import com.vaadin.flow.component.textfield.TextArea;
|
import com.vaadin.flow.component.textfield.TextArea;
|
||||||
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import de.assecutor.aimailassistant.mail.domain.EmailType;
|
import de.assecutor.aimailassistant.mail.domain.EmailType;
|
||||||
import de.assecutor.aimailassistant.mail.domain.Gender;
|
import de.assecutor.aimailassistant.mail.domain.Gender;
|
||||||
import de.assecutor.aimailassistant.mail.domain.OrderEmail;
|
import de.assecutor.aimailassistant.mail.domain.OrderEmail;
|
||||||
import de.assecutor.aimailassistant.mail.domain.OrderSummary;
|
import de.assecutor.aimailassistant.mail.domain.OrderSummary;
|
||||||
import de.assecutor.aimailassistant.mail.domain.Station;
|
import de.assecutor.aimailassistant.mail.domain.Station;
|
||||||
|
import de.assecutor.aimailassistant.mail.domain.StationAction;
|
||||||
import de.assecutor.aimailassistant.mail.service.LlmService;
|
import de.assecutor.aimailassistant.mail.service.LlmService;
|
||||||
import de.assecutor.aimailassistant.mail.service.SmtpEmailService;
|
import de.assecutor.aimailassistant.mail.service.SmtpEmailService;
|
||||||
|
|
||||||
@@ -343,14 +350,47 @@ public class OrderDetailDialog extends Dialog {
|
|||||||
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);
|
||||||
|
|
||||||
for (Station station : summary.getStations()) {
|
buildStationCards(section);
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildStationCards(VerticalLayout section) {
|
||||||
|
var stations = summary.getStations();
|
||||||
|
for (int i = 0; i < stations.size(); i++) {
|
||||||
|
Station station = stations.get(i);
|
||||||
|
int index = i;
|
||||||
|
|
||||||
|
HorizontalLayout cardWrapper = new HorizontalLayout();
|
||||||
|
cardWrapper.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
|
cardWrapper.setSpacing(true);
|
||||||
|
cardWrapper.setWidthFull();
|
||||||
|
cardWrapper.getStyle().set("cursor", "grab");
|
||||||
|
|
||||||
|
// Numbered badge (circular, light blue)
|
||||||
|
Span numberBadge = new Span(String.valueOf(i + 1));
|
||||||
|
numberBadge.getStyle()
|
||||||
|
.set("display", "flex")
|
||||||
|
.set("align-items", "center")
|
||||||
|
.set("justify-content", "center")
|
||||||
|
.set("width", "28px")
|
||||||
|
.set("height", "28px")
|
||||||
|
.set("min-width", "28px")
|
||||||
|
.set("border-radius", "50%")
|
||||||
|
.set("background-color", "var(--lumo-primary-color-10pct)")
|
||||||
|
.set("color", "var(--lumo-primary-text-color)")
|
||||||
|
.set("font-weight", "600")
|
||||||
|
.set("font-size", "var(--lumo-font-size-s)");
|
||||||
|
|
||||||
Div stationCard = new Div();
|
Div stationCard = new Div();
|
||||||
stationCard.addClassNames(
|
stationCard.addClassNames(
|
||||||
LumoUtility.Background.CONTRAST_5,
|
LumoUtility.Background.CONTRAST_5,
|
||||||
LumoUtility.BorderRadius.MEDIUM,
|
LumoUtility.BorderRadius.MEDIUM,
|
||||||
LumoUtility.Padding.SMALL
|
LumoUtility.Padding.SMALL
|
||||||
);
|
);
|
||||||
stationCard.setWidthFull();
|
stationCard.getStyle()
|
||||||
|
.set("flex", "1")
|
||||||
|
.set("position", "relative");
|
||||||
|
|
||||||
HorizontalLayout stationContent = new HorizontalLayout();
|
HorizontalLayout stationContent = new HorizontalLayout();
|
||||||
stationContent.setAlignItems(FlexComponent.Alignment.CENTER);
|
stationContent.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
@@ -386,33 +426,79 @@ public class OrderDetailDialog extends Dialog {
|
|||||||
stationInfo.setSpacing(false);
|
stationInfo.setSpacing(false);
|
||||||
stationInfo.getStyle().set("min-width", "0");
|
stationInfo.getStyle().set("min-width", "0");
|
||||||
|
|
||||||
if (station.getName() != null) {
|
if (station.getName() != null && !station.getName().isBlank()) {
|
||||||
Span name = new Span(station.getName());
|
Span name = new Span(station.getName());
|
||||||
name.addClassName(LumoUtility.FontWeight.SEMIBOLD);
|
name.addClassName(LumoUtility.FontWeight.SEMIBOLD);
|
||||||
stationInfo.add(name);
|
stationInfo.add(name);
|
||||||
}
|
}
|
||||||
if (station.getAddress() != null) {
|
if (station.getAddress() != null && !station.getAddress().isBlank()) {
|
||||||
Span address = new Span(station.getAddress());
|
Span address = new Span(station.getAddress());
|
||||||
address.addClassNames(LumoUtility.TextColor.SECONDARY, LumoUtility.FontSize.SMALL);
|
address.addClassNames(LumoUtility.TextColor.SECONDARY, LumoUtility.FontSize.SMALL);
|
||||||
address.getStyle().set("word-break", "break-word");
|
address.getStyle().set("word-break", "break-word");
|
||||||
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);
|
stationCard.add(stationContent, editButton);
|
||||||
section.add(stationCard);
|
|
||||||
|
cardWrapper.add(numberBadge, stationCard);
|
||||||
|
|
||||||
|
// Drag and Drop setup
|
||||||
|
DragSource<HorizontalLayout> dragSource = DragSource.create(cardWrapper);
|
||||||
|
dragSource.setDragData(index);
|
||||||
|
dragSource.addDragStartListener(e -> cardWrapper.getStyle().set("opacity", "0.5"));
|
||||||
|
dragSource.addDragEndListener(e -> cardWrapper.getStyle().remove("opacity"));
|
||||||
|
|
||||||
|
DropTarget<HorizontalLayout> dropTarget = DropTarget.create(cardWrapper);
|
||||||
|
dropTarget.setDropEffect(DropEffect.MOVE);
|
||||||
|
dropTarget.addDropListener(e -> {
|
||||||
|
e.getDragSourceComponent().ifPresent(source -> {
|
||||||
|
if (source instanceof HorizontalLayout) {
|
||||||
|
e.getDragData().ifPresent(data -> {
|
||||||
|
if (data instanceof Integer sourceIndex) {
|
||||||
|
reorderStations(sourceIndex, index, section);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
section.add(cardWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tour anzeigen Button
|
// Tour anzeigen Button
|
||||||
if (summary.getStations().size() >= 2) {
|
if (stations.size() >= 2) {
|
||||||
Button showTourButton = new Button("Tour anzeigen", e -> openTourMap());
|
Button showTourButton = new Button("Tour anzeigen", e -> openTourMap());
|
||||||
showTourButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SMALL);
|
showTourButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SMALL);
|
||||||
showTourButton.setWidthFull();
|
showTourButton.setWidthFull();
|
||||||
section.add(showTourButton);
|
section.add(showTourButton);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return section;
|
private void reorderStations(int fromIndex, int toIndex, VerticalLayout section) {
|
||||||
|
if (fromIndex == toIndex) return;
|
||||||
|
|
||||||
|
var stations = summary.getStations();
|
||||||
|
Station movedStation = stations.remove(fromIndex);
|
||||||
|
stations.add(toIndex, movedStation);
|
||||||
|
|
||||||
|
// Update summary JSON
|
||||||
|
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
|
||||||
|
onProcessed.accept(orderEmail);
|
||||||
|
|
||||||
|
// Refresh the stations section
|
||||||
|
refreshStationsSection(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openTourMap() {
|
private void openTourMap() {
|
||||||
@@ -420,6 +506,64 @@ public class OrderDetailDialog extends Dialog {
|
|||||||
tourMapDialog.open();
|
tourMapDialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openStationEditDialog(Station station, VerticalLayout stationsSection) {
|
||||||
|
Dialog editDialog = new Dialog();
|
||||||
|
editDialog.setHeaderTitle("Station bearbeiten");
|
||||||
|
editDialog.setWidth("400px");
|
||||||
|
|
||||||
|
FormLayout formLayout = new FormLayout();
|
||||||
|
formLayout.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1));
|
||||||
|
|
||||||
|
TextField nameField = new TextField("Name");
|
||||||
|
nameField.setWidthFull();
|
||||||
|
nameField.setValue(station.getName() != null ? station.getName() : "");
|
||||||
|
|
||||||
|
TextArea addressField = new TextArea("Adresse");
|
||||||
|
addressField.setWidthFull();
|
||||||
|
addressField.setMinHeight("80px");
|
||||||
|
addressField.setValue(station.getAddress() != null ? station.getAddress() : "");
|
||||||
|
|
||||||
|
ComboBox<StationAction> actionComboBox = new ComboBox<>("Aktion");
|
||||||
|
actionComboBox.setItems(StationAction.values());
|
||||||
|
actionComboBox.setItemLabelGenerator(StationAction::getDisplayName);
|
||||||
|
actionComboBox.setValue(station.getAction());
|
||||||
|
actionComboBox.setWidthFull();
|
||||||
|
|
||||||
|
formLayout.add(nameField, addressField, actionComboBox);
|
||||||
|
editDialog.add(formLayout);
|
||||||
|
|
||||||
|
Button cancelButton = new Button("Abbrechen", e -> editDialog.close());
|
||||||
|
Button saveButton = new Button("Speichern", e -> {
|
||||||
|
station.setName(nameField.getValue());
|
||||||
|
station.setAddress(addressField.getValue());
|
||||||
|
station.setAction(actionComboBox.getValue());
|
||||||
|
|
||||||
|
// Update summary JSON
|
||||||
|
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
|
||||||
|
onProcessed.accept(orderEmail);
|
||||||
|
|
||||||
|
// Refresh the stations section
|
||||||
|
refreshStationsSection(stationsSection);
|
||||||
|
|
||||||
|
editDialog.close();
|
||||||
|
Notification.show("Station aktualisiert", 3000, Notification.Position.BOTTOM_START);
|
||||||
|
});
|
||||||
|
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
|
||||||
|
editDialog.getFooter().add(cancelButton, saveButton);
|
||||||
|
editDialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshStationsSection(VerticalLayout stationsSection) {
|
||||||
|
stationsSection.removeAll();
|
||||||
|
|
||||||
|
H4 title = new H4("Stationen");
|
||||||
|
title.addClassNames(LumoUtility.Margin.Bottom.SMALL, LumoUtility.Margin.Top.SMALL);
|
||||||
|
stationsSection.add(title);
|
||||||
|
|
||||||
|
buildStationCards(stationsSection);
|
||||||
|
}
|
||||||
|
|
||||||
private VerticalLayout createRemarksSection() {
|
private VerticalLayout createRemarksSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ vaadin.launch-browser=true
|
|||||||
# To improve the performance during development.
|
# To improve the performance during development.
|
||||||
vaadin.allowed-packages=com.vaadin,org.vaadin,com.flowingcode,de.assecutor.aimailassistant
|
vaadin.allowed-packages=com.vaadin,org.vaadin,com.flowingcode,de.assecutor.aimailassistant
|
||||||
|
|
||||||
# H2 Database Configuration
|
# H2 Database Configuration (file-based for persistence)
|
||||||
spring.datasource.url=jdbc:h2:mem:mailassistant
|
spring.datasource.url=jdbc:h2:file:./data/mailassistant;DB_CLOSE_ON_EXIT=FALSE
|
||||||
spring.datasource.driverClassName=org.h2.Driver
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
spring.datasource.username=sa
|
spring.datasource.username=sa
|
||||||
spring.datasource.password=
|
spring.datasource.password=
|
||||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||||
spring.jpa.hibernate.ddl-auto=create-drop
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.h2.console.enabled=true
|
spring.h2.console.enabled=true
|
||||||
|
|
||||||
# IMAP Configuration
|
# IMAP Configuration
|
||||||
|
|||||||
Reference in New Issue
Block a user