Compare commits

...

11 commits

Author SHA1 Message Date
c283ebd6c1
#27: WIP
All checks were successful
Quality Check / Linting Check (push) Successful in 12s
Quality Check / Javadoc Check (push) Successful in 21s
Signed-off-by: Dominik Säume <Dominik.Saeume@hmmh.de>
2024-05-22 11:56:53 +02:00
275aa3252a
#27: Implementing Alternative Medications for the View 2024-05-22 11:55:41 +02:00
e9aa98d63a Merge pull request '#8 story/pfleger-module-login-pfleger' (#50) from story/pfleger-module-login-pfleger into main
All checks were successful
Quality Check / Linting Check (push) Successful in 19s
Javadoc Deploy / Javadoc (push) Successful in 33s
Quality Check / Javadoc Check (push) Successful in 31s
Reviewed-on: #50
Reviewed-by: SZUT-Ole <ole.kueck@hmmh.de>
2024-05-22 09:46:01 +00:00
0df409e775
#8: Cleanup & Javadoc
All checks were successful
Quality Check / Linting Check (push) Successful in 16s
Quality Check / Linting Check (pull_request) Successful in 21s
Quality Check / Javadoc Check (push) Successful in 35s
Quality Check / Javadoc Check (pull_request) Successful in 34s
Signed-off-by: Dominik Säume <Dominik.Saeume@hmmh.de>
2024-05-22 10:36:32 +02:00
7db1c83a08
#8: Use Permissions in Nurse Controller
Some checks failed
Quality Check / Linting Check (push) Failing after 12s
Quality Check / Javadoc Check (push) Successful in 21s
Quality Check / Linting Check (pull_request) Failing after 13s
Quality Check / Javadoc Check (pull_request) Successful in 21s
Signed-off-by: Dominik Säume <Dominik.Saeume@hmmh.de>
2024-05-22 09:44:53 +02:00
ae7fc3ab64
#8: Implement User Permissions System Base and usage with Tabs
Signed-off-by: Dominik Säume <Dominik.Saeume@hmmh.de>
2024-05-22 09:44:53 +02:00
069465c4ca
#8: Implement Login Logic 2024-05-22 09:44:48 +02:00
fb6fc923ac
#8: Setup Model, DAO & Fixtures
Signed-off-by: Dominik Säume <Dominik.Saeume@hmmh.de>
2024-05-21 19:11:43 +02:00
03ef4a235a
#8: Setup Schema for User Login 2024-05-21 19:11:05 +02:00
092b436247
#8: Setup Modal Window 2024-05-21 19:11:05 +02:00
fb26756309 README.md aktualisiert
All checks were successful
Quality Check / Linting Check (push) Successful in 18s
Javadoc Deploy / Javadoc (push) Successful in 32s
Quality Check / Javadoc Check (push) Successful in 29s
2024-05-21 17:06:34 +00:00
33 changed files with 1338 additions and 357 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="Database" uuid="5a5b8be1-080b-4129-b89d-42f1ea832b90"> <data-source source="LOCAL" name="nursingHome.db" uuid="5a5b8be1-080b-4129-b89d-42f1ea832b90">
<driver-ref>sqlite.xerial</driver-ref> <driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver> <jdbc-driver>org.sqlite.JDBC</jdbc-driver>

View file

@ -27,9 +27,12 @@
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/nurse/database/Nurse.sql" /> <option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/nurse/database/Nurse.sql" />
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/medication/database/Medication.sql" /> <option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/medication/database/Medication.sql" />
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/medication/database/Medication_Ingredient.sql" /> <option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/medication/database/Medication_Ingredient.sql" />
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/login/database/User.sql" />
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/login/database/UserPermission.sql" />
<option value="file://$PROJECT_DIR$/src/main/resources/de/hitec/nhplus/login/database/UserToNurse.sql" />
</array> </array>
</option> </option>
<option name="outLayout" value="File per object by schema.groovy" /> <option name="outLayout" value="File per object.groovy" />
</State> </State>
</list> </list>
</option> </option>

View file

@ -1,3 +1,6 @@
[![Static Badge](https://img.shields.io/badge/Wiki-Wiki?style=for-the-badge&logo=wikipedia&color=slategray)](https://git.euph.dev/SZUT/nhplus/wiki) [![Static Badge](https://img.shields.io/badge/Wiki-Wiki?style=for-the-badge&logo=wikipedia&color=slategray)](https://git.euph.dev/SZUT/nhplus/wiki)
[![Static Badge](https://img.shields.io/badge/Tests-Tests?style=for-the-badge&logo=textpattern&logoColor=cyan&color=blue)](https://git.euph.dev/SZUT/nhplus/wiki/Tests) [![Static Badge](https://img.shields.io/badge/Tests-Tests?style=for-the-badge&logo=textpattern&logoColor=cyan&color=blue)](https://git.euph.dev/SZUT/nhplus/wiki/Tests)
[![Static Badge](https://img.shields.io/badge/Javadoc-Javadoc?style=for-the-badge&logo=readthedocs&logoColor=orange&color=beige)](https://nhplus.euph.dev) [![Static Badge](https://img.shields.io/badge/Javadoc-Javadoc?style=for-the-badge&logo=readthedocs&logoColor=orange&color=beige)](https://nhplus.euph.dev)
[![Static Badge](https://img.shields.io/badge/Credentails-Credentails?style=for-the-badge&logo=keepassxc&logoColor=lime&color=darkgreen)](https://git.euph.dev/SZUT/nhplus/wiki/Test-Anmelde-Daten)

Binary file not shown.

View file

@ -1,11 +1,15 @@
package de.hitec.nhplus; package de.hitec.nhplus;
import de.hitec.nhplus.datastorage.ConnectionBuilder; import de.hitec.nhplus.datastorage.ConnectionBuilder;
import de.hitec.nhplus.login.LoginController;
import de.hitec.nhplus.login.User;
import de.hitec.nhplus.main.MainWindowController;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
@ -33,13 +37,43 @@ public class Main extends Application {
@Override @Override
public void start(Stage primaryStage) { public void start(Stage primaryStage) {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
executeMainApplication(); User user = executeLogin();
if(user != null){
executeMainApplication(user);
}
}
/**
* Executes the login.
* @return User The {@link User} object for the logged-in {@link User}.
* Is {@code null}, if the login was not successful,
*/
private User executeLogin() {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/de/hitec/nhplus/login/LoginView.fxml"));
BorderPane pane = loader.load();
Scene scene = new Scene(pane);
Stage loginStage = new Stage();
loginStage.setTitle("NHPlus");
loginStage.setScene(scene);
loginStage.setResizable(false);
LoginController controller = loader.getController();
controller.initialize(loginStage);
loginStage.showAndWait();
return controller.user;
} catch (IOException exception) {
exception.printStackTrace();
return null;
}
} }
/** /**
* Executes the main application. * Executes the main application.
*/ */
private void executeMainApplication() { private void executeMainApplication(User user) {
try { try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/de/hitec/nhplus/main/MainWindowView.fxml")); FXMLLoader loader = new FXMLLoader(Main.class.getResource("/de/hitec/nhplus/main/MainWindowView.fxml"));
TabPane pane = loader.load(); TabPane pane = loader.load();
@ -48,6 +82,10 @@ public class Main extends Application {
this.primaryStage.setTitle("NHPlus"); this.primaryStage.setTitle("NHPlus");
this.primaryStage.setScene(scene); this.primaryStage.setScene(scene);
this.primaryStage.setResizable(true); this.primaryStage.setResizable(true);
MainWindowController controller = loader.getController();
controller.initialize(user);
this.primaryStage.show(); this.primaryStage.show();
this.primaryStage.setOnCloseRequest(event -> { this.primaryStage.setOnCloseRequest(event -> {

View file

@ -1,5 +1,6 @@
package de.hitec.nhplus.datastorage; package de.hitec.nhplus.datastorage;
import de.hitec.nhplus.login.database.UserDao;
import de.hitec.nhplus.medication.database.MedicationDao; import de.hitec.nhplus.medication.database.MedicationDao;
import de.hitec.nhplus.nurse.database.NurseDao; import de.hitec.nhplus.nurse.database.NurseDao;
import de.hitec.nhplus.patient.database.PatientDao; import de.hitec.nhplus.patient.database.PatientDao;
@ -63,4 +64,12 @@ public class DaoFactory {
public MedicationDao createMedicationDAO() { public MedicationDao createMedicationDAO() {
return new MedicationDao(ConnectionBuilder.getConnection()); return new MedicationDao(ConnectionBuilder.getConnection());
} }
/**
* @return A new {@link UserDao} instance with a database connection.
* @see de.hitec.nhplus.login.User User
*/
public UserDao createUserDAO() {
return new UserDao(ConnectionBuilder.getConnection());
}
} }

View file

@ -46,6 +46,11 @@ public class Fixtures {
medicationFixture.setupTable(connection); medicationFixture.setupTable(connection);
medicationFixture.load(); medicationFixture.load();
UserFixture userFixture = new UserFixture(nursesByName);
userFixture.dropTable(connection);
userFixture.setupTable(connection);
userFixture.load();
} catch (Exception exception) { } catch (Exception exception) {
System.out.println(exception.getMessage()); System.out.println(exception.getMessage());
} }

View file

@ -1,17 +1,17 @@
package de.hitec.nhplus.fixtures; package de.hitec.nhplus.fixtures;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.medication.Ingredient;
import de.hitec.nhplus.medication.Medication;
import de.hitec.nhplus.medication.database.MedicationDao;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.medication.Ingredient;
import de.hitec.nhplus.medication.Medication;
import de.hitec.nhplus.medication.database.MedicationDao;
/** /**
* {@link Fixture} for {@link Medication}. * {@link Fixture} for {@link Medication}.
* *
@ -20,11 +20,13 @@ import java.util.*;
public class MedicationFixture implements Fixture<Medication> { public class MedicationFixture implements Fixture<Medication> {
private static final String SCHEMA = "/de/hitec/nhplus/medication/database/Medication.sql"; private static final String SCHEMA = "/de/hitec/nhplus/medication/database/Medication.sql";
private static final String INGREDIENT_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Ingredient.sql"; private static final String INGREDIENT_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Ingredient.sql";
private static final String ALTERNATIVE_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Alternative.sql";
@Override @Override
public void dropTable(Connection connection) throws SQLException { public void dropTable(Connection connection) throws SQLException {
connection.createStatement().execute("DROP TABLE IF EXISTS medication"); connection.createStatement().execute("DROP TABLE IF EXISTS medication");
connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient"); connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient");
connection.createStatement().execute("DROP TABLE IF EXISTS medication_alternative");
} }
@Override @Override
@ -32,16 +34,25 @@ public class MedicationFixture implements Fixture<Medication> {
final InputStream schema = Main.class.getResourceAsStream(SCHEMA); final InputStream schema = Main.class.getResourceAsStream(SCHEMA);
final InputStream ingredientSchema = Main.class.getResourceAsStream(INGREDIENT_SCHEMA); final InputStream ingredientSchema = Main.class.getResourceAsStream(INGREDIENT_SCHEMA);
final InputStream alterantiveSchema = Main.class.getResourceAsStream(ALTERNATIVE_SCHEMA);
assert schema != null; assert schema != null;
assert ingredientSchema != null; assert ingredientSchema != null;
assert alterantiveSchema != null;
String SQL = new Scanner(schema, StandardCharsets.UTF_8) String SQL = new Scanner(schema, StandardCharsets.UTF_8)
.useDelimiter("\\A") .useDelimiter("\\A")
.next(); .next();
String ingredientSQL = ";" + new Scanner(ingredientSchema, StandardCharsets.UTF_8) String ingredientSQL = ";" + new Scanner(ingredientSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A") .useDelimiter("\\A")
.next(); .next();
String alternativeSQL = ";" + new Scanner(alterantiveSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
connection.createStatement().execute(SQL); connection.createStatement().execute(SQL);
connection.createStatement().execute(ingredientSQL); connection.createStatement().execute(ingredientSQL);
connection.createStatement().execute(alternativeSQL);
} }
@ -67,81 +78,87 @@ public class MedicationFixture implements Fixture<Medication> {
Ingredient warfarinnatrium = new Ingredient("Warfarinnatrium"); Ingredient warfarinnatrium = new Ingredient("Warfarinnatrium");
medications.add(new Medication( medications.add(new Medication(
"Metformin", "Metformin",
"AstraZeneca", "AstraZeneca",
List.of( List.of(
metforminhydrochlorid, metforminhydrochlorid,
cellulose, cellulose,
povidon, povidon,
magnesiumstearat magnesiumstearat
), ),
"Übelkeit, Durchfall, Laktatazidose (selten)", "Übelkeit, Durchfall, Laktatazidose (selten)",
"Oral", "Oral",
100 100,
new ArrayList<>()
)); ));
medications.add(new Medication( medications.add(new Medication(
"Lisinopril", "Lisinopril",
"Teva Pharmaceuticals", "Teva Pharmaceuticals",
List.of( List.of(
lisinoprilDihydrat, lisinoprilDihydrat,
mannitol, mannitol,
calciumphosphat, calciumphosphat,
magnesiumstearat magnesiumstearat
), ),
"Schwindel, trockener Husten", "Schwindel, trockener Husten",
"Oral", "Oral",
150 150,
new ArrayList<>()
)); ));
medications.add(new Medication( medications.add(new Medication(
"Simvastatin", "Simvastatin",
"Mylan", "Mylan",
List.of( List.of(
simvastatin, simvastatin,
laktose, laktose,
cellulose, cellulose,
magnesiumstearat magnesiumstearat
), ),
"Muskelschmerzen, Leberprobleme(selten)", "Muskelschmerzen, Leberprobleme(selten)",
"Oral", "Oral",
80 80,
new ArrayList<>()
)); ));
medications.add(new Medication( medications.add(new Medication(
"Enoxaparin", "Enoxaparin",
"Sanofi", "Sanofi",
List.of( List.of(
enoxaparinNatrium, enoxaparinNatrium,
benzylalkohol, benzylalkohol,
wasser wasser
), ),
"Blutungen, Reaktionen an der Injektionsstelle", "Blutungen, Reaktionen an der Injektionsstelle",
"Unterhautinjektion", "Unterhautinjektion",
120 120,
new ArrayList<>()
)); ));
medications.add(new Medication( medications.add(new Medication(
"Levothyroxin", "Levothyroxin",
"Sandoz", "Sandoz",
List.of( List.of(
levothyroxinnatrium, levothyroxinnatrium,
laktose, laktose,
staerke, staerke,
akaziengummi akaziengummi
), ),
"Herzrasen, Gewichtsverlust", "Herzrasen, Gewichtsverlust",
"Oral", "Oral",
90 90,
new ArrayList<>()
)); ));
medications.add(new Medication( medications.add(new Medication(
"Warfarin", "Warfarin",
"Apotex Inc.", "Apotex Inc.",
List.of( List.of(
warfarinnatrium, warfarinnatrium,
laktose, laktose,
staerke, staerke,
magnesiumstearat magnesiumstearat
), ),
"Blutungen, Blutergüsse", "Blutungen, Blutergüsse",
"Oral", "Oral",
110 110,
new ArrayList<>()
)); ));
MedicationDao dao = DaoFactory.getInstance().createMedicationDAO(); MedicationDao dao = DaoFactory.getInstance().createMedicationDAO();
Map<String, Medication> medicationsByName = new HashMap<>(); Map<String, Medication> medicationsByName = new HashMap<>();

View file

@ -54,6 +54,12 @@ public class NurseFixture implements Fixture<Nurse> {
true true
)); ));
nurses.add(new Nurse(
"Maria",
"Höller",
"666"
));
NurseDao dao = DaoFactory.getInstance().createNurseDAO(); NurseDao dao = DaoFactory.getInstance().createNurseDAO();
for (Nurse nurse : nurses) { for (Nurse nurse : nurses) {
dao.create(nurse); dao.create(nurse);

View file

@ -0,0 +1,108 @@
package de.hitec.nhplus.fixtures;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.login.Permissions;
import de.hitec.nhplus.login.User;
import de.hitec.nhplus.login.database.UserDao;
import de.hitec.nhplus.nurse.Nurse;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
/**
* {@link Fixture} for {@link User}.
*
* @author Dominik Säume
*/
public class UserFixture implements Fixture<User> {
private static final String SCHEMA = "/de/hitec/nhplus/login/database/User.sql";
private static final String PERMISSION_SCHEMA = "/de/hitec/nhplus/login/database/UserPermission.sql";
private static final String TO_NURSE_SCHEMA = "/de/hitec/nhplus/login/database/UserToNurse.sql";
private final Map<String, Nurse> nursesByName;
public UserFixture(Map<String, Nurse> nursesByName) {
this.nursesByName = nursesByName;
}
@Override
public void dropTable(Connection connection) throws SQLException {
connection.createStatement().execute("DROP TABLE IF EXISTS user");
connection.createStatement().execute("DROP TABLE IF EXISTS user__permissions");
connection.createStatement().execute("DROP TABLE IF EXISTS user__nurse");
}
@Override
public void setupTable(Connection connection) throws SQLException {
final InputStream schema = Main.class.getResourceAsStream(SCHEMA);
final InputStream permissionSchema = Main.class.getResourceAsStream(PERMISSION_SCHEMA);
final InputStream toNurseSchema = Main.class.getResourceAsStream(TO_NURSE_SCHEMA);
assert schema != null;
assert permissionSchema != null;
assert toNurseSchema != null;
String SQL = new Scanner(schema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
String permissionSQL = new Scanner(permissionSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
String toNurseSQL = new Scanner(toNurseSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
connection.createStatement().execute(SQL);
connection.createStatement().execute(permissionSQL);
connection.createStatement().execute(toNurseSQL);
}
@Override
public Map<String, User> load() throws SQLException {
List<User> users = new ArrayList<>();
User udo = new User(
"udo",
Permissions.OWNER,
null
);
udo.setPassword("uD0_187!");
users.add(udo);
User maria = new User(
"maria",
Permissions.NURSE,
nursesByName.get("Maria")
);
maria.setPassword("H!mm3lf4hrt");
users.add(maria);
User werner = new User(
"werner",
Permissions.MANAGEMENT,
null
);
werner.setPassword("TurboSchraube42!");
users.add(werner);
User fullPermissionsTestUser = new User(
"test",
-1,
null
);
fullPermissionsTestUser.setPassword("");
users.add(fullPermissionsTestUser);
UserDao dao = DaoFactory.getInstance().createUserDAO();
Map<String, User> usersByUsername = new HashMap<>();
for (User user : users) {
dao.create(user);
usersByUsername.put(user.getUsername(), user);
}
return usersByUsername;
}
}

View file

@ -0,0 +1,104 @@
package de.hitec.nhplus.login;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.login.database.UserDao;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Arrays;
/**
* Controller for handling the login of {@link User}s.
*
* @author Dominik Säume
*/
public class LoginController {
public User user;
@FXML
public TextField textFieldUsername;
@FXML
public PasswordField passwordField;
@FXML
public Button buttonSubmit;
private Stage stage;
private int loginTries = 0;
/**
* JavaFX Initialization method that is called after the binding of all the fields.
*
* @param stage The {@link Stage}, so the modal can close itself, when finished.
*/
public void initialize(Stage stage) {
this.stage = stage;
}
/**
* Internal method to handle actions on wrong logins, like a shake, timout and total tries.
*/
private void handleWrongPasswordOrUsername() {
loginTries++;
// Shake
TranslateTransition ttUsername = new TranslateTransition(Duration.millis(50), textFieldUsername);
ttUsername.setByX(10);
ttUsername.setAutoReverse(true);
ttUsername.setCycleCount(6);
TranslateTransition ttPassword = new TranslateTransition(Duration.millis(50), passwordField);
ttPassword.setByX(10);
ttPassword.setAutoReverse(true);
ttPassword.setCycleCount(6);
ttUsername.play();
ttPassword.play();
// Timout
PauseTransition pause = new PauseTransition(Duration.seconds(3));
pause.setOnFinished(event -> {
if (loginTries == 3) {
stage.close();
}
buttonSubmit.setDisable(false);
});
pause.play();
}
@FXML
public void handleSubmit() {
buttonSubmit.setDisable(true);
UserDao dao = DaoFactory.getInstance().createUserDAO();
try {
int id = dao.readUserId(textFieldUsername.getText());
if (id == 0) {
handleWrongPasswordOrUsername();
return;
}
byte[] salt = dao.readPasswordSalt(id);
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);
byte[] hash = md.digest(passwordField.getText().getBytes(StandardCharsets.UTF_8));
byte[] requiredHash = dao.readPasswordHash(id);
if (!Arrays.equals(hash, requiredHash)) {
handleWrongPasswordOrUsername();
return;
}
user = dao.read(id);
stage.close();
} catch (SQLException | NoSuchAlgorithmException exception) {
exception.printStackTrace();
}
}
}

View file

@ -0,0 +1,14 @@
package de.hitec.nhplus.login;
/**
* A simple class holding the bitmasks for all permissions.
* This is a class instead of an enum, for ease of use.
*
* @author Dominiok Säume
*/
public class Permissions {
public final static int EVERYBODY = 0b0;
public final static int NURSE = 0b1;
public final static int MANAGEMENT = 0b10;
public final static int OWNER = 0b100;
}

View file

@ -0,0 +1,120 @@
package de.hitec.nhplus.login;
import de.hitec.nhplus.nurse.Nurse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* The model for a {@link User}.
*
* @author Dominik Säume
*/
public class User {
private int id;
private String username;
private byte[] passwordSalt;
private byte[] passwordHash;
private int permissions = 0;
private Nurse nurse;
/**
* This constructor allows instantiating a {@link User} object with all existing fields.
*/
public User(
int id,
String username,
byte[] passwordSalt,
byte[] passwordHash,
int permissions,
Nurse nurse
) {
this.id = id;
this.username = username;
this.passwordSalt = passwordSalt;
this.passwordHash = passwordHash;
this.permissions = permissions;
this.nurse = nurse;
}
/**
* This constructor allows instantiating a {@link User} object.
*/
public User(
String username,
int permissions,
Nurse nurse
) {
this.username = username;
this.permissions = permissions;
this.nurse = nurse;
}
/**
* Sets the {@link User} password. The {@link User} will need to be manually stored with the
* {@link de.hitec.nhplus.login.database.UserDao UserDao}, for changes to persists.
* @param password The new Password
*/
public void setPassword(String password) {
try {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[32];
random.nextBytes(salt);
this.passwordSalt = salt;
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);
this.passwordHash = md.digest(password.getBytes(StandardCharsets.UTF_8));
}catch (NoSuchAlgorithmException exception){
exception.printStackTrace();
}
}
public boolean hasNursePermissions(){
return (permissions & Permissions.NURSE) != 0;
}
public boolean hasAdminPermissions(){
return (permissions & Permissions.MANAGEMENT) != 0;
}
public int getId() {
return id;
}
public byte[] getPasswordSalt() {
return passwordSalt;
}
public byte[] getPasswordHash() {
return passwordHash;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getPermissions() {
return permissions;
}
public void setPermissions(int permissions) {
this.permissions = permissions;
}
public Nurse getNurse() {
return nurse;
}
public void setNurse(Nurse nurse) {
this.nurse = nurse;
}
}

View file

@ -0,0 +1,232 @@
package de.hitec.nhplus.login.database;
import de.hitec.nhplus.datastorage.Dao;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.login.User;
import de.hitec.nhplus.nurse.Nurse;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link UserDao} is an implementation of the{@link de.hitec.nhplus.datastorage.Dao Dao}
* for the {@link User} model.
*
* @author Dominik Säume
*/
public class UserDao implements Dao<User> {
protected final Connection connection;
public UserDao(Connection connection) {
this.connection = connection;
}
/**
* Try reading a {@link User#id} by its {@link User#username}.
* @param username The {@link User#username username} to try reading the {@link User#id id} of.
* @return The {@link User#id}. {@code 0} if the {@link User} doesn't exist.
*/
public int readUserId(String username) throws SQLException {
final String SQL = "SELECT id FROM user WHERE username = ?";
PreparedStatement statement = this.connection.prepareStatement(SQL);
statement.setString(1, username);
return statement.executeQuery().getInt(1);
}
/**
* Read a {@link User}s {@link User#passwordSalt password salt}.
* @param id The {@link User#id} of which the {@link User#passwordSalt password salt} should be read.
* @return The {@link User#passwordSalt password salt} as a{@code byte[]}.
*/
public byte[] readPasswordSalt(int id) throws SQLException {
final String SQL = "SELECT passwordSalt FROM user WHERE id = ?";
PreparedStatement statement = this.connection.prepareStatement(SQL);
statement.setInt(1, id);
return statement.executeQuery().getBytes(1);
}
/**
* Read a {@link User}s {@link User#passwordHash password hash} for authentication purposes.
* @param id The {@link User#id} of which the {@link User#passwordHash password hash} should be read.
* @return The {@link User#passwordHash password hash} as a{@code byte[]}.
*/
public byte[] readPasswordHash(int id) throws SQLException {
final String SQL = "SELECT passwordHash FROM user WHERE id = ?";
PreparedStatement statement = this.connection.prepareStatement(SQL);
statement.setInt(1, id);
return statement.executeQuery().getBytes(1);
}
@Override
public User read(int id) throws SQLException {
final String SQL = """
SELECT
user.username,
user.passwordSalt,
user.passwordHash,
user__permissions.permissions,
user__nurse.nurseId
FROM user
LEFT JOIN user__permissions ON user.id = user__permissions.userId
LEFT JOIN user__nurse ON user.id = user__nurse.userId
WHERE user.id = ?;
""";
PreparedStatement statement = this.connection.prepareStatement(SQL);
statement.setInt(1, id);
ResultSet result = statement.executeQuery();
int nurseId = result.getInt(5);
Nurse nurse = null;
if (!result.wasNull()) {
nurse = DaoFactory.getInstance().createNurseDAO().read(nurseId);
}
return new User(
id,
result.getString(1),
result.getBytes(2),
result.getBytes(3),
result.getInt(4),
nurse
);
}
@Override
public void create(User user) throws SQLException {
connection.setAutoCommit(false); //Switch to Manual Commit, to do an SQL Transaction
final String userSQL = """
INSERT INTO user
(username, passwordSalt, passwordHash)
VALUES (?, ?, ?);
""";
PreparedStatement statement = this.connection.prepareStatement(userSQL);
statement.setString(1, user.getUsername());
statement.setBytes(2, user.getPasswordSalt());
statement.setBytes(3, user.getPasswordHash());
statement.execute();
ResultSet generatedKeys = connection.createStatement().executeQuery("SELECT last_insert_rowid()");
connection.commit(); //Finish SQL Transaction
connection.setAutoCommit(true); //Switch back Mode
if (!generatedKeys.next()) {
return;
}
int newId = generatedKeys.getInt(1);
final String permissionSQL = """
INSERT INTO user__permissions
(userId, permissions)
VALUES (?, ?);
""";
PreparedStatement permissionStatement = this.connection.prepareStatement(permissionSQL);
permissionStatement.setInt(1, newId);
permissionStatement.setInt(2, user.getPermissions());
permissionStatement.execute();
if (user.getNurse() == null) {
return;
}
final String nurseSQL = """
INSERT INTO user__nurse
(userId, nurseId)
VALUES (?, ?);
""";
PreparedStatement nurseStatement = this.connection.prepareStatement(nurseSQL);
nurseStatement.setInt(1, newId);
nurseStatement.setInt(2, user.getNurse().getId());
nurseStatement.execute();
}
@Override
public void update(User user) throws SQLException {
final String userSQL = """
UPDATE user SET
username = ?,
passwordSalt = ?,
passwordHash = ?
WHERE id = ?
""";
PreparedStatement statement = this.connection.prepareStatement(userSQL);
statement.setString(1, user.getUsername());
statement.setBytes(2, user.getPasswordSalt());
statement.setBytes(3, user.getPasswordHash());
statement.setInt(3, user.getId());
statement.executeUpdate();
final String permissionSQL = """
UPDATE user__permissions SET
permissions = ?
WHERE userId = ?
""";
PreparedStatement permissionStatement = this.connection.prepareStatement(permissionSQL);
permissionStatement.setInt(1, user.getPermissions());
permissionStatement.setInt(2, user.getId());
permissionStatement.executeUpdate();
if (user.getNurse() == null) {
final String nurseSQL = """
DELETE FROM user__nurse WHERE userId = ?
""";
this.connection.prepareStatement(nurseSQL).executeUpdate();
return;
}
final String nurseSQL = """
UPDATE user__nurse set
nurseId = ?
WHERE userId = ?
""";
PreparedStatement nurseStatement = this.connection.prepareStatement(nurseSQL);
nurseStatement.setInt(1, user.getNurse().getId());
nurseStatement.setInt(2, user.getId());
permissionStatement.executeUpdate();
}
@Override
public void delete(int id) throws SQLException {
final String SQL = """
DELETE FROM user WHERE user.id = ?;
""";
PreparedStatement preparedStatement = this.connection.prepareStatement(SQL);
preparedStatement.setInt(1, id);
preparedStatement.executeUpdate();
}
@Override
public List<User> readAll() throws SQLException {
final String SQL = """
SELECT
user.id,
user.username,
user.passwordSalt,
user.passwordHash,
user__permissions.permissions,
user__nurse.nurseId
FROM user
LEFT JOIN user__permissions ON user.id = user__permissions.userId
LEFT JOIN user__nurse ON user.id = user__nurse.userId
""";
ResultSet result = connection.prepareStatement(SQL).executeQuery();
List<User> users = new ArrayList<>();
while (result.next()) {
int nurseId = result.getInt(6);
Nurse nurse = null;
if (!result.wasNull()) {
nurse = DaoFactory.getInstance().createNurseDAO().read(nurseId);
}
users.add(new User(
result.getInt(1),
result.getString(2),
result.getBytes(3),
result.getBytes(4),
result.getInt(5),
nurse
));
}
return users;
}
}

View file

@ -1,224 +1,98 @@
package de.hitec.nhplus.main; package de.hitec.nhplus.main;
import de.hitec.nhplus.Main; import de.hitec.nhplus.login.Permissions;
import de.hitec.nhplus.nurse.Nurse; import de.hitec.nhplus.login.User;
import de.hitec.nhplus.utils.tab.TabManager;
import de.hitec.nhplus.utils.tab.TabStruct;
import javafx.event.Event;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.SelectionModel;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import de.hitec.nhplus.treatment.Treatment;
import de.hitec.nhplus.medication.Medication;
import de.hitec.nhplus.patient.Patient;
import java.io.IOException; import java.util.List;
import java.util.Objects;
/** /**
* Controller for the main window of the application, which holds all tabs. * Controller for the main window of the application, which holds all tabs.
* *
* @author Bernd Heidemann
* @author Dominik Säume * @author Dominik Säume
* @author Armin Ribic
* @author Dorian Nemec
*/ */
public class MainWindowController { public class MainWindowController {
private static MainWindowController instace;
@FXML @FXML
private TabPane mainTabPane; public TabPane mainTabPane;
@FXML private User user;
private AnchorPane patientPage; private TabManager tabManager;
@FXML
private Tab patientTab;
@FXML
private AnchorPane activeTreatmentPage;
@FXML
private Tab treatmentTab;
@FXML
private TabPane treatmentTabPane;
@FXML
private Tab activeTreatmentTab;
@FXML
private AnchorPane lockedTreatmentPage;
@FXML
private Tab lockedTreatmentTab;
@FXML
private Tab nurseTab;
@FXML
private TabPane nurseTabPane;
@FXML
private AnchorPane activeNursePage;
@FXML
private Tab activeNurseTab;
@FXML
private AnchorPane lockedNursePage;
@FXML
private Tab lockedNurseTab;
@FXML
private AnchorPane medicationPage;
@FXML
private Tab medicationTab;
/**
* Initialization method that is called after the binding of all the fields.
*/
@FXML
public void initialize() {
loadPatientPage();
mainTabPane.getSelectionModel().select(patientTab);
patientTab.setOnSelectionChanged(event -> loadPatientPage());
medicationTab.setOnSelectionChanged(event -> loadMedicationPage());
nurseTab.setOnSelectionChanged(event -> loadNursePage());
nurseTabPane.getSelectionModel().select(activeNurseTab);
activeNurseTab.setOnSelectionChanged(event -> loadActiveNursePage());
lockedNurseTab.setOnSelectionChanged(event -> loadLockedNursePage());
treatmentTab.setOnSelectionChanged(event -> loadTreatmentPage());
treatmentTabPane.getSelectionModel().select(activeTreatmentTab);
activeTreatmentTab.setOnSelectionChanged(event -> loadActiveTreatmentPage());
lockedTreatmentTab.setOnSelectionChanged(event -> loadLockedTreatmentPage());
public MainWindowController() {
instace = this;
} }
/** /**
* Loads the {@link Patient} page into its tab. * Method copying the idea of a singleton, to allow getting the current {@link User}.
*/ */
private void loadPatientPage() { public static MainWindowController getInstance() {
try { return instace;
BorderPane patientsPane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/patient/AllPatientView.fxml"))
);
patientPage.getChildren().setAll(patientsPane);
AnchorPane.setTopAnchor(patientsPane, 0d);
AnchorPane.setBottomAnchor(patientsPane, 0d);
AnchorPane.setLeftAnchor(patientsPane, 0d);
AnchorPane.setRightAnchor(patientsPane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
} }
/** /**
* Loads the {@link Treatment } tab. * JavaFX Initialization method that is called after the binding of all the fields.
*
* @param user The logged in {@link User}.
*/ */
private void loadTreatmentPage() { @FXML
SelectionModel<Tab> selectionModel = treatmentTabPane.getSelectionModel(); public void initialize(User user) {
Tab selectedTab = selectionModel.getSelectedItem(); instace = this;
if (selectedTab == activeTreatmentTab) { this.user = user;
loadActiveTreatmentPage(); this.tabManager = new TabManager(user);
} setupTabs();
if (selectedTab == lockedTreatmentTab) {
loadLockedTreatmentPage();
}
} }
/** /**
* Loads the active {@link Treatment} page into its tab. * Sets up all Tabs Accessible, with help of the {@link TabManager}.
*/ */
private void loadActiveTreatmentPage() { private void setupTabs() {
try { tabManager.setupTab(mainTabPane, new TabStruct(
BorderPane activeTreatmentPane = FXMLLoader.load( "Patienten",
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/treatment/AllTreatmentView.fxml")) "/de/hitec/nhplus/patient/AllPatientView.fxml",
); Permissions.NURSE
activeTreatmentPage.getChildren().setAll(activeTreatmentPane); ));
AnchorPane.setTopAnchor(activeTreatmentPane, 0d); tabManager.setupSubTabPane(mainTabPane, "Behandlungen", Permissions.NURSE, List.of(
AnchorPane.setBottomAnchor(activeTreatmentPane, 0d); new TabStruct(
AnchorPane.setLeftAnchor(activeTreatmentPane, 0d); "Behandlungen",
AnchorPane.setRightAnchor(activeTreatmentPane, 0d); "/de/hitec/nhplus/treatment/AllTreatmentView.fxml",
} catch (IOException exception) { Permissions.NURSE
exception.printStackTrace(); ),
} new TabStruct(
"Gesperrte Behandlungen",
"/de/hitec/nhplus/treatment/LockedTreatmentView.fxml",
Permissions.NURSE
)
));
tabManager.setupSubTabPane(mainTabPane, "Pfleger", Permissions.EVERYBODY, List.of(
new TabStruct(
"Pfleger",
"/de/hitec/nhplus/nurse/AllNurseView.fxml",
Permissions.NURSE | Permissions.MANAGEMENT | Permissions.OWNER
),
new TabStruct(
"Gesperrte Pfleger",
"/de/hitec/nhplus/nurse/LockedNurseView.fxml",
Permissions.MANAGEMENT | Permissions.OWNER
)
));
tabManager.setupTab(mainTabPane, new TabStruct(
"Medikamente",
"/de/hitec/nhplus/medication/AllMedicationView.fxml",
Permissions.MANAGEMENT
));
Tab defaultTab = mainTabPane.getTabs().get(0);
mainTabPane.getSelectionModel().select(defaultTab);
defaultTab.getOnSelectionChanged().handle(new Event(Event.ANY));
} }
/** public User getUser() {
* Loads the locked {@link Treatment} page into its tab. return user;
*/
private void loadLockedTreatmentPage() {
try {
BorderPane treatmentsPane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/treatment/LockedTreatmentView.fxml"))
);
lockedTreatmentPage.getChildren().setAll(treatmentsPane);
AnchorPane.setTopAnchor(treatmentsPane, 0d);
AnchorPane.setBottomAnchor(treatmentsPane, 0d);
AnchorPane.setLeftAnchor(treatmentsPane, 0d);
AnchorPane.setRightAnchor(treatmentsPane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
}
/**
* Loads the {@link Nurse} page into its tab.
*/
private void loadNursePage() {
SelectionModel<Tab> selectionModel = nurseTabPane.getSelectionModel();
Tab selectedTab = selectionModel.getSelectedItem();
if (selectedTab == activeNurseTab) {
loadActiveNursePage();
}
if (selectedTab == lockedNurseTab) {
loadLockedNursePage();
}
}
/**
* Loads the active {@link Nurse} page into its tab.
*/
private void loadActiveNursePage() {
try {
BorderPane activeNursePane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/nurse/AllNurseView.fxml"))
);
activeNursePage.getChildren().setAll(activeNursePane);
AnchorPane.setTopAnchor(activeNursePane, 0d);
AnchorPane.setBottomAnchor(activeNursePane, 0d);
AnchorPane.setLeftAnchor(activeNursePane, 0d);
AnchorPane.setRightAnchor(activeNursePane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
}
/**
* Loads the locked {@link Nurse} page into its tab.
*/
private void loadLockedNursePage() {
try {
BorderPane lockedNursePane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/nurse/LockedNurseView.fxml"))
);
lockedNursePage.getChildren().setAll(lockedNursePane);
AnchorPane.setTopAnchor(lockedNursePane, 0d);
AnchorPane.setBottomAnchor(lockedNursePane, 0d);
AnchorPane.setLeftAnchor(lockedNursePane, 0d);
AnchorPane.setRightAnchor(lockedNursePane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
}
/**
* Loads the {@link Medication} page into its tab.
*/
private void loadMedicationPage() {
try {
BorderPane medicationPane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource("/de/hitec/nhplus/medication/AllMedicationView.fxml"))
);
medicationPage.getChildren().setAll(medicationPane);
AnchorPane.setTopAnchor(medicationPane, 0d);
AnchorPane.setBottomAnchor(medicationPane, 0d);
AnchorPane.setLeftAnchor(medicationPane, 0d);
AnchorPane.setRightAnchor(medicationPane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
} }
} }

View file

@ -52,6 +52,10 @@ public class AllMedicationController {
private final ObservableList<Medication> medications = FXCollections.observableArrayList(); private final ObservableList<Medication> medications = FXCollections.observableArrayList();
private MedicationDao dao; private MedicationDao dao;
public MedicationDao getDao() {
return dao;
}
/** /**
* Initialization method that is called after the binding of all the fields. * Initialization method that is called after the binding of all the fields.
*/ */

View file

@ -23,6 +23,7 @@ public class Medication {
private final SimpleStringProperty possibleSideEffects; private final SimpleStringProperty possibleSideEffects;
private final SimpleStringProperty administrationMethod; private final SimpleStringProperty administrationMethod;
private final SimpleIntegerProperty currentStock; private final SimpleIntegerProperty currentStock;
private final SimpleListProperty<Medication> alternativeMedication;
/** /**
* This constructor allows instantiating a {@link Medication} object, * This constructor allows instantiating a {@link Medication} object,
@ -37,7 +38,8 @@ public class Medication {
List<Ingredient> ingredients, List<Ingredient> ingredients,
String possibleSideEffects, String possibleSideEffects,
String administrationMethod, String administrationMethod,
int currentStock int currentStock,
List<Medication> alternativeMedication
) { ) {
this.name = new SimpleStringProperty(name); this.name = new SimpleStringProperty(name);
this.manufacturer = new SimpleStringProperty(manufacturer); this.manufacturer = new SimpleStringProperty(manufacturer);
@ -45,6 +47,7 @@ public class Medication {
this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects); this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects);
this.administrationMethod = new SimpleStringProperty(administrationMethod); this.administrationMethod = new SimpleStringProperty(administrationMethod);
this.currentStock = new SimpleIntegerProperty(currentStock); this.currentStock = new SimpleIntegerProperty(currentStock);
this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication));
} }
/** /**
@ -57,7 +60,8 @@ public class Medication {
List<Ingredient> ingredients, List<Ingredient> ingredients,
String possibleSideEffects, String possibleSideEffects,
String administrationMethod, String administrationMethod,
int currentStock int currentStock,
List<Medication> alternativeMedication
) { ) {
this.id = new SimpleIntegerProperty(id); this.id = new SimpleIntegerProperty(id);
this.name = new SimpleStringProperty(name); this.name = new SimpleStringProperty(name);
@ -66,6 +70,7 @@ public class Medication {
this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects); this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects);
this.administrationMethod = new SimpleStringProperty(administrationMethod); this.administrationMethod = new SimpleStringProperty(administrationMethod);
this.currentStock = new SimpleIntegerProperty(currentStock); this.currentStock = new SimpleIntegerProperty(currentStock);
this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication));
} }
public int getId() { public int getId() {
@ -124,6 +129,18 @@ public class Medication {
this.possibleSideEffects.set(possibleSideEffects); this.possibleSideEffects.set(possibleSideEffects);
} }
public ObservableList<Medication> getAlternativeMedication() {
return alternativeMedication.get();
}
public SimpleListProperty<Medication> alternativeMedicationProperty() {
return alternativeMedication;
}
public void setAlternativeMedication(ObservableList<Medication> alternativeMedication) {
this.alternativeMedication.set(alternativeMedication);
}
public String getAdministrationMethod() { public String getAdministrationMethod() {
return administrationMethod.get(); return administrationMethod.get();
} }
@ -162,6 +179,14 @@ public class Medication {
.add("Possible Side Effects: " + this.getPossibleSideEffects()) .add("Possible Side Effects: " + this.getPossibleSideEffects())
.add("Administration Method: " + this.getAdministrationMethod()) .add("Administration Method: " + this.getAdministrationMethod())
.add("Current Stock: " + this.getCurrentStock()) .add("Current Stock: " + this.getCurrentStock())
.add("Alternative Medication" + this.getAlternativeMedication())
.toString(); .toString();
} }
public String getComboBoxString() {
return new StringJoiner(System.lineSeparator())
.add("Name: " + this.getName())
.add("Hersteller: " + this.getManufacturer())
.toString();
}
} }

View file

@ -0,0 +1,129 @@
package de.hitec.nhplus.medication;
import java.util.List;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import org.controlsfx.control.SearchableComboBox;
/**
* A custom implementation of the {@link ListCell} for {@link Ingredient}s.
* This implementation contains an automatic resizing of the parent {@link ListView}.
*
* @author Dominik Säume
*/
public class MedicationListCell extends ListCell<Medication> {
private final Button deleteButton;
private SearchableComboBox<Medication> comboBox;
private static final double BUILTIN_BORDER_WIDTH = 1;
private static final double CELL_SPACING = 4;
private static final double CELL_PADDING = 4;
private static final double BUTTON_PADDING_X = 8;
private static final double BUTTON_PADDING_Y = 4;
private final double totalSpacing;
private final List<Medication> allOtherMedications;
public MedicationListCell(List<Medication> allOtherMedications) {
this.allOtherMedications = allOtherMedications;
this.setPadding(new Insets(CELL_PADDING));
comboBox = new SearchableComboBox();
ObservableList<Medication> list = FXCollections.observableArrayList();
list.setAll(allOtherMedications);
comboBox.setItems(list);
comboBox.setPromptText("Alternatve Auswählen");
comboBox.setCellFactory(this::comboBoxFactory);
comboBox.setButtonCell(comboBoxButtonFactory());
comboBox.valueProperty().addListener(this::onComboBoxChange);
deleteButton = new Button("-");
deleteButton.setPadding(new Insets(
BUTTON_PADDING_Y,
BUTTON_PADDING_X,
BUTTON_PADDING_Y,
BUTTON_PADDING_X
));
deleteButton.setOnAction(event -> getListView().getItems().remove(getItem()));
// Calculate Delete Button Width
Text textNode = new Text(deleteButton.getText());
textNode.setFont(deleteButton.getFont());
double calculatedDeleteButtonWidth = textNode.getLayoutBounds().getWidth() + BUTTON_PADDING_X * 2;
totalSpacing = BUILTIN_BORDER_WIDTH * 2 // List View
+ CELL_PADDING * 2
+ CELL_SPACING
+ calculatedDeleteButtonWidth;
}
private void onComboBoxChange(
ObservableValue<? extends Medication> observableValue,
Medication oldValue,
Medication newValue
) {
ListView<Medication> listView = getListView();
double max = listView.lookupAll("*")
.stream()
.filter(node -> node instanceof MedicationListCell)
.mapToDouble(node -> getComboBoxWidth())
.max()
.orElse(0);
listView.setMinWidth(max + totalSpacing);
}
@Override
protected void updateItem(Medication item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
BorderPane cellPane = new BorderPane();
cellPane.setCenter(comboBox);
cellPane.setRight(deleteButton);
BorderPane.setMargin(deleteButton, new Insets(0, 0, 0, CELL_SPACING));
setGraphic(cellPane);
}
}
private double getComboBoxWidth(){
return comboBox.getWidth();
}
private ListCell comboBoxFactory(Object param) {
return new ListCell<Medication>() {
@Override
protected void updateItem(Medication med, boolean empty) {
super.updateItem(med, empty);
setText(
empty || med == null
? null
: med.getComboBoxString()
);
}
};
}
private ListCell comboBoxButtonFactory() {
return new ListCell<Medication>() {
@Override
protected void updateItem(Medication med, boolean empty) {
super.updateItem(med, empty);
setText(
empty || med == null
? comboBox.getPromptText()
: med.getComboBoxString()
);
}
};
}
}

View file

@ -1,6 +1,11 @@
package de.hitec.nhplus.medication; package de.hitec.nhplus.medication;
import de.hitec.nhplus.treatment.Treatment; import static de.hitec.nhplus.utils.Validator.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -11,12 +16,6 @@ import javafx.scene.control.TextArea;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import static de.hitec.nhplus.utils.Validator.*;
/** /**
* The controller for creating and editing a specific {@link Medication}. * The controller for creating and editing a specific {@link Medication}.
* *
@ -38,21 +37,25 @@ public class MedicationModalController {
public TextArea textAreaPossibleSideEffects; public TextArea textAreaPossibleSideEffects;
@FXML @FXML
public Button buttonSave; public Button buttonSave;
@FXML
public ListView<Medication> listViewAlternativeMedication;
private Stage stage; private Stage stage;
private Medication medication; private Medication medication;
private final ObservableList<Ingredient> ingredients = FXCollections.observableArrayList(); private final ObservableList<Ingredient> ingredients = FXCollections.observableArrayList();
private final ObservableList<Medication> alternativeMediaction = FXCollections.observableArrayList();
private AllMedicationController controller; private AllMedicationController controller;
private boolean isNewMedication = false; private boolean isNewMedication = false;
private List<Medication> allOtherMedications;
/** /**
* Initialization method that is called after the binding of all the fields. * Initialization method that is called after the binding of all the fields.
*/ */
@FXML @FXML
public void initialize( public void initialize(
Stage stage, Stage stage,
AllMedicationController controller, AllMedicationController controller,
Medication medication Medication medication
) { ) {
this.stage = stage; this.stage = stage;
this.controller = controller; this.controller = controller;
@ -62,25 +65,30 @@ public class MedicationModalController {
} else { } else {
isNewMedication = true; isNewMedication = true;
this.medication = new Medication( this.medication = new Medication(
"", "",
"", "",
new ArrayList<>(), new ArrayList<>(),
"", "",
"", "",
0 0,
new ArrayList<>()
); );
this.buttonSave.setDisable(true); this.buttonSave.setDisable(true);
} }
listViewIngredients.setCellFactory(cellData -> new IngredientListCell()); listViewIngredients.setCellFactory(cellData -> new IngredientListCell());
listViewIngredients.setItems(ingredients); listViewIngredients.setItems(ingredients);
showData(); showData();
listViewAlternativeMedication.setCellFactory(cellData -> new MedicationListCell(allOtherMedications));
listViewAlternativeMedication.setItems(alternativeMediaction);
ChangeListener<String> inputMedicationValidationListener = (observableValue, oldText, newText) -> { ChangeListener<String> inputMedicationValidationListener = (observableValue, oldText, newText) -> {
boolean isValid = isValidMedicationName(textFieldName.getText()) boolean isValid = isValidMedicationName(textFieldName.getText())
&& isValidMedicationManufacturer(textFieldManufacturer.getText()) && isValidMedicationManufacturer(textFieldManufacturer.getText())
&& isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText()) && isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText())
&& isValidStock(textFieldCurrentStock.getText()); && isValidStock(textFieldCurrentStock.getText());
this.buttonSave.setDisable(!isValid); this.buttonSave.setDisable(!isValid);
}; };
@ -96,6 +104,17 @@ public class MedicationModalController {
*/ */
private void showData() { private void showData() {
ingredients.setAll(medication.getIngredients()); ingredients.setAll(medication.getIngredients());
try {
allOtherMedications = controller.getDao().readAll();
if (!isNewMedication) {
allOtherMedications = allOtherMedications
.stream()
.filter(med -> med.getId() != medication.getId())
.toList();
}
} catch (Exception exception) {
exception.printStackTrace();
}
textFieldName.setText(medication.getName()); textFieldName.setText(medication.getName());
textFieldManufacturer.setText(medication.getManufacturer()); textFieldManufacturer.setText(medication.getManufacturer());
textFieldAdministrationMethod.setText(medication.getAdministrationMethod()); textFieldAdministrationMethod.setText(medication.getAdministrationMethod());
@ -111,12 +130,12 @@ public class MedicationModalController {
this.medication.setCurrentStock(Integer.parseInt(textFieldCurrentStock.getText())); this.medication.setCurrentStock(Integer.parseInt(textFieldCurrentStock.getText()));
this.medication.setPossibleSideEffects(textAreaPossibleSideEffects.getText()); this.medication.setPossibleSideEffects(textAreaPossibleSideEffects.getText());
this.medication.setIngredients(ingredients this.medication.setIngredients(ingredients
.stream() .stream()
.map(Ingredient::getName) .map(Ingredient::getName)
.distinct() .distinct()
.filter(Predicate.not(String::isEmpty)) .filter(Predicate.not(String::isEmpty))
.map(Ingredient::new) .map(Ingredient::new)
.toList() .toList()
); );
if (isNewMedication) { if (isNewMedication) {
@ -138,4 +157,16 @@ public class MedicationModalController {
public void handleAddIngredient() { public void handleAddIngredient() {
ingredients.add(new Ingredient("")); ingredients.add(new Ingredient(""));
} }
public void handleAddAlternativeMedication() {
alternativeMediaction.add(new Medication(
null,
null,
new ArrayList<>(),
null,
null,
-1,
new ArrayList<>()
));
}
} }

View file

@ -63,6 +63,18 @@ public class MedicationDao implements Dao<Medication> {
ingredientStatement.setString(2, ingredient.getName()); ingredientStatement.setString(2, ingredient.getName());
ingredientStatement.execute(); ingredientStatement.execute();
} }
final String alternativeMedicationSQL = """
INSERT INTO medication_alternative
(id, alternativeId)
VALUES (?, ?);
""";
for (Medication alternative : medication.getAlternativeMedication()) {
PreparedStatement alternativeStatement = this.connection.prepareStatement(alternativeMedicationSQL);
alternativeStatement.setInt(1, newId);
alternativeStatement.setInt(2, alternative.getId());
alternativeStatement.execute();
}
} }
@Override @Override
@ -82,7 +94,7 @@ public class MedicationDao implements Dao<Medication> {
@Override @Override
public List<Medication> readAll() throws SQLException { public List<Medication> readAll() throws SQLException {
final String SQL = """ final String SQL = """
SELECT medication.*, medication_ingredient.name SELECT medication.*, medication_ingredient.name, medication_alternative.alternativeId
FROM medication LEFT JOIN FROM medication LEFT JOIN
medication_ingredient ON medication.id = medication_ingredient.id medication_ingredient ON medication.id = medication_ingredient.id
"""; """;
@ -103,7 +115,8 @@ public class MedicationDao implements Dao<Medication> {
new ArrayList<>(), new ArrayList<>(),
result.getString(4), result.getString(4),
result.getString(5), result.getString(5),
result.getInt(6) result.getInt(6),
new ArrayList<>()
); );
medications.add(medication); medications.add(medication);
} }
@ -188,10 +201,11 @@ public class MedicationDao implements Dao<Medication> {
result.getInt(1), result.getInt(1),
result.getString(2), result.getString(2),
result.getString(3), result.getString(3),
List.of(), new ArrayList<>(),
result.getString(4), result.getString(4),
result.getString(5), result.getString(5),
result.getInt(6) result.getInt(6),
new ArrayList<>()
); );
List<Ingredient> ingredients = new ArrayList<>(); List<Ingredient> ingredients = new ArrayList<>();

View file

@ -1,8 +1,8 @@
package de.hitec.nhplus.nurse; package de.hitec.nhplus.nurse;
import static de.hitec.nhplus.utils.Validator.*;
import de.hitec.nhplus.datastorage.DaoFactory; import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.login.Permissions;
import de.hitec.nhplus.main.MainWindowController;
import de.hitec.nhplus.nurse.database.NurseDao; import de.hitec.nhplus.nurse.database.NurseDao;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -17,6 +17,8 @@ import javafx.scene.control.cell.TextFieldTableCell;
import java.sql.SQLException; import java.sql.SQLException;
import static de.hitec.nhplus.utils.Validator.*;
/** /**
* The controller for viewing all {@link Nurse}s. * The controller for viewing all {@link Nurse}s.
* *
@ -49,12 +51,17 @@ public class AllNurseController {
private final ObservableList<Nurse> nurses = FXCollections.observableArrayList(); private final ObservableList<Nurse> nurses = FXCollections.observableArrayList();
private NurseDao dao; private NurseDao dao;
private boolean hasEditPermissions;
/** /**
* Initialization method that is called after the binding of all the fields. * Initialization method that is called after the binding of all the fields.
*/ */
@FXML @FXML
public void initialize() { public void initialize() {
int editPermissions = Permissions.MANAGEMENT | Permissions.OWNER;
int userPermissions = MainWindowController.getInstance().getUser().getPermissions();
hasEditPermissions = (userPermissions & editPermissions) != 0;
this.readAllAndShowInTableView(); this.readAllAndShowInTableView();
this.columnId.setCellValueFactory(new PropertyValueFactory<>("id")); this.columnId.setCellValueFactory(new PropertyValueFactory<>("id"));
@ -71,12 +78,18 @@ public class AllNurseController {
this.tableView.setItems(this.nurses); this.tableView.setItems(this.nurses);
this.buttonAdd.setDisable(true); this.buttonAdd.setDisable(true);
ChangeListener<String> inputNewNurseValidationListener = (observableValue, oldText, newText)-> if (!hasEditPermissions) {
this.buttonLock.setDisable(true);
return;
}
ChangeListener<String> inputNewNurseValidationListener = (observableValue, oldText, newText) ->
{ {
boolean isValid = isValidFirstName(this.textFieldFirstName.getText()) boolean isValid = isValidFirstName(this.textFieldFirstName.getText())
&& isValidSurName(this.textFieldSurName.getText()) && isValidSurName(this.textFieldSurName.getText())
&& isValidPhoneNumber(this.textFieldPhoneNumber.getText()); && isValidPhoneNumber(this.textFieldPhoneNumber.getText());
AllNurseController.this.buttonAdd.setDisable(!isValid); AllNurseController.this.buttonAdd.setDisable(!isValid);
}; };
@ -89,12 +102,12 @@ public class AllNurseController {
/** /**
* Internal method to read all data and set it to the table view. * Internal method to read all data and set it to the table view.
*/ */
private void readAllAndShowInTableView(){ private void readAllAndShowInTableView() {
this.nurses.clear(); this.nurses.clear();
this.dao = DaoFactory.getInstance().createNurseDAO(); this.dao = DaoFactory.getInstance().createNurseDAO();
try { try {
this.nurses.setAll(this.dao.readAllActive()); this.nurses.setAll(this.dao.readAllActive());
}catch (SQLException exception){ } catch (SQLException exception) {
exception.printStackTrace(); exception.printStackTrace();
} }
} }
@ -120,14 +133,13 @@ public class AllNurseController {
} }
@FXML @FXML
public void handleAdd(){ public void handleAdd() {
String surname=this.textFieldSurName.getText(); String surname = this.textFieldSurName.getText();
String firstName=this.textFieldFirstName.getText(); String firstName = this.textFieldFirstName.getText();
String phoneNumber=this.textFieldPhoneNumber.getText(); String phoneNumber = this.textFieldPhoneNumber.getText();
try { try {
this.dao.create(new Nurse(firstName, surname, phoneNumber)); this.dao.create(new Nurse(firstName, surname, phoneNumber));
} } catch (SQLException exception) {
catch (SQLException exception){
exception.printStackTrace(); exception.printStackTrace();
} }
readAllAndShowInTableView(); readAllAndShowInTableView();
@ -135,16 +147,16 @@ public class AllNurseController {
} }
@FXML @FXML
public void handleLock(){ public void handleLock() {
Nurse selectedItem = this.tableView.getSelectionModel().getSelectedItem(); Nurse selectedItem = this.tableView.getSelectionModel().getSelectedItem();
if (selectedItem == null){ if (selectedItem == null) {
return; return;
} }
try { try {
selectedItem.setLocked(true); selectedItem.setLocked(true);
this.dao.update(selectedItem); this.dao.update(selectedItem);
}catch (SQLException exception){ } catch (SQLException exception) {
exception.printStackTrace(); exception.printStackTrace();
} }
readAllAndShowInTableView(); readAllAndShowInTableView();
@ -152,6 +164,10 @@ public class AllNurseController {
@FXML @FXML
public void handleOnEditSurname(TableColumn.CellEditEvent<Nurse, String> event) { public void handleOnEditSurname(TableColumn.CellEditEvent<Nurse, String> event) {
if(!hasEditPermissions){
event.getTableView().refresh();
return;
}
String newSurName = event.getNewValue(); String newSurName = event.getNewValue();
if (!isValidSurName(newSurName)) { if (!isValidSurName(newSurName)) {
showValidationError("Nachname"); showValidationError("Nachname");
@ -164,6 +180,10 @@ public class AllNurseController {
@FXML @FXML
public void handleOnEditFirstname(TableColumn.CellEditEvent<Nurse, String> event) { public void handleOnEditFirstname(TableColumn.CellEditEvent<Nurse, String> event) {
if(!hasEditPermissions){
event.getTableView().refresh();
return;
}
String newFirstName = event.getNewValue(); String newFirstName = event.getNewValue();
if (!isValidFirstName(newFirstName)) { if (!isValidFirstName(newFirstName)) {
showValidationError("Vorname"); showValidationError("Vorname");
@ -176,6 +196,10 @@ public class AllNurseController {
@FXML @FXML
public void handleOnEditPhoneNumber(TableColumn.CellEditEvent<Nurse, String> event) { public void handleOnEditPhoneNumber(TableColumn.CellEditEvent<Nurse, String> event) {
if(!hasEditPermissions){
event.getTableView().refresh();
return;
}
String newPhoneNumber = event.getNewValue(); String newPhoneNumber = event.getNewValue();
if (!isValidPhoneNumber(newPhoneNumber)) { if (!isValidPhoneNumber(newPhoneNumber)) {
showValidationError("Telefonnummer"); showValidationError("Telefonnummer");

View file

@ -44,7 +44,7 @@ public class Nurse extends Person {
/** /**
* This constructor allows instantiating a {@link Nurse} object with * This constructor allows instantiating a {@link Nurse} object with
* specifying if the nurse is locked or not. * specifying whether the {@link Nurse} is locked or not.
*/ */
public Nurse( public Nurse(
String firstName, String firstName,

View file

@ -0,0 +1,114 @@
package de.hitec.nhplus.utils.tab;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.login.Permissions;
import de.hitec.nhplus.login.User;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
/**
* A utility class to construct {@link Tab}s from views. This also supports sub {@link Tab}s.
*
* @author Dominiok Säume
* @see TabManager
*/
public class TabManager {
private final User user;
public TabManager(User user) {
this.user = user;
}
/**
* Internal method to check permissions
* @param requiredPermissions The permissions the {@link User} requires.
* @param permissions The permissions the {@link User} has.
*/
private boolean hasPermissions(int requiredPermissions, int permissions) {
return requiredPermissions == Permissions.EVERYBODY
|| (permissions & requiredPermissions) != 0;
}
/**
* Method to set up a single tab and register it to its {@link TabPane}.
* @param tabPane The {@link TabPane} to register to.
* @param tabStruct The data needed to construct the new tab.
*/
public void setupTab(TabPane tabPane, TabStruct tabStruct) {
if (!hasPermissions(tabStruct.requiredPermissions, user.getPermissions())) {
return;
}
Tab tab = new Tab();
tab.setText(tabStruct.title);
AnchorPane pane = new AnchorPane();
tab.setContent(pane);
tab.setOnSelectionChanged(loadTabEventHandler(tabStruct, pane));
tabPane.getTabs().add(tab);
}
/**
* {@link EventHandler} to handle the loading of a {@link Tab}.
* @param tabStruct The {@link TabStruct} holding the path of the view.
* @param pane The main pane of the {@link Tab}.
*/
private EventHandler<Event> loadTabEventHandler(TabStruct tabStruct, AnchorPane pane) {
return event -> {
try {
BorderPane patientsPane = FXMLLoader.load(
Objects.requireNonNull(Main.class.getResource(tabStruct.view))
);
pane.getChildren().setAll(patientsPane);
AnchorPane.setTopAnchor(patientsPane, 0d);
AnchorPane.setBottomAnchor(patientsPane, 0d);
AnchorPane.setLeftAnchor(patientsPane, 0d);
AnchorPane.setRightAnchor(patientsPane, 0d);
} catch (IOException exception) {
exception.printStackTrace();
}
};
}
/**
* Method to set up a sub tab and register it to its parent {@link TabPane}.
* @param parentTabPane The {@link TabPane} to register to.
* @param title The title of the {@link Tab}.
* @param requiredPermissions The permissions required to see the sub {@link Tab}.
* @param subTabs A {@link List} of {@link TabStruct}s to create in the sub {@link Tab}.
*/
public void setupSubTabPane(TabPane parentTabPane, String title, int requiredPermissions, List<TabStruct> subTabs) {
if (!hasPermissions(requiredPermissions, user.getPermissions())) {
return;
}
Tab tab = new Tab();
tab.setText(title);
TabPane subTabPane = new TabPane();
subTabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
tab.setContent(subTabPane);
subTabs.forEach(tabStruct -> setupTab(subTabPane, tabStruct));
if (subTabPane.getTabs().isEmpty()) {
return;
}
tab.setOnSelectionChanged(loadSubTabPaneEventHandler(subTabPane));
parentTabPane.getTabs().add(tab);
}
/**
* {@link EventHandler} to handle the loading of a sub {@link TabPane}.
* @param tabPane The {@link TabPane} of the sub {@link Tab} to handle loading.
*/
private EventHandler<Event> loadSubTabPaneEventHandler(TabPane tabPane) {
return event -> {
Tab tab = tabPane.getSelectionModel().getSelectedItem();
tab.getOnSelectionChanged().handle(new Event(Event.ANY));
};
}
}

View file

@ -0,0 +1,19 @@
package de.hitec.nhplus.utils.tab;
/**
* A simple class holding the data needed for constructing a {@link javafx.scene.control.Tab Tab}.
*
* @author Dominiok Säume
* @see TabManager
*/
public class TabStruct {
public String title;
public String view;
public int requiredPermissions;
public TabStruct(String title, String view, int requiredPermissions) {
this.title = title;
this.view = view;
this.requiredPermissions = requiredPermissions;
}
}

View file

@ -3,6 +3,7 @@ module de.hitec.nhplus {
requires javafx.fxml; requires javafx.fxml;
requires java.sql; requires java.sql;
requires org.xerial.sqlitejdbc; requires org.xerial.sqlitejdbc;
requires org.controlsfx.controls;
exports de.hitec.nhplus; exports de.hitec.nhplus;
opens de.hitec.nhplus to javafx.fxml; opens de.hitec.nhplus to javafx.fxml;
@ -10,6 +11,9 @@ module de.hitec.nhplus {
exports de.hitec.nhplus.main; exports de.hitec.nhplus.main;
opens de.hitec.nhplus.main to javafx.base, javafx.fxml; opens de.hitec.nhplus.main to javafx.base, javafx.fxml;
exports de.hitec.nhplus.login;
opens de.hitec.nhplus.login to javafx.base, javafx.fxml;
exports de.hitec.nhplus.patient; exports de.hitec.nhplus.patient;
exports de.hitec.nhplus.patient.database; exports de.hitec.nhplus.patient.database;
opens de.hitec.nhplus.patient.database to javafx.base, javafx.fxml; opens de.hitec.nhplus.patient.database to javafx.base, javafx.fxml;

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.*?>
<BorderPane
xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="de.hitec.nhplus.login.LoginController"
minWidth="240"
maxWidth="240"
>
<padding>
<Insets bottom="8" left="8" right="8" top="8"/>
</padding>
<top>
<Label text="Anmeldung" BorderPane.alignment="CENTER"/>
</top>
<center>
<VBox spacing="8">
<BorderPane.margin>
<Insets bottom="8.0" top="8.0"/>
</BorderPane.margin>
<TextField
fx:id="textFieldUsername"
promptText="nutzername"
VBox.vgrow="ALWAYS"
/>
<PasswordField
fx:id="passwordField"
promptText="password"
VBox.vgrow="ALWAYS"
/>
</VBox>
</center>
<bottom>
<Button
fx:id="buttonSubmit"
text="Bestätigen"
BorderPane.alignment="CENTER"
onAction="#handleSubmit"
/>
</bottom>
</BorderPane>

View file

@ -0,0 +1,7 @@
CREATE TABLE user
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
passwordSalt BLOB NOT NULL,
passwordHash BLOB NOT NULL
)

View file

@ -0,0 +1,6 @@
CREATE TABLE user__permissions
(
userId INTEGER NOT NULL UNIQUE,
permissions INTEGER NOT NULL, -- Binary Bitmask for Permissions
FOREIGN KEY (userId) REFERENCES user (id) ON DELETE CASCADE
)

View file

@ -0,0 +1,7 @@
CREATE TABLE user__nurse
(
userId INTEGER NOT NULL UNIQUE,
nurseId INTEGER NOT NULL UNIQUE,
FOREIGN KEY (userId) REFERENCES user (id) ON DELETE CASCADE,
FOREIGN KEY (nurseId) REFERENCES nurse (id) ON DELETE CASCADE
)

View file

@ -1,38 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<TabPane <TabPane
fx:id="mainTabPane" fx:id="mainTabPane"
tabClosingPolicy="UNAVAILABLE" tabClosingPolicy="UNAVAILABLE"
xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="de.hitec.nhplus.main.MainWindowController" fx:controller="de.hitec.nhplus.main.MainWindowController"
> />
<Tab fx:id="patientTab" text="Patienten">
<AnchorPane fx:id="patientPage"/>
</Tab>
<Tab fx:id="treatmentTab" text="Behandlungen">
<TabPane fx:id="treatmentTabPane" tabClosingPolicy="UNAVAILABLE">
<Tab fx:id="activeTreatmentTab" text="Behandlungen">
<AnchorPane fx:id="activeTreatmentPage"/>
</Tab>
<Tab fx:id="lockedTreatmentTab" text="Gesperrte Behandlungen">
<AnchorPane fx:id="lockedTreatmentPage" />
</Tab>
</TabPane>
</Tab>
<Tab fx:id="nurseTab" text="Pfleger">
<TabPane fx:id="nurseTabPane" tabClosingPolicy="UNAVAILABLE">
<Tab fx:id="activeNurseTab" text="Pfleger">
<AnchorPane fx:id="activeNursePage"/>
</Tab>
<Tab fx:id="lockedNurseTab" text="Gesperrte Pfleger">
<AnchorPane fx:id="lockedNursePage"/>
</Tab>
</TabPane>
</Tab>
<Tab fx:id="medicationTab" text="Medikamente">
<AnchorPane fx:id="medicationPage"/>
</Tab>
</TabPane>

View file

@ -49,6 +49,11 @@
minWidth="100.0" minWidth="100.0"
text="Lagerbestand" text="Lagerbestand"
/> />
<TableColumn
fx:id="columnAlternativeMedication"
minWidth="100.0"
text="Alternative Medikamente"
/>
</columns> </columns>
<columnResizePolicy> <columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>

View file

@ -3,6 +3,7 @@
<?import javafx.geometry.*?> <?import javafx.geometry.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import org.controlsfx.control.SearchableComboBox?>
<BorderPane <BorderPane
xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1"
@ -77,16 +78,13 @@
</padding> </padding>
<left> <left>
<BorderPane> <BorderPane>
<BorderPane.margin>
<Insets right="8"/>
</BorderPane.margin>
<top> <top>
<Label text="Inhaltsstoffe:"/> <Label text="Inhaltsstoffe:"/>
</top> </top>
<center> <center>
<ListView fx:id="listViewIngredients" minWidth="200"> <ListView fx:id="listViewIngredients" minWidth="200">
<BorderPane.margin> <BorderPane.margin>
<Insets top="8"/> <Insets top="4"/>
</BorderPane.margin> </BorderPane.margin>
</ListView> </ListView>
</center> </center>
@ -105,10 +103,45 @@
</bottom> </bottom>
</BorderPane> </BorderPane>
</left> </left>
<center> <right>
<BorderPane> <BorderPane>
<top> <top>
<Label text="Nebenwirkungen"/> <Label text="Alternative Medikamente:"/>
</top>
<center>
<ListView fx:id="listViewAlternativeMedication" minWidth="200">
<BorderPane.margin>
<Insets top="4"/>
</BorderPane.margin>
</ListView>
</center>
<bottom>
<AnchorPane>
<BorderPane.margin>
<Insets top="8"/>
</BorderPane.margin>
<Button
onAction="#handleAddAlternativeMedication"
text="+"
AnchorPane.leftAnchor="0"
AnchorPane.rightAnchor="0"
/>
</AnchorPane>
</bottom>
</BorderPane>
</right>
<center>
<BorderPane>
<BorderPane.margin>
<Insets left="8" right="8"/>
</BorderPane.margin>
<top>
<Label text="Nebenwirkungen">
<BorderPane.margin>
<Insets bottom="4"/>
</BorderPane.margin>
</Label>
</top> </top>
<center> <center>
<TextArea fx:id="textAreaPossibleSideEffects"/> <TextArea fx:id="textAreaPossibleSideEffects"/>

View file

@ -0,0 +1,7 @@
CREATE TABLE medication_alternative
(
id INTEGER NOT NULL ,
alternativeId INTEGER NOT NULL ,
FOREIGN KEY (id) REFERENCES medication (id) ON DELETE CASCADE,
FOREIGN KEY (alternativeId) REFERENCES medication (id) ON DELETE CASCADE
)