├── src ├── main │ ├── resources │ │ ├── css │ │ │ ├── themeLight.css │ │ │ ├── fontBig.css │ │ │ ├── fontSmall.css │ │ │ ├── Application.css │ │ │ └── themeDark.css │ │ ├── images │ │ │ ├── DoctorImage.png │ │ │ ├── Screenshot from 2022-10-25 22-19-24.png │ │ │ ├── Screenshot from 2022-10-25 22-19-34.png │ │ │ ├── Screenshot from 2022-10-25 22-21-20.png │ │ │ └── Screenshot from 2022-10-25 22-30-29.png │ │ └── fxml │ │ │ ├── Option.fxml │ │ │ ├── ShowData.fxml │ │ │ ├── DeleteCustomer.fxml │ │ │ ├── AddCustomer.fxml │ │ │ └── LoginPage.fxml │ └── java │ │ ├── module-info.java │ │ └── com │ │ └── nidhal │ │ ├── view │ │ ├── ColorTheme.java │ │ ├── FontSize.java │ │ └── ViewFactory.java │ │ ├── config │ │ └── DBConfig.java │ │ ├── Launcher.java │ │ ├── controller │ │ ├── BaseController.java │ │ ├── DeleteCustomerController.java │ │ ├── LoginPageController.java │ │ ├── OptionsController.java │ │ ├── AddCustomerController.java │ │ └── AllDataController.java │ │ └── model │ │ ├── Customer.java │ │ └── DataBaseConnection.java └── test │ └── java │ └── com │ └── nidhal │ └── model │ └── DataBaseConnectionTest.java ├── .gitignore ├── .idea ├── vcs.xml ├── .gitignore ├── jpa-buddy.xml ├── encodings.xml ├── sqldialects.xml ├── dataSources.xml ├── compiler.xml ├── misc.xml └── jarRepositories.xml ├── docker-compose.yaml ├── README.md ├── db_setup.sql └── pom.xml /src/main/resources/css/themeLight.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /target/ -------------------------------------------------------------------------------- /src/main/resources/css/fontBig.css: -------------------------------------------------------------------------------- 1 | *{ 2 | -fx-font-size: 8pt; 3 | } -------------------------------------------------------------------------------- /src/main/resources/css/fontSmall.css: -------------------------------------------------------------------------------- 1 | *{ 2 | -fx-font-size: 7pt; 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/images/DoctorImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NidhalNaffati/Customer-Management-DesktopApplication/HEAD/src/main/resources/images/DoctorImage.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/images/Screenshot from 2022-10-25 22-19-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NidhalNaffati/Customer-Management-DesktopApplication/HEAD/src/main/resources/images/Screenshot from 2022-10-25 22-19-24.png -------------------------------------------------------------------------------- /src/main/resources/images/Screenshot from 2022-10-25 22-19-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NidhalNaffati/Customer-Management-DesktopApplication/HEAD/src/main/resources/images/Screenshot from 2022-10-25 22-19-34.png -------------------------------------------------------------------------------- /src/main/resources/images/Screenshot from 2022-10-25 22-21-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NidhalNaffati/Customer-Management-DesktopApplication/HEAD/src/main/resources/images/Screenshot from 2022-10-25 22-21-20.png -------------------------------------------------------------------------------- /src/main/resources/images/Screenshot from 2022-10-25 22-30-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NidhalNaffati/Customer-Management-DesktopApplication/HEAD/src/main/resources/images/Screenshot from 2022-10-25 22-30-29.png -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/jpa-buddy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module Customer.Management.DesktopApplication { 2 | 3 | requires javafx.graphics; 4 | requires javafx.fxml; 5 | requires javafx.controls; 6 | requires java.sql; 7 | 8 | 9 | opens com.nidhal; 10 | opens com.nidhal.model; 11 | opens com.nidhal.controller; 12 | opens com.nidhal.view; 13 | 14 | } -------------------------------------------------------------------------------- /.idea/sqldialects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/css/Application.css: -------------------------------------------------------------------------------- 1 | #btnsubmit 2 | { 3 | -fx-background-color : #fffafa ; 4 | -fx-font-size : 12px ; 5 | -fx-text-fill : #708090 ; 6 | -fx-padding : 8 8 8 8 ; 7 | -fx-border-radius : 10 ; 8 | -fx-font-weight : bold ; 9 | } 10 | 11 | #messagelabel 12 | { 13 | -fx-background-color : #fffafa ; 14 | } 15 | 16 | #textfieldforCIN 17 | { 18 | -fx-alignment : center ; 19 | } -------------------------------------------------------------------------------- /src/main/java/com/nidhal/view/ColorTheme.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.view; 2 | 3 | public enum ColorTheme { 4 | LIGHT, 5 | DARK; 6 | 7 | public static String getCssPath(ColorTheme colorTheme) { 8 | return switch (colorTheme) { 9 | case LIGHT -> "/css/themeLight.css"; 10 | case DARK -> "/css/themeDark.css"; 11 | default -> null; 12 | }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | mysql: 3 | image: mysql:8-debian # u can use any version of mysql 4 | container_name: mysql-testing-container 5 | volumes: 6 | - ./db_setup.sql:/docker-entrypoint-initdb.d/db_setup.sql 7 | environment: 8 | MYSQL_ROOT_PASSWORD: password 9 | MYSQL_USER: user 10 | MYSQL_PASSWORD: password 11 | MYSQL_DATABASE: CustomerDB 12 | ports: 13 | - "3306:3306" -------------------------------------------------------------------------------- /src/main/java/com/nidhal/view/FontSize.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.view; 2 | 3 | public enum FontSize { 4 | SMALL, 5 | BIG; 6 | 7 | 8 | public static String getCssPath(FontSize fontSize) { 9 | switch (fontSize) { 10 | case BIG: 11 | return "/css/fontBig.css"; 12 | case SMALL: 13 | return "/css/fontSmall.css"; 14 | default: 15 | return "/css/fontSmall.css"; 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mysql.8 6 | true 7 | com.mysql.cj.jdbc.Driver 8 | jdbc:mysql://localhost:3306 9 | $ProjectFileDir$ 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/config/DBConfig.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.config; 2 | 3 | /** 4 | * This class holds the database configuration details such as database name, URL, username, and password. 5 | */ 6 | public class DBConfig { 7 | private static final String DATABASE_NAME = "CustomerDB"; 8 | private static final String URL = "jdbc:mysql://localhost:3306/" + DATABASE_NAME + "?"; 9 | private static final String USER = "user"; 10 | private static final String PASSWORD = "password"; 11 | 12 | public static String getUrl() { 13 | return URL; 14 | } 15 | 16 | public static String getUser() { 17 | return USER; 18 | } 19 | 20 | public static String getPassword() { 21 | return PASSWORD; 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/nidhal/Launcher.java: -------------------------------------------------------------------------------- 1 | package com.nidhal; 2 | 3 | import com.nidhal.view.ViewFactory; 4 | import javafx.application.Application; 5 | import javafx.stage.Stage; 6 | 7 | 8 | import static com.nidhal.model.DataBaseConnection.createDataBaseIfNotExists; 9 | import static com.nidhal.model.DataBaseConnection.createTableCustomerIfNotExists; 10 | 11 | public class Launcher extends Application { 12 | public static void main(String[] args) { 13 | 14 | createDataBaseIfNotExists(); 15 | 16 | createTableCustomerIfNotExists(); 17 | 18 | launch(); 19 | } 20 | 21 | @Override 22 | public void start(Stage stage) { 23 | ViewFactory viewFactory = new ViewFactory(); 24 | viewFactory.showLoginWindow(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/fxml/Option.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 30 | 31 | 32 | 38 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /db_setup.sql: -------------------------------------------------------------------------------- 1 | USE CustomerDB; 2 | 3 | CREATE TABLE IF NOT EXISTS customer_table 4 | ( 5 | id INT NOT NULL, 6 | firstName VARCHAR(16) NOT NULL, 7 | lastName VARCHAR(16) NOT NULL, 8 | phoneNumber INT(8) NULL, 9 | location VARCHAR(32) NOT NULL, 10 | PRIMARY KEY (id), 11 | UNIQUE INDEX phoneNumber_UNIQUE (phoneNumber ASC) 12 | ); 13 | 14 | 15 | INSERT INTO customer_table (id, firstName, lastName, phoneNumber, location) 16 | VALUES (1, 'John', 'Doe', 11234567, 'Jerusalem'), 17 | (2, 'Jane', 'Doe', 12345678, 'Ramallah'), 18 | (3, 'John', 'Smith', 23456789, 'Bethlehem'), 19 | (4, 'Jane', 'Smith', 34567890, 'Al-Khalil'), 20 | (5, 'Alice', 'Johnson', 45678901, 'Nablus'), 21 | (6, 'Bob', 'Williams', 56789012, 'Gaza'), 22 | (7, 'Emma', 'Brown', 78965432, 'Jenin'), 23 | (8, 'David', 'Martinez', 78901234, 'Tulkarm'), 24 | (9, 'Olivia', 'Garcia', 14725896, 'Qalqilya'), 25 | (10, 'James', 'Wilson', 90123456, 'Ariha'), 26 | (11, 'Sophia', 'Lee', 45678902, 'Beit Sahour'), 27 | (12, 'Michael', 'Lopez', 78901235, 'Beit Jala'), 28 | (13, 'Isabella', 'Taylor', 56789011, 'Khan Yunis'), 29 | (14, 'William', 'Harris', 89012345, 'Rafah'), 30 | (15, 'Charlotte', 'Clark', 67890123, 'Beit Hanoun'), 31 | (16, 'Daniel', 'Young', 25896314, 'Jerusalem'), 32 | (17, 'Amelia', 'Lewis', 91472589, 'Ramallah'), 33 | (18, 'Benjamin', 'Allen', 23456777, 'Bethlehem'), 34 | (19, 'Mia', 'Walker', 78901222, 'Nablus'), 35 | (20, 'Ethan', 'King', 34567888, 'Gaza'), 36 | (21, 'Abigail', 'Green', 56789013, 'Jenin'), 37 | (22, 'Alexander', 'Baker', 45698731, 'Tulkarm'), 38 | (23, 'Harper', 'Adams', 34567891, 'Qalqilya'), 39 | (24, 'Ryan', 'Hill', 14796302, 'Ariha'), 40 | (25, 'Evelyn', 'Campbell', 56789014, 'Beit Sahour'), 41 | (26, 'Noah', 'Mitchell', 12365497, 'Beit Jala'), 42 | (27, 'Avery', 'Roberts', 67890321, 'Khan Yunis'), 43 | (28, 'Grace', 'Carter', 74185263, 'Rafah'), 44 | (29, 'Liam', 'Phillips', 12345666, 'Beit Hanoun'), 45 | (30, 'Chloe', 'Evans', 56789111, 'Jerusalem'); 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.nidhal 8 | Customer-Management-DesktopApplication 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 17 13 | 17 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.openjfx 22 | javafx-fxml 23 | 15.0.1 24 | 25 | 26 | 27 | 28 | mysql 29 | mysql-connector-java 30 | 8.0.28 31 | 32 | 33 | 34 | org.junit.jupiter 35 | junit-jupiter 36 | test 37 | 38 | 39 | 40 | org.openjfx 41 | javafx-controls 42 | 19 43 | 44 | 45 | 46 | 47 | junit 48 | junit 49 | 4.12 50 | test 51 | 52 | 53 | 54 | org.junit.jupiter 55 | junit-jupiter 56 | 5.8.1 57 | test 58 | 59 | 60 | org.mockito 61 | mockito-junit-jupiter 62 | 4.0.0 63 | test 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.openjfx 73 | javafx-maven-plugin 74 | 0.0.8 75 | 76 | Launcher 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/controller/OptionsController.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.controller; 2 | 3 | 4 | import com.nidhal.view.ColorTheme; 5 | import com.nidhal.view.FontSize; 6 | import com.nidhal.view.ViewFactory; 7 | import javafx.collections.FXCollections; 8 | import javafx.fxml.FXML; 9 | import javafx.fxml.Initializable; 10 | import javafx.scene.control.Button; 11 | import javafx.scene.control.ChoiceBox; 12 | import javafx.scene.control.Slider; 13 | import javafx.stage.Stage; 14 | import javafx.util.StringConverter; 15 | 16 | import java.net.URL; 17 | import java.util.ResourceBundle; 18 | 19 | public class OptionsController extends BaseController implements Initializable { 20 | public OptionsController(ViewFactory viewFactory, String fxmlName) { 21 | super(viewFactory, fxmlName); 22 | } 23 | 24 | @FXML 25 | private Button btnApply; 26 | 27 | @FXML 28 | private Button btnCancel; 29 | 30 | @FXML 31 | private ChoiceBox themePicker; 32 | 33 | @FXML 34 | private Slider fontSizePicker; 35 | 36 | 37 | @Override 38 | public void initialize(URL url, ResourceBundle resourceBundle) { 39 | setUpThemePicker(); 40 | setUpSizePicker(); 41 | } 42 | 43 | private void setUpSizePicker() { 44 | fontSizePicker.setMin(0); 45 | fontSizePicker.setMax(FontSize.values().length - 1); 46 | fontSizePicker.setValue(viewFactory.getFontSize().ordinal()); 47 | fontSizePicker.setMajorTickUnit(1); 48 | fontSizePicker.setMinorTickCount(0); 49 | fontSizePicker.setBlockIncrement(1); 50 | fontSizePicker.setSnapToTicks(true); 51 | fontSizePicker.setShowTickMarks(true); 52 | fontSizePicker.setShowTickLabels(true); 53 | fontSizePicker.setLabelFormatter(new StringConverter() { 54 | @Override 55 | public String toString(Double object) { 56 | int i = object.intValue(); 57 | return FontSize.values()[i].toString(); 58 | } 59 | 60 | @Override 61 | public Double fromString(String string) { 62 | return null; 63 | } 64 | }); 65 | fontSizePicker.valueProperty().addListener((obs, oldVal, newVal) -> { 66 | fontSizePicker.setValue(newVal.intValue()); 67 | }); 68 | 69 | } 70 | 71 | 72 | private void setUpThemePicker() { 73 | themePicker.setItems(FXCollections.observableArrayList(ColorTheme.values())); 74 | themePicker.setValue(viewFactory.getColorTheme()); 75 | } 76 | 77 | 78 | // to apply the changes. 79 | @FXML 80 | void ApplyBtnAction() { 81 | viewFactory.setColorTheme(themePicker.getValue()); 82 | viewFactory.setFontSize(FontSize.values()[(int) (fontSizePicker.getValue())]); 83 | viewFactory.updateStyles(); 84 | } 85 | 86 | // to cancel the changes. 87 | @FXML 88 | void ApplyBtnCancel() { 89 | Stage stage = (Stage) fontSizePicker.getScene().getWindow(); 90 | viewFactory.closeStage(stage); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/model/Customer.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.model; 2 | 3 | public class Customer { 4 | 5 | //id should be exactly 8 numbers 6 | private int id; 7 | 8 | //first name should be less than 16 characters 9 | private String firstName; 10 | 11 | //last name should be less than 16 characters 12 | private String lastName; 13 | 14 | //phone number should be exactly 8 numbers 15 | private int phoneNumber; 16 | 17 | //location should be less than 32 characters 18 | private String location; 19 | 20 | //constructor with all attributes. 21 | public Customer(int id, String firstName, String lastName, int phoneNumber, String location) { 22 | this.id = id; 23 | this.firstName = firstName; 24 | this.lastName = lastName; 25 | this.phoneNumber = phoneNumber; 26 | this.location = location; 27 | } 28 | 29 | 30 | // this method used for verifying where the id/phoneNumber is valid or not. 31 | static boolean validID(int ID) { 32 | boolean valid = true; 33 | try { 34 | int idLength = String.valueOf(ID).length(); 35 | System.out.println("idLength = " + idLength); 36 | if (idLength != 8) throw new IllegalArgumentException(); 37 | } catch (IllegalArgumentException ex) { 38 | System.out.println("Your Id/Phone must be = 8"); 39 | valid = false; 40 | } 41 | 42 | return valid; 43 | } 44 | 45 | // this method used for verifying where the firstName/lastName is valid or not by setting the limit 16. 46 | // this method used for verifying where the location is valid or not by setting the limit 32. 47 | 48 | public static boolean validStrings(String field, int limit) { 49 | boolean valid = true; 50 | try { 51 | int idLength = field.length(); 52 | if (idLength > limit) throw new IllegalArgumentException(); 53 | } catch (IllegalArgumentException ex) { 54 | System.out.println(field + " must be less than " + limit); 55 | valid = false; 56 | } 57 | return valid; 58 | } 59 | 60 | 61 | // this method used in AddCustomerController so we verify all the fields when we are adding a customer. 62 | public static boolean validFields(int id, int phoneNumber, String firstName, String lastName, String location) { 63 | 64 | 65 | if (!(validID(id) && validID(phoneNumber) && validStrings(firstName, 16) && validStrings(lastName, 16) && validStrings(location, 32))) 66 | return false; 67 | else return true; 68 | } 69 | 70 | 71 | 72 | // getters & setters. 73 | public int getId() { 74 | return id; 75 | } 76 | 77 | public void setId(int id) { 78 | this.id = id; 79 | } 80 | 81 | public String getFirstName() { 82 | return firstName; 83 | } 84 | 85 | public void setFirstName(String firstName) { 86 | this.firstName = firstName; 87 | } 88 | 89 | public String getLastName() { 90 | return lastName; 91 | } 92 | 93 | public void setLastName(String lastName) { 94 | this.lastName = lastName; 95 | } 96 | 97 | public int getPhoneNumber() { 98 | return phoneNumber; 99 | } 100 | 101 | public void setPhoneNumber(int phoneNumber) { 102 | this.phoneNumber = phoneNumber; 103 | } 104 | 105 | public String getLocation() { 106 | return location; 107 | } 108 | 109 | public void setLocation(String location) { 110 | this.location = location; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/controller/AddCustomerController.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.controller; 2 | 3 | 4 | import com.nidhal.view.ViewFactory; 5 | import javafx.fxml.FXML; 6 | import javafx.scene.control.Button; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.control.TextField; 9 | 10 | 11 | import static com.nidhal.model.DataBaseConnection.addNewCustomer; 12 | import static com.nidhal.model.Customer.validFields; 13 | 14 | /** 15 | * The `AddCustomerController` class is a JavaFX controller for the "Add Customer" screen. 16 | * It extends the `BaseController` class and is responsible for adding new customers to the database. 17 | */ 18 | public class AddCustomerController extends BaseController { 19 | /** 20 | * Constructor to initialize the `AddCustomerController` class. 21 | * It takes in two arguments: a `ViewFactory` object and a `String` containing the FXML file name. 22 | * 23 | * @param viewFactory the `ViewFactory` object used to switch between screens. 24 | * @param fxmlName the `String` containing the name of the FXML file. 25 | */ 26 | public AddCustomerController(ViewFactory viewFactory, String fxmlName) { 27 | super(viewFactory, fxmlName); 28 | } 29 | 30 | /** 31 | * JavaFX `Button` object that triggers the `addCustomer()` method when clicked. 32 | */ 33 | @FXML 34 | private Button btnAdd; 35 | 36 | /** 37 | * JavaFX `Label` objects for displaying messages to the user. 38 | */ 39 | @FXML 40 | private Label messageLabel, idLabel, firstNameLabel, locationLabel, phoneNumberLabel, scenceLabel, secondNameLabel; 41 | 42 | /** 43 | * JavaFX `TextField` objects for entering customer information. 44 | */ 45 | @FXML 46 | private TextField textFieldForId, textFieldForFirstName, textFieldForLocation, 47 | textFieldForPhoneNumber, textFieldForLastName; 48 | 49 | /** 50 | * Method to add a new customer to the database. 51 | *

52 | * The method retrieves the customer information from the text fields and checks if the values are valid. 53 | * If the values are invalid, it displays a message to the user and clears the text fields. 54 | * If the values are valid, it calls the `addNewCustomer` method and passes in the customer information, 55 | * along with the message label and the text field for the ID. 56 | */ 57 | @FXML 58 | void addCustomer() { 59 | 60 | // Retrieve the customer information from the text fields 61 | String firstName = textFieldForFirstName.getText(); 62 | String lastName = textFieldForLastName.getText(); 63 | String location = textFieldForLocation.getText(); 64 | 65 | // Convert the ID from a string to an int 66 | int id = 0; 67 | try { 68 | id = Integer.parseInt(textFieldForId.getText()); 69 | } catch (IllegalArgumentException ex) { 70 | messageLabel.setText("ID must be 8 numbers."); 71 | } 72 | 73 | // Convert the phone number from a string to an int 74 | int phoneNumber = 0; 75 | try { 76 | phoneNumber = Integer.parseInt(textFieldForId.getText()); 77 | } catch (IllegalArgumentException ex) { 78 | messageLabel.setText("Phone number must be 8 numbers."); 79 | } 80 | 81 | // Check if the values are valid 82 | if (!validFields(id, phoneNumber, firstName, lastName, location)) { 83 | messageLabel.setText("wrong values check out the docs."); 84 | textFieldForId.clear(); 85 | } else { 86 | addNewCustomer(id, phoneNumber, firstName, lastName, location, messageLabel, textFieldForId); 87 | } 88 | } 89 | 90 | private void clearTextFields() { 91 | textFieldForFirstName.clear(); 92 | textFieldForLastName.clear(); 93 | textFieldForId.clear(); 94 | textFieldForLocation.clear(); 95 | textFieldForPhoneNumber.clear(); 96 | } 97 | } -------------------------------------------------------------------------------- /src/test/java/com/nidhal/model/DataBaseConnectionTest.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.model; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.MockitoJUnitRunner; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | import java.awt.*; 12 | import java.sql.Connection; 13 | import java.sql.SQLException; 14 | 15 | import java.sql.*; 16 | 17 | import static org.junit.jupiter.api.Assertions.assertTrue; 18 | 19 | @RunWith(MockitoJUnitRunner.class) 20 | class DataBaseConnectionTest { 21 | 22 | @Mock 23 | Connection databaseConnection; 24 | 25 | static String databaseName = "CustomerDB"; 26 | static String url = "jdbc:mysql://localhost:3306/" + databaseName + "?"; 27 | static String user = "root"; 28 | static String password = "root"; 29 | 30 | //create the method that gives a random number greater that 1000_0000 and less than 9999_9999 31 | public static int getRandomNumber() { 32 | int min = 1000_0000; 33 | int max = 9999_9999; 34 | int random_int = (int) Math.floor(Math.random() * (max - min + 1) + min); 35 | return random_int; 36 | } 37 | 38 | 39 | @BeforeEach 40 | void setUp() throws SQLException { 41 | databaseConnection = DriverManager.getConnection(url, user, password); 42 | } 43 | 44 | 45 | @Test 46 | void testDataBaseConnectivity() throws SQLException { 47 | assertNotNull(databaseConnection); 48 | assertTrue(databaseConnection.isValid(0)); 49 | databaseConnection.close(); 50 | } 51 | 52 | @Test 53 | public void testGetDataBaseConnection() { 54 | try { 55 | Connection conn = DataBaseConnection.getDataBaseConnection(); 56 | assertNotNull(conn); 57 | } catch (SQLException e) { 58 | fail("Error connecting to the database"); 59 | } 60 | } 61 | 62 | @Test 63 | public void testCreateDataBaseIfNotExists() { 64 | DataBaseConnection.createDataBaseIfNotExists(); 65 | // Check if the database was created by trying to connect to it 66 | try { 67 | Connection conn = DataBaseConnection.getDataBaseConnection(); 68 | assertNotNull(conn); 69 | } catch (SQLException e) { 70 | fail("Error connecting to the database"); 71 | } 72 | } 73 | 74 | @Test 75 | public void testCreateTableCustomerIfNotExists() { 76 | DataBaseConnection.createTableCustomerIfNotExists(); 77 | // Check if the table was created by trying to execute a SELECT statement 78 | try { 79 | Connection conn = DataBaseConnection.getDataBaseConnection(); 80 | Statement stmt = conn.createStatement(); 81 | ResultSet rs = stmt.executeQuery("SELECT * FROM customer_table"); 82 | assertNotNull(rs); 83 | } catch (SQLException e) { 84 | fail("Error creating table or executing query"); 85 | } 86 | } 87 | 88 | private void addCustomer() { 89 | try { 90 | Connection conn = DataBaseConnection.getDataBaseConnection(); 91 | Statement stmt = conn.createStatement(); 92 | stmt.executeUpdate("INSERT INTO customer_table (id, firstName, lastName, phoneNumber, location) VALUES (2, 'John', 'Doe', 87654322, 'Tunisia')"); 93 | } catch (SQLException e) { 94 | e.printStackTrace(); 95 | fail("Error adding customer"); 96 | } 97 | } 98 | 99 | 100 | 101 | 102 | @Test 103 | public void testDeleteCustomerByID() { 104 | addCustomer(); 105 | // Verify that the customer was added 106 | try { 107 | Connection conn = DataBaseConnection.getDataBaseConnection(); 108 | Statement stmt = conn.createStatement(); 109 | ResultSet rs = stmt.executeQuery("SELECT * FROM customer_table WHERE id = 1"); 110 | assertTrue(rs.next()); 111 | // Delete the customer 112 | DataBaseConnection.deleteCustomerByID(1, null, null); 113 | // Verify that the customer was deleted 114 | rs = stmt.executeQuery("SELECT * FROM customer_table WHERE id = 1"); 115 | assertFalse(rs.next()); 116 | } catch (SQLException e) { 117 | fail("Error deleting customer"); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/main/resources/fxml/AddCustomer.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 58 | 64 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/controller/AllDataController.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.controller; 2 | 3 | import com.nidhal.model.Customer; 4 | import com.nidhal.view.ViewFactory; 5 | import javafx.collections.FXCollections; 6 | import javafx.collections.ObservableList; 7 | import javafx.fxml.FXML; 8 | import javafx.fxml.Initializable; 9 | import javafx.scene.control.Button; 10 | import javafx.scene.control.TableColumn; 11 | import javafx.scene.control.TableView; 12 | import javafx.scene.control.TextField; 13 | import javafx.scene.control.cell.PropertyValueFactory; 14 | 15 | import java.net.URL; 16 | import java.util.ArrayList; 17 | import java.util.ResourceBundle; 18 | 19 | import static com.nidhal.model.DataBaseConnection.getAllCustomersFromTheDB; 20 | import static com.nidhal.model.DataBaseConnection.getCustomersById; 21 | 22 | /** 23 | * The AllDataController class extends the BaseController and implements the Initializable interface. 24 | * It is responsible for displaying all customer data in a table and for searching for a specific customer by ID. 25 | * 26 | */ 27 | public class AllDataController extends BaseController implements Initializable { 28 | 29 | /** 30 | * Constructor for AllDataController. 31 | * 32 | * @param viewFactory the factory to create views. 33 | * @param fxmlName the name of the FXML file. 34 | */ 35 | public AllDataController(ViewFactory viewFactory, String fxmlName) { 36 | super(viewFactory, fxmlName); 37 | } 38 | 39 | /** 40 | * The table view to display the customer data. 41 | */ 42 | @FXML 43 | private TableView tableViewForCustomer; 44 | 45 | /** 46 | * The table columns for displaying the customer ID, phone number, first name, last name, and location. 47 | */ 48 | @FXML 49 | private TableColumn idCall, phoneNumberCall; 50 | @FXML 51 | private TableColumn firstNameCall , lastNameCall, locationCall; 52 | 53 | /** 54 | * The button for triggering the search. 55 | */ 56 | @FXML 57 | private Button btnSearch; 58 | 59 | /** 60 | * The text field for entering the search criteria. 61 | */ 62 | @FXML 63 | private TextField textFieldForSearch; 64 | 65 | /** 66 | * The list to store the customer data. 67 | */ 68 | ObservableList customerList = FXCollections.observableArrayList(); 69 | 70 | 71 | /** 72 | * The initialize method is called when the view is loaded. It shows all the customers. 73 | * 74 | * @param url the URL of the resource. 75 | * @param resourceBundle the resource bundle. 76 | */ 77 | @Override 78 | public void initialize(URL url, ResourceBundle resourceBundle) { 79 | showUpAllCustomers(); 80 | } 81 | 82 | 83 | /** 84 | * Refreshes the customer data by clearing the current data and calling the showAllCustomers method. 85 | */ 86 | @FXML 87 | void refreshData() { 88 | customerList.clear(); 89 | textFieldForSearch.clear(); 90 | showUpAllCustomers(); 91 | } 92 | 93 | /** 94 | * Shows all the customers by retrieving the data from the database and setting it to the table view. 95 | */ 96 | @FXML 97 | private void showUpAllCustomers() { 98 | 99 | ArrayList customerArrayList = getAllCustomersFromTheDB(); 100 | 101 | ObservableList observableArrayList = FXCollections.observableList(customerArrayList); 102 | 103 | SetCustomers(observableArrayList); 104 | 105 | } 106 | 107 | /** 108 | * Searches for a customer by ID by retrieving the data from the database and setting it to the table view. 109 | */ 110 | @FXML 111 | private void searchForCustomerByID() { 112 | int idNumber = 0; 113 | try { 114 | idNumber = Integer.parseInt(textFieldForSearch.getText()); 115 | } catch (NumberFormatException ex) { 116 | ex.printStackTrace(); 117 | } 118 | 119 | ArrayList customerArrayList = getCustomersById(idNumber); 120 | 121 | ObservableList observableArrayList = FXCollections.observableList(customerArrayList); 122 | 123 | SetCustomers(observableArrayList); 124 | 125 | } 126 | 127 | /** 128 | * This method sets the customers to be displayed in the table view for customers. 129 | * 130 | * @param customerList - An ObservableList of Customer objects. 131 | */ 132 | private void SetCustomers(ObservableList customerList) { 133 | idCall.setCellValueFactory(new PropertyValueFactory<>("id")); 134 | firstNameCall.setCellValueFactory(new PropertyValueFactory<>("firstName")); 135 | lastNameCall.setCellValueFactory(new PropertyValueFactory<>("lastName")); 136 | phoneNumberCall.setCellValueFactory(new PropertyValueFactory<>("phoneNumber")); 137 | locationCall.setCellValueFactory(new PropertyValueFactory<>("location")); 138 | 139 | tableViewForCustomer.setItems(customerList); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/view/ViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.view; 2 | import com.nidhal.controller.*; 3 | import javafx.fxml.FXMLLoader; 4 | import javafx.scene.Parent; 5 | import javafx.scene.Scene; 6 | import javafx.scene.image.Image; 7 | import javafx.stage.Stage; 8 | 9 | import java.net.URL; 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * ViewFactory is responsible for creating and showing different windows for different tasks such as login, 14 | * showing all customer's data, adding a new customer, deleting a customer and displaying options. 15 | * Each window is represented by a controller class (e.g. LoginPageController, AllDataController) 16 | * that extends the BaseController class and is responsible for the logic of the corresponding window. 17 | * The ViewFactory class also keeps track of all the currently open windows (stages) in an ArrayList and 18 | * allows for closing and updating the styles of these windows. 19 | */ 20 | public class ViewFactory { 21 | 22 | /** 23 | * ArrayList to keep track of all the currently open windows(stages) 24 | */ 25 | private final ArrayList activeStages; 26 | 27 | /** 28 | * constructor to initialize activeStages list 29 | */ 30 | public ViewFactory() { 31 | activeStages = new ArrayList(); 32 | } 33 | 34 | /** 35 | * Show the login window 36 | */ 37 | public void showLoginWindow() { 38 | BaseController controller = new LoginPageController(this, "/fxml/LoginPage.fxml"); 39 | initializeStage(controller); 40 | } 41 | 42 | /** 43 | * Show all the customers in the db. 44 | */ 45 | public void showAllDataWindow() { 46 | BaseController controller = new AllDataController(this, "/fxml/ShowData.fxml"); 47 | initializeStage(controller); 48 | } 49 | 50 | /** 51 | * Show the delete customer page. 52 | */ 53 | public void showDeleteCustomerWindow() { 54 | BaseController controller = new DeleteCustomerController(this, "/fxml/DeleteCustomer.fxml"); 55 | initializeStage(controller); 56 | } 57 | 58 | /** 59 | * Show add new customer window 60 | */ 61 | public void showAddCustomerWindow() { 62 | BaseController controller = new AddCustomerController(this, "/fxml/AddCustomer.fxml"); 63 | initializeStage(controller); 64 | } 65 | 66 | /** 67 | * Show option window 68 | */ 69 | public void showOptionWindow() { 70 | BaseController controller = new OptionsController(this, "/fxml/Option.fxml"); 71 | initializeStage(controller); 72 | } 73 | 74 | /** 75 | * This method is responsible for showing a specific window (parameter) 76 | * @param baseController the controller class that represents the window 77 | */ 78 | private void initializeStage(BaseController baseController) { 79 | System.out.print("Opening : "); 80 | System.out.println(getClass().getResource(baseController.getFxmlFile())); 81 | 82 | URL fxmlFileUrl = getClass().getResource(baseController.getFxmlFile()); 83 | FXMLLoader fxmlLoader = new FXMLLoader(); 84 | fxmlLoader.setController(baseController); 85 | Parent parent; 86 | 87 | try { 88 | fxmlLoader.setLocation(fxmlFileUrl); 89 | parent = fxmlLoader.load(); 90 | Scene scene = new Scene(parent); 91 | Stage stage = new Stage(); 92 | stage.setScene(scene); 93 | stage.setResizable(false); 94 | scene.getStylesheets().add(getClass().getResource("/css/themeDark.css").toExternalForm()); 95 | stage.getIcons().add(new Image(getClass().getResourceAsStream("/images/DoctorImage.png"))); 96 | stage.setScene(scene); 97 | stage.setTitle("CUSTOMER MANAGEMENT"); 98 | stage.show(); 99 | activeStages.add(stage); 100 | }catch (Exception e) { 101 | System.out.println("Error initializing stage: " + e.getMessage()); 102 | } 103 | } 104 | 105 | /** 106 | * Close a specific stage 107 | * @param stageToClose the stage to be closed 108 | */ 109 | public void closeStage(Stage stageToClose) { 110 | stageToClose.close(); 111 | activeStages.remove(stageToClose); 112 | } 113 | 114 | /** 115 | * Method to update the styles of all the currently open stages 116 | */ 117 | public void updateStyles() { 118 | try { 119 | for (Stage stage : activeStages) { 120 | Scene scene = stage.getScene(); 121 | scene.getStylesheets().clear(); 122 | scene.getStylesheets().add(getClass().getResource(ColorTheme.getCssPath(colorTheme)).toExternalForm()); 123 | scene.getStylesheets().add(getClass().getResource(FontSize.getCssPath(fontSize)).toExternalForm()); 124 | } 125 | } catch (NullPointerException exception) { 126 | exception.printStackTrace(); 127 | } 128 | 129 | } 130 | 131 | /** 132 | * current color theme 133 | */ 134 | private ColorTheme colorTheme = ColorTheme.DARK; 135 | /** 136 | * current font size 137 | */ 138 | private FontSize fontSize = FontSize.BIG; 139 | 140 | /** 141 | * Get the current color theme 142 | * @return the current color theme 143 | */ 144 | public ColorTheme getColorTheme() { 145 | return colorTheme; 146 | } 147 | 148 | /** 149 | * Set the color theme 150 | * @param colorTheme the color theme to set 151 | */ 152 | public void setColorTheme(ColorTheme colorTheme) { 153 | this.colorTheme = colorTheme; 154 | } 155 | 156 | /** 157 | * Get the current font size 158 | * @return the current font size 159 | */ 160 | public FontSize getFontSize() { 161 | return fontSize; 162 | } 163 | 164 | /** 165 | * Set the font size 166 | * @param fontSize the font size to set 167 | */ 168 | public void setFontSize(FontSize fontSize) { 169 | this.fontSize = fontSize; 170 | } 171 | 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/main/resources/fxml/LoginPage.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 19 | 25 | 26 | 27 | 36 | 45 | 54 | 64 | 65 | 66 | 72 | 78 | 84 | 90 | 96 | 102 | 108 | 114 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/java/com/nidhal/model/DataBaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.nidhal.model; 2 | 3 | 4 | import com.nidhal.config.DBConfig; 5 | 6 | import javafx.scene.control.Label; 7 | import javafx.scene.control.TextField; 8 | 9 | import java.sql.*; 10 | import java.util.ArrayList; 11 | import java.util.logging.Logger; 12 | 13 | import static com.nidhal.model.Customer.validID; 14 | 15 | /** 16 | * DataBaseConnection is a class that handles connecting the database 17 | * and performing CRUD operations on the "customer_table" using JDBC. 18 | * It contains methods for creating the database and table if they do not exist, adding new customers, 19 | * deleting customers by ID, and getting a connection to the database. 20 | */ 21 | public class DataBaseConnection { 22 | 23 | // Logger 24 | private static final Logger logger = Logger.getLogger(DataBaseConnection.class.getName()); 25 | 26 | public static Connection getDataBaseConnection() throws SQLException { 27 | try { 28 | return DriverManager.getConnection(DBConfig.getUrl(), DBConfig.getUser(), DBConfig.getPassword()); 29 | } catch (SQLException e) { 30 | throw new SQLException("ERROR CONNECTING TO THE DATABASE", e); 31 | } 32 | } 33 | 34 | public static void createDataBaseIfNotExists() { 35 | try (Connection connection = getDataBaseConnection(); Statement statement = connection.createStatement()) { 36 | String sql = "CREATE DATABASE IF NOT EXISTS CustomerDB"; 37 | statement.executeUpdate(sql); 38 | logger.info("DATABASE CREATED SUCCESSFULLY."); 39 | } catch (SQLException e) { 40 | throw new RuntimeException("Error creating database", e); 41 | } 42 | } 43 | 44 | public static void createTableCustomerIfNotExists() { 45 | try (Connection connection = getDataBaseConnection(); 46 | Statement statement = connection.createStatement()) { 47 | String sql = "CREATE TABLE IF NOT EXISTS customer_table (id INT NOT NULL, firstName VARCHAR(16) NOT NULL, lastName VARCHAR(16) NOT NULL, phoneNumber INT(8) NULL, location VARCHAR(32) NOT NULL, PRIMARY KEY (id), UNIQUE INDEX phoneNumber_UNIQUE (phoneNumber))"; 48 | statement.executeUpdate(sql); 49 | logger.info("TABLE CREATED SUCCESSFULLY."); 50 | } catch (SQLException e) { 51 | throw new RuntimeException("Error creating customer table", e); 52 | } 53 | } 54 | 55 | public static void deleteCustomerByID(int id, Label messageLabel, TextField textFieldForId) { 56 | if (!validID(id)) { 57 | logger.warning("INVALID ID:" + id); 58 | messageLabel.setText("ID MUST BE A POSITIVE INTEGER"); 59 | return; 60 | } 61 | 62 | String sql = "DELETE FROM customer_table WHERE id = ?"; 63 | 64 | try (Connection connection = getDataBaseConnection(); 65 | PreparedStatement preparedStatement = connection.prepareStatement(sql)) { 66 | 67 | preparedStatement.setInt(1, id); 68 | int rowsAffected = preparedStatement.executeUpdate(); 69 | if (rowsAffected > 0) { 70 | messageLabel.setText("CUSTOMER DELETED SUCCESSFULLY, ID: " + id); 71 | } else { 72 | messageLabel.setText("CUSTOMER NOT FOUND, ID: " + id); 73 | } 74 | textFieldForId.clear(); 75 | } catch (SQLException ex) { 76 | logger.severe("ERROR DELETING CUSTOMER: " + ex.getMessage()); 77 | messageLabel.setText("ERROR DELETING CUSTOMER, ID: " + id); 78 | } 79 | } 80 | 81 | public static void addNewCustomer(int id, int phoneNumber, String firstName, String lastName, String location, Label messageLabel, TextField textFieldForId) { 82 | if (!validID(id)) { 83 | logger.warning("INVALID ID: " + id); 84 | messageLabel.setText("ID MUST BE A POSITIVE INTEGER"); 85 | return; 86 | } 87 | 88 | String sql = "INSERT INTO customer_table (id, firstName, lastName, phoneNumber, location) VALUES (?, ?, ?, ?, ?)"; 89 | 90 | try (Connection connection = getDataBaseConnection(); 91 | PreparedStatement preparedStatement = connection.prepareStatement(sql)) { 92 | preparedStatement.setInt(1, id); 93 | preparedStatement.setString(2, firstName); 94 | preparedStatement.setString(3, lastName); 95 | preparedStatement.setInt(4, phoneNumber); 96 | preparedStatement.setString(5, location); 97 | preparedStatement.executeUpdate(); 98 | messageLabel.setText("CUSTOMER ADDED SUCCESSFULLY"); 99 | } catch (SQLIntegrityConstraintViolationException e) { 100 | logger.warning("ID NUMBER ALREADY EXISTS: " + id); 101 | messageLabel.setText("ID NUMBER ALREADY EXISTS"); 102 | textFieldForId.clear(); 103 | } catch (SQLException e) { 104 | logger.severe("ERROR ADDING CUSTOMER: " + e.getMessage()); 105 | messageLabel.setText("ERROR ADDING CUSTOMER"); 106 | } 107 | } 108 | 109 | public static ArrayList getAllCustomersFromTheDB() { 110 | ArrayList customerList = new ArrayList<>(); 111 | String sql = "SELECT * FROM customer_table"; 112 | 113 | try (Connection connection = getDataBaseConnection(); Statement statement = connection.createStatement(); ResultSet result = statement.executeQuery(sql)) { 114 | while (result.next()) { 115 | customerList.add(new Customer(result.getInt("id"), result.getString("firstName"), result.getString("lastName"), result.getInt("phoneNumber"), result.getString("location"))); 116 | } 117 | } catch (SQLException e) { 118 | logger.severe("ERROR RETRIEVING DATA: " + e.getMessage()); 119 | } 120 | return customerList; 121 | } 122 | 123 | public static ArrayList getCustomersById(int id) { 124 | ArrayList customerList = new ArrayList<>(); 125 | String sql = "SELECT * FROM customer_table WHERE id = ?"; 126 | 127 | try (Connection connection = getDataBaseConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { 128 | preparedStatement.setInt(1, id); 129 | try (ResultSet result = preparedStatement.executeQuery()) { 130 | while (result.next()) { 131 | customerList.add(new Customer(result.getInt("id"), result.getString("firstName"), result.getString("lastName"), result.getInt("phoneNumber"), result.getString("location"))); 132 | } 133 | } 134 | } catch (SQLException e) { 135 | logger.severe("ERROR RETRIEVING DATA: " + e.getMessage()); 136 | } 137 | return customerList; 138 | } 139 | } --------------------------------------------------------------------------------