Files
ChatStatistikTool/src/ui/TrainingFormUIEnterEvent.java
Christian Berger fa7c019588 init
2025-11-24 09:15:38 +01:00

532 lines
20 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// File: ui/TrainingFormUIEnterEvent.java
package ui;
import db.TrainingDAO;
import db.TrainingTypeDAO;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import model.PersonMapping;
import util.Config;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
/**
* Tab 1: Formular zur Erfassung einer neuen Schulung oder Führung.
* Inhalt aus der bisherigen TrainingFormUI übernommen.
*/
public class TrainingFormUIEnterEvent extends BorderPane {
private final ComboBox<String> veranstaltungstypBox = new ComboBox<>();
private final ComboBox<String> inhaltstypBox = new ComboBox<>();
private final TextField titelField = new TextField();
private final DatePicker dateField = new DatePicker();
private final TimeSpinner startzeitSpinner = new TimeSpinner();
private final TextField dauerField = new TextField();
private final ComboBox<String> modusBox = new ComboBox<>();
private final TextField teilnehmerAnzahlField = new TextField();
private final ComboBox<String> teilnehmerTypBox = new ComboBox<>();
//private final TextField durchfuehrendePersonField = new TextField();
private final ComboBox<String> durchfuehrendePersonBox = new ComboBox<>();
private final ComboBox<String> ausgefallenBox = new ComboBox<>();
private final TextArea kommentarField = new TextArea();
private final TextField evaluationField = new TextField();
private final Button speichernButton = new Button("Speichern");
private final Button resetButton = new Button("Zurücksetzen");
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
public TrainingFormUIEnterEvent() {
setPadding(new Insets(15));
Label heading = new Label("Neue Schulung anlegen");
heading.setStyle("-fx-font-size: 18; -fx-font-weight: bold;");
Label hint = new Label("Hinweis: Bereits geplante Veranstaltungen bitte im Tab 'Daten' bearbeiten.");
hint.setStyle("-fx-font-size: 12; -fx-text-fill: #555555; -fx-font-style: italic;");
VBox headingBox = new VBox(4, heading, hint);
setTop(headingBox);
GridPane grid = new GridPane();
grid.setPadding(new Insets(15, 0, 0, 0));
grid.setVgap(8);
grid.setHgap(10);
int row = 0;
grid.add(new Label("Veranstaltungstyp:"), 0, row); grid.add(veranstaltungstypBox, 1, row++);
grid.add(new Label("Inhaltstyp:"), 0, row); grid.add(inhaltstypBox, 1, row++);
grid.add(new Label("Titel Veranstaltung:"), 0, row); grid.add(titelField, 1, row++);
grid.add(new Label("Datum:"), 0, row); grid.add(dateField, 1, row++);
grid.add(new Label("Startzeit:"), 0, row); grid.add(startzeitSpinner, 1, row++);
grid.add(new Label("Dauer (Min):"), 0, row); grid.add(dauerField, 1, row++);
grid.add(new Label("Modus:"), 0, row); grid.add(modusBox, 1, row++);
grid.add(new Label("Teilnehmende:"), 0, row); grid.add(teilnehmerAnzahlField, 1, row++);
grid.add(new Label("Ausgefallen (Grund):"), 0, row); grid.add(ausgefallenBox, 1, row++);
grid.add(new Label("Typ Teilnehmende:"), 0, row); grid.add(teilnehmerTypBox, 1, row++);
//grid.add(new Label("Durchführende Person:"), 0, row); grid.add(durchfuehrendePersonField, 1, row++);
grid.add(new Label("Durchführende Person:"), 0, row);
grid.add(durchfuehrendePersonBox, 1, row++);
// --- Evaluation (direkte Eingabe ODER Live-Evaluation starten) ---
grid.add(new Label("Bewertung (16):"), 0, row);
HBox evalBox = new HBox(10);
evalBox.setPadding(new Insets(0, 0, 0, 0));
evaluationField.setPromptText("16");
evaluationField.setEditable(true);
evaluationField.setPrefWidth(80);
Button liveEvalButton = new Button("Live-Evaluation starten");
liveEvalButton.setStyle("-fx-font-size: 12;");
liveEvalButton.setOnAction(e -> {
EvaluationPopupUI popup = new EvaluationPopupUI();
double avg = popup.showAndWait();
if (avg > 0) {
evaluationField.setText(String.valueOf(avg));
}
});
evalBox.getChildren().addAll(evaluationField, liveEvalButton);
grid.add(evalBox, 1, row++);
grid.add(new Label("Kommentar:"), 0, row); grid.add(kommentarField, 1, row++);
grid.add(new HBox(10, speichernButton, resetButton), 1, ++row);
veranstaltungstypBox.getItems().setAll(model.DataModels.getVeranstaltungstypen());
modusBox.getItems().setAll(model.DataModels.getModi());
teilnehmerTypBox.getItems().setAll(model.DataModels.getTeilnehmendeTypen());
ausgefallenBox.getItems().setAll(model.DataModels.getAusgefallenGruende());
// Durchführende Personen: vordefinierte Namen + frei editierbar
durchfuehrendePersonBox.getItems().setAll(PersonMapping.getAllNames());
durchfuehrendePersonBox.setEditable(true);
// --- Durchführende Person: Dropdown + Standardwert ---
List<String> bekanntePersonen = new java.util.ArrayList<>(model.PersonMapping.getAllNames());
durchfuehrendePersonBox.getItems().setAll(bekanntePersonen);
durchfuehrendePersonBox.setEditable(true);
durchfuehrendePersonBox.setPromptText("Name oder Kürzel eingeben");
String currentUser = System.getProperty("user.name");
String displayName = model.PersonMapping.toName(currentUser);
durchfuehrendePersonBox.setValue(displayName);
fillInhaltstypBoxFromDatabase();
kommentarField.setPrefRowCount(3);
kommentarField.setWrapText(true);
teilnehmerAnzahlField.textProperty().addListener((obs, oldVal, newVal) -> {
Integer parsed = parseIntSafe(newVal);
int anzahl = parsed == null ? 0 : parsed;
if (anzahl <= 0) {
ausgefallenBox.setDisable(false);
} else {
ausgefallenBox.setValue("");
ausgefallenBox.setDisable(true);
}
validateFields();
});
ausgefallenBox.valueProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null && !newVal.isBlank()) {
teilnehmerAnzahlField.setText("0");
teilnehmerAnzahlField.setDisable(true);
} else {
teilnehmerAnzahlField.setDisable(false);
}
validateFields();
});
String username = System.getProperty("user.name");
durchfuehrendePersonBox.getItems().setAll(model.PersonMapping.getAllNames());
durchfuehrendePersonBox.setEditable(true);
durchfuehrendePersonBox.setPromptText("Name oder Kürzel eingeben");
durchfuehrendePersonBox.setValue(model.PersonMapping.toName(username));
veranstaltungstypBox.setOnAction(e -> updateFieldsForVeranstaltungstyp());
inhaltstypBox.setOnAction(e -> updateTitleFromInhaltstyp());
applyIntegerFilter(dauerField);
applyIntegerFilter(teilnehmerAnzahlField);
applyDecimalFilter(evaluationField);
ChangeListener<Object> validationListener = (obs, o, n) -> validateFields();
veranstaltungstypBox.valueProperty().addListener(validationListener);
inhaltstypBox.valueProperty().addListener(validationListener);
titelField.textProperty().addListener(validationListener);
dateField.valueProperty().addListener(validationListener);
dauerField.textProperty().addListener(validationListener);
modusBox.valueProperty().addListener(validationListener);
teilnehmerAnzahlField.textProperty().addListener(validationListener);
teilnehmerTypBox.valueProperty().addListener(validationListener);
startzeitSpinner.valueProperty().addListener(validationListener);
startzeitSpinner.getEditor().textProperty().addListener(validationListener);
speichernButton.setOnAction(e -> saveTraining());
speichernButton.setDisable(true);
setCenter(new ScrollPane(grid));
}
private void fillInhaltstypBoxFromDatabase() {
TrainingTypeDAO dao = new TrainingTypeDAO(Config.getDbPath());
List<String> types = dao.getAllTrainingTypes();
inhaltstypBox.getItems().setAll(types);
}
private static class TypeRule {
final String forcedInhalt;
final boolean inhaltLocked;
final LocalDate forcedDate;
final boolean dateLocked;
final Integer forcedDauer;
final boolean dauerLocked;
final String forcedModus;
final boolean modusLocked;
final LocalTime forcedStart;
final boolean startLocked;
TypeRule(String forcedInhalt, boolean inhaltLocked,
LocalDate forcedDate, boolean dateLocked,
Integer forcedDauer, boolean dauerLocked,
String forcedModus, boolean modusLocked,
LocalTime forcedStart, boolean startLocked) {
this.forcedInhalt = forcedInhalt;
this.inhaltLocked = inhaltLocked;
this.forcedDate = forcedDate;
this.dateLocked = dateLocked;
this.forcedDauer = forcedDauer;
this.dauerLocked = dauerLocked;
this.forcedModus = forcedModus;
this.modusLocked = modusLocked;
this.forcedStart = forcedStart;
this.startLocked = startLocked;
}
}
private static final java.util.Map<String, TypeRule> RULES = java.util.Map.of(
"fuehrung",
new TypeRule(
"Bibliotheksnutzung", true, // forcedInhalt + locked
null, false, // date unchanged
45, true, // Dauer
"praesenz", true, // Modus
null, false // Startzeit unchanged
),
"schulung",
new TypeRule(
null, false, // Inhalt unchanged
null, false, // Date unchanged
null, false, // Dauer unchanged
null, false, // Modus unchanged
null, false // Startzeit unchanged
),
"coffeelecture",
new TypeRule(
null, false, // identical to schulung
null, false,
null, false,
null, false,
null, false
),
"selbstlerneinheit",
new TypeRule(
null, false, // Inhalt permitted
java.time.LocalDate.now(), true, // date forced to today + locked
0, true, // Dauer = 0 + locked
"online", true, // Modus = online + locked
java.time.LocalTime.of(0, 0), true // Start = 00:00 + locked
)
);
private void applyTypeRule(String typ) {
TypeRule r = RULES.get(typ);
if (r == null) return;
// Inhalt
if (r.forcedInhalt != null) inhaltstypBox.setValue(r.forcedInhalt);
inhaltstypBox.setDisable(r.inhaltLocked);
// Date
if (r.forcedDate != null) dateField.setValue(r.forcedDate);
dateField.setDisable(r.dateLocked);
// Dauer
if (r.forcedDauer != null) dauerField.setText(String.valueOf(r.forcedDauer));
dauerField.setDisable(r.dauerLocked);
// Modus
if (r.forcedModus != null) modusBox.setValue(r.forcedModus);
modusBox.setDisable(r.modusLocked);
// Startzeit
if (r.forcedStart != null) startzeitSpinner.getValueFactory().setValue(r.forcedStart);
startzeitSpinner.setDisable(r.startLocked);
}
private void updateFieldsForVeranstaltungstyp() {
String typ = veranstaltungstypBox.getValue();
if (typ == null) return;
// FULL reset
resetDefaultsForTypeChange();
// Unlock EVERYTHING before applying rules (important!)
inhaltstypBox.setDisable(false);
dateField.setDisable(false);
dauerField.setDisable(false);
modusBox.setDisable(false);
startzeitSpinner.setDisable(false);
// Apply rule set
applyTypeRule(typ);
// Generate title based on NEW values
titelField.setText(generateTitle(
typ,
inhaltstypBox.getValue()
));
validateFields();
}
private String generateTitle(String typ, String inhalt) {
if (typ == null) return "";
// Führung: fixed title
if (typ.equals("fuehrung")) {
return "Bibliotheksführung";
}
// If no topic chosen yet → no title
if (inhalt == null || inhalt.isBlank()) {
return "";
}
switch (typ) {
case "schulung":
return "Schulung: " + inhalt;
case "coffeelecture":
return "Coffee Lecture: " + inhalt;
case "selbstlerneinheit":
return "Selbstlerneinheit: " + inhalt;
default:
return "";
}
}
private void resetDefaultsForTypeChange() {
// Reset ONLY the fields the user requested:
inhaltstypBox.setValue(null);
inhaltstypBox.setDisable(false);
titelField.clear();
modusBox.setValue(null);
modusBox.setDisable(false);
}
private void updateTitleFromInhaltstyp() {
String typ = veranstaltungstypBox.getValue();
String inhalt = inhaltstypBox.getValue();
titelField.setText(generateTitle(typ, inhalt));
validateFields();
}
public void refreshTrainingTypes() {
fillInhaltstypBoxFromDatabase();
}
private void saveTraining() {
try {
if (!validateFields()) return;
String veranstaltungstyp = veranstaltungstypBox.getValue();
String inhaltstyp = inhaltstypBox.getValue();
String titel = titelField.getText();
LocalDate datum = dateField.getValue();
LocalTime startzeit;
try {
startzeit = LocalTime.parse(startzeitSpinner.getEditor().getText(), TIME_FORMAT);
} catch (DateTimeParseException e) {
markField(startzeitSpinner.getEditor(), false);
validateFields();
return;
}
Integer dauer = parseIntSafe(dauerField.getText());
String modus = modusBox.getValue();
Integer teilnehmende = parseIntSafe(teilnehmerAnzahlField.getText());
String teilnehmendeTyp = teilnehmerTypBox.getValue();
String personInput = durchfuehrendePersonBox.getEditor().getText();
if (personInput == null || personInput.isBlank())
personInput = durchfuehrendePersonBox.getValue();
String person = PersonMapping.toKuerzel(personInput);
String grund = ausgefallenBox.getValue();
String kommentar = kommentarField.getText();
String datumStr = datum.format(DateTimeFormatter.ISO_LOCAL_DATE);
String startzeitStr = startzeit.format(TIME_FORMAT);
Double evaluation = parseDoubleSafe(evaluationField.getText());
if (evaluation != null && (evaluation < 1.0 || evaluation > 6.0)) {
new Alert(Alert.AlertType.WARNING, "Bewertung muss zwischen 1.0 und 6.0 liegen.").showAndWait();
return;
}
TrainingDAO dao = new TrainingDAO();
dao.insertTraining(
datumStr,
startzeitStr,
dauer,
modus,
teilnehmende,
teilnehmendeTyp,
person,
grund,
titel,
inhaltstyp,
veranstaltungstyp,
kommentar,
0,
evaluation
);
new Alert(Alert.AlertType.INFORMATION, "Datensatz erfolgreich gespeichert.").showAndWait();
resetForm();
} catch (Exception ex) {
new Alert(Alert.AlertType.ERROR, "Fehler beim Speichern: " + ex.getMessage()).showAndWait();
ex.printStackTrace();
}
}
// Hilfsmethoden ------------------------------------------------------------------
private void applyIntegerFilter(TextField field) {
field.textProperty().addListener((obs, old, neu) -> {
if (!neu.matches("\\d*")) field.setText(neu.replaceAll("[^\\d]", ""));
});
}
private void applyDecimalFilter(TextField field) {
field.textProperty().addListener((obs, old, neu) -> {
if (!neu.matches("\\d*(\\.\\d*)?")) field.setText(neu.replaceAll("[^\\d.]", ""));
});
}
private Integer parseIntSafe(String text) {
if (text == null || text.isBlank()) return null;
try {
return Integer.parseInt(text.trim());
} catch (NumberFormatException e) {
return null;
}
}
private Double parseDoubleSafe(String text) {
if (text == null || text.isBlank()) return null;
try {
return Double.parseDouble(text.trim());
} catch (NumberFormatException e) {
return null;
}
}
private boolean validateFields() {
boolean valid = true;
valid &= markField(veranstaltungstypBox, veranstaltungstypBox.getValue() != null);
valid &= markField(inhaltstypBox, inhaltstypBox.getValue() != null && !inhaltstypBox.getValue().isBlank());
valid &= markField(titelField, !titelField.getText().isBlank());
valid &= markField(dateField, dateField.getValue() != null);
valid &= markField(dauerField, !dauerField.getText().isBlank());
valid &= markField(modusBox, modusBox.getValue() != null);
valid &= markField(teilnehmerAnzahlField, !teilnehmerAnzahlField.getText().isBlank());
valid &= markField(teilnehmerTypBox, teilnehmerTypBox.getValue() != null);
String timeText = startzeitSpinner.getEditor().getText();
boolean validTime = false;
if (timeText != null && !timeText.isBlank()) {
try {
LocalTime.parse(timeText, TIME_FORMAT);
validTime = true;
} catch (DateTimeParseException e) {
validTime = false;
}
}
valid &= markField(startzeitSpinner.getEditor(), validTime);
speichernButton.setDisable(!valid);
return valid;
}
private boolean markField(Control field, boolean ok) {
if (ok) {
field.setStyle("");
} else {
field.setStyle("-fx-border-color: red; -fx-border-width: 2;");
}
return ok;
}
private void resetForm() {
veranstaltungstypBox.setValue(null);
inhaltstypBox.setDisable(false);
inhaltstypBox.setValue(null);
titelField.clear();
dateField.setValue(null);
startzeitSpinner.getValueFactory().setValue(LocalTime.of(10, 0));
dauerField.clear();
modusBox.setValue(null);
teilnehmerAnzahlField.clear();
teilnehmerTypBox.setValue(null);
ausgefallenBox.setValue("");
kommentarField.clear();
validateFields();
}
}