init
This commit is contained in:
531
src/ui/TrainingFormUIEnterEvent.java
Normal file
531
src/ui/TrainingFormUIEnterEvent.java
Normal file
@@ -0,0 +1,531 @@
|
||||
// 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 (1–6):"), 0, row);
|
||||
|
||||
HBox evalBox = new HBox(10);
|
||||
evalBox.setPadding(new Insets(0, 0, 0, 0));
|
||||
|
||||
evaluationField.setPromptText("1–6");
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user