diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java
index b9d67da..3aea332 100644
--- a/src/main/java/edu/group5/app/App.java
+++ b/src/main/java/edu/group5/app/App.java
@@ -11,6 +11,7 @@
import edu.group5.app.model.user.UserService;
import edu.group5.app.model.wrapper.DbWrapper;
import edu.group5.app.model.wrapper.OrgApiWrapper;
+import edu.group5.app.view.aboutuspage.AboutUsView;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
@@ -35,6 +36,7 @@ public class App extends Application {
NavigationController nav;
private Logger logger;
+ static int MAX_RETRIES = 3;
@Override
public void init() {
@@ -44,8 +46,14 @@ public void init() {
this.dbWrapper = new DbWrapper(false);
OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all");
- while (!dbWrapper.connect()) {
+ int retries = 0;
+ while (!dbWrapper.connect() && retries < MAX_RETRIES) {
this.logger.warning("Failed to connect to database");
+ retries++;
+ }
+ if (retries == MAX_RETRIES) {
+ this.logger.severe("Failed to connect to database after " + MAX_RETRIES + " attempts");
+ throw new RuntimeException("Failed to connect to database");
}
// Load data from database
@@ -75,7 +83,7 @@ public void init() {
OrganizationService organizationService = new OrganizationService(organizationRepository, orgScraper);
this.root = new BorderPane();
this.appState = new AppState();
- this.nav = new NavigationController(root, appState, userService, donationService, organizationService);
+ this.nav = new NavigationController(root, appState, userService, donationService, organizationService, this.getHostServices());
}
@Override
diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java
index 249a436..7c34afd 100644
--- a/src/main/java/edu/group5/app/control/AuthController.java
+++ b/src/main/java/edu/group5/app/control/AuthController.java
@@ -3,14 +3,12 @@
import edu.group5.app.model.AppState;
import edu.group5.app.model.user.User;
import edu.group5.app.model.user.UserService;
+import edu.group5.app.utils.ParameterValidator;
import edu.group5.app.view.loginpage.LoginPageView;
import edu.group5.app.view.loginpage.SignUpPageView;
import javafx.scene.control.Alert;
-import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
-import java.util.Arrays;
-
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
@@ -31,11 +29,31 @@ public class AuthController {
private final UserService userService;
public AuthController(AppState appState, NavigationController nav, UserService userService) {
+ ParameterValidator.objectChecker(appState, "AppState");
+ ParameterValidator.objectChecker(nav, "NavigationController");
+ ParameterValidator.objectChecker(userService, "UserService");
this.appState = appState;
this.nav = nav;
this.userService = userService;
}
+
+ /**
+ * Sets the current logged-in user.
+ * @param user the user to set as current
+ */
+ public void setCurrentUser(User user) {
+ appState.setCurrentUser(user);
+ }
+
+ /**
+ * Gets the current logged-in user.
+ * @return the current user, or null if no user logged in
+ */
+ public User getCurrentUser() {
+ return appState.getCurrentUser();
+ }
+
/**
* Handles the registration of a {@link User}.
*
@@ -69,7 +87,7 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName,
// Clears password char array after creating a hash.
String hashedPassword = encoder.encode(new String(passwordChars));
for (int i = 0; i < passwordChars.length; i++) {
- passwordChars[0] = '0';
+ passwordChars[i] = '\u0000';
}
Alert privacyPolicy = new Alert(Alert.AlertType.CONFIRMATION);
@@ -86,9 +104,8 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName,
if (success) {
User user = userService.getUserByEmail(email);
-
- appState.setCurrentUser(user);
- nav.showHomePage();
+ appState.setCurrentUser(user);
+ nav.showHomePage();
} else {
view.showError("Registration failed. Email may already be in use.");
}
diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java
index 8a8ec79..07af6fd 100644
--- a/src/main/java/edu/group5/app/control/DonationController.java
+++ b/src/main/java/edu/group5/app/control/DonationController.java
@@ -6,6 +6,7 @@
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.user.Customer;
import edu.group5.app.model.user.User;
+import edu.group5.app.utils.ParameterValidator;
import java.math.BigDecimal;
import java.util.HashSet;
@@ -34,11 +35,46 @@ public class DonationController {
private final DonationService service;
public DonationController(AppState appState, NavigationController nav, DonationService service) {
+ ParameterValidator.objectChecker(appState, "AppState");
+ ParameterValidator.objectChecker(nav, "NavigationController");
+ ParameterValidator.objectChecker(service, "DonationService");
this.appState = appState;
this.nav = nav;
this.service = service;
}
+ /**
+ * Sets the current donation amount.
+ * @param amount the amount to donate
+ */
+ public void setDonationAmount(BigDecimal amount) {
+ appState.setCurrentDonationAmount(amount);
+ }
+
+ /**
+ * Gets the current donation amount.
+ * @return the donation amount
+ */
+ public BigDecimal getDonationAmount() {
+ return appState.getCurrentDonationAmount();
+ }
+
+ /**
+ * Sets the current payment method.
+ * @param paymentMethod the payment method
+ */
+ public void setPaymentMethod(String paymentMethod) {
+ appState.setCurrentPaymentMethod(paymentMethod);
+ }
+
+ /**
+ * Gets the current payment method.
+ * @return the payment method
+ */
+ public String getPaymentMethod() {
+ return appState.getCurrentPaymentMethod();
+ }
+
/**
* Retrieves all donations made by a specific user.
*
@@ -46,7 +82,8 @@ public DonationController(AppState appState, NavigationController nav, DonationS
* @return a map of donations.
*/
public Map getUserDonations(int userId) {
- return service.getDonationRepository().filterByUser(userId);
+ ParameterValidator.intChecker(userId, "User ID");
+ return service.getUserDonations(userId);
}
/**
@@ -56,7 +93,11 @@ public Map getUserDonations(int userId) {
* @return a set of organization IDs
*/
public Set getUniqueOrganizationIDs() {
- Map userDonations = getUserDonations(appState.getCurrentUser().getUserId());
+ User currentUser = appState.getCurrentUser();
+ if (currentUser == null) {
+ throw new IllegalStateException("No user logged in");
+ }
+ Map userDonations = getUserDonations(currentUser.getUserId());
Set uniqueOrganizations = new HashSet<>();
for (Donation donation : userDonations.values()) {
@@ -146,19 +187,26 @@ private void handleDonate() {
);
if (success) {
- System.out.println("Donation created: " + amount + " kr to " + currentOrg.name() + ", with payment method: " + paymentMethod);
+ System.out.println("Donation created: "
+ + amount + " kr to " + currentOrg.name()
+ + ", with payment method: " + paymentMethod);
} else {
System.err.println("Failed to create donation");
}
// Clear donation session state
appState.setCurrentDonationAmount(null);
+ // Clear org
+ appState.setCurrentOrganization(null);
+ // Clear payment method
+ appState.setCurrentPaymentMethod(null);
// Navigate to payment complete
nav.showPaymentCompletePage();
}
private void showError(String message) {
+ ParameterValidator.stringChecker(message, "message");
Alert errorAlert = new Alert(Alert.AlertType.WARNING);
errorAlert.setTitle("Donation Error");
errorAlert.setHeaderText("Cannot Process Donation");
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index 7bfbc8c..8bbbbb3 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -4,7 +4,9 @@
import edu.group5.app.model.donation.DonationService;
import edu.group5.app.model.organization.OrganizationService;
import edu.group5.app.model.user.UserService;
+import edu.group5.app.utils.ParameterValidator;
import edu.group5.app.view.Header;
+import edu.group5.app.view.aboutuspage.AboutUsView;
import edu.group5.app.view.causespage.CausesPageView;
import edu.group5.app.view.donationpage.DonationPageView;
import edu.group5.app.view.donationpage.PaymentCompletePageView;
@@ -14,10 +16,23 @@
import edu.group5.app.view.loginpage.SignUpPageView;
import edu.group5.app.view.organizationpage.OrganizationPageView;
import edu.group5.app.view.userpage.UserPageView;
+import javafx.application.HostServices;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.VBox;
/**
- * Controller responsible for navigating between views within the root node.
+ * Controller responsible for handling navigation between different pages of the application.
+ * Coordinates between {@link AppState}, {@link AuthController}, {@link DonationController},
+ * {@link OrganizationController} and the various views to manage page transitions and state updates.
+ *
+ * Provides methods to navigate to the home page, login page, sign-up page, causes page,
+ * organization page, donation page, payment complete page and user profile page. Each method updates the
+ * top header and center content of the main application layout accordingly.
+ *
*/
public class NavigationController {
private final BorderPane root;
@@ -25,64 +40,111 @@ public class NavigationController {
private final LoginHeader loginHeader;
private final AppState appState;
+ private final HostServices hostServices;
private final AuthController authController;
private final DonationController donationController;
private final OrganizationController organizationController;
- public NavigationController(BorderPane root, AppState appState, UserService userService, DonationService donationService, OrganizationService organizationService) {
+ public NavigationController(BorderPane root, AppState appState, UserService userService,
+ DonationService donationService, OrganizationService organizationService, HostServices hostServices) {
+ ParameterValidator.objectChecker(root, "Root BorderPane");
+ ParameterValidator.objectChecker(appState, "AppState");
+ ParameterValidator.objectChecker(userService, "UserService");
+ ParameterValidator.objectChecker(donationService, "DonationService");
+ ParameterValidator.objectChecker(organizationService, "OrganizationService");
+ ParameterValidator.objectChecker(hostServices, "HostServices");
+
this.root = root;
this.header = new Header(this);
this.loginHeader = new LoginHeader();
-
this.appState = appState;
+ this.hostServices = hostServices;
this.authController = new AuthController(appState, this, userService);
this.donationController = new DonationController(appState, this, donationService);
- this.organizationController = new OrganizationController(organizationService);
+ this.organizationController = new OrganizationController(appState, organizationService);
}
+ /**
+ * Navigates to the home page by setting the top header and center content of the main layout.
+ * The home page serves as the landing page of the application, providing an overview and access to various features.
+ */
public void showHomePage() {
root.setTop(header);
- root.setCenter(new HomePageView(appState, this));
+ root.setCenter(new HomePageView(this));
}
+ /**
+ * Navigates to the login page by setting the top header and center content of the main layout.
+ * The login page allows existing users to enter their credentials and access their account.
+ */
public void showLoginPage() {
root.setTop(loginHeader);
- root.setCenter(new LoginPageView(appState, this, authController));
+ root.setCenter(new LoginPageView(this, authController));
}
+ /**
+ * Navigates to the sign-up page by setting the top header and center content of the main layout.
+ * The sign-up page allows new users to create an account by providing their details.
+ */
public void showSignUpPage() {
root.setTop(loginHeader);
- root.setCenter(new SignUpPageView(appState, this, authController));
+ root.setCenter(new SignUpPageView(this, authController));
}
+ /**
+ * Navigates to the payment complete page by setting the top header and center content of the main layout.
+ * The payment complete page confirms the successful completion of a donation transaction.
+ */
public void showPaymentCompletePage() {
root.setTop(header);
root.setCenter(new PaymentCompletePageView(this));
}
+ /**
+ * Navigates to the causes page by setting the top header and center content of the main layout.
+ * The causes page allows users to browse and search for organizations they may want to donate to.
+ */
public void showCausesPage() {
root.setTop(header);
- root.setCenter(new CausesPageView(appState, this, organizationController));
+ root.setCenter(new CausesPageView(this, organizationController));
}
+ /**
+ * Navigates to the organization page by setting the top header and center content of the main layout.
+ * The organization page provides detailed information about a specific organization, including its mission,
+ * impact, and donation options, allowing users to learn more before making a donation.
+ */
public void showOrganizationPage() {
root.setTop(header);
- root.setCenter(new OrganizationPageView(appState, this, donationController));
+ root.setCenter(new OrganizationPageView(this, organizationController, donationController));
}
+ /**
+ * Navigates to the donation page by setting the top header and center content of the main layout.
+ * The donation page allows users to make new donations to selected organizations.
+ */
public void showDonationPage() {
root.setTop(header);
- root.setCenter(new DonationPageView(appState, this, donationController));
+ root.setCenter(new DonationPageView(this, donationController));
}
+ /**
+ * Displays an "About Us" dialog with information about the application and its developers.
+ * The dialog includes a description of the app's mission and a hyperlink to the project's GitHub repository
+ */
public void showAboutUsPage() {
- root.setTop(header);
+ new AboutUsView(hostServices).displayAboutUs();
}
+ /**
+ * Navigates to the user profile page by setting the top header and center content of the main layout.
+ * The user profile page allows users to view and manage their account information,
+ * donation history, and other personalized features.
+ */
public void showUserPage() {
root.setTop(header);
- root.setCenter(new UserPageView(appState, this, authController, donationController, organizationController));
+ root.setCenter(new UserPageView(this, authController, donationController, organizationController));
}
}
diff --git a/src/main/java/edu/group5/app/control/OrganizationController.java b/src/main/java/edu/group5/app/control/OrganizationController.java
index fb65127..6ffe830 100644
--- a/src/main/java/edu/group5/app/control/OrganizationController.java
+++ b/src/main/java/edu/group5/app/control/OrganizationController.java
@@ -1,22 +1,54 @@
package edu.group5.app.control;
+import edu.group5.app.model.AppState;
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.organization.OrganizationService;
+import edu.group5.app.utils.ParameterValidator;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* Controller responsible for organization-related operations.
+ *
+ *
+ * Coordinates with {@link OrganizationService} to:
+ *
+ *
Retrieve organization information by ID
+ *
Retrieve all trusted organizations
+ *
+ *
*/
public class OrganizationController {
+ private final AppState appState;
private final OrganizationService service;
- public OrganizationController(OrganizationService service) {
+ public OrganizationController(AppState appState, OrganizationService service) {
+ ParameterValidator.objectChecker(appState, "AppState");
+ ParameterValidator.objectChecker(service, "OrganizationService");
+ this.appState = appState;
this.service = service;
}
+
+ /**
+ * Sets the current selected organization.
+ * @param organization the organization to set as current
+ */
+ public void setCurrentOrganization(Organization org) {
+ appState.setCurrentOrganization(org);
+ }
+
+ /**
+ * Gets the current selected organization.
+ * @return the current organization, or null if none selected
+ */
+ public Organization getCurrentOrganization() {
+ return appState.getCurrentOrganization();
+ }
+
public Organization getOrganizationById(int orgId) {
+ ParameterValidator.intChecker(orgId, "Organization ID");
return service.findByOrgNumber(orgId);
}
diff --git a/src/main/java/edu/group5/app/model/AppState.java b/src/main/java/edu/group5/app/model/AppState.java
index 73d5cfc..bd56ba0 100644
--- a/src/main/java/edu/group5/app/model/AppState.java
+++ b/src/main/java/edu/group5/app/model/AppState.java
@@ -4,41 +4,82 @@
import edu.group5.app.model.user.User;
import java.math.BigDecimal;
-
+/**
+ * AppState class represents the current state of the application, including:
+ *
the current user
+ *
the selected organization
+ *
current donation amount
+ *
current payment method
+ *
+ * It serves as a centralized data store for the application's
+ * stateful information, allowing different components of the application to access and modify this information as needed.
+ */
public class AppState {
private User currentUser;
private BigDecimal currentDonationAmount;
private Organization currentOrganization;
private String currentDonation;
+ /**
+ * Gets the current user of the application.
+ * @return the current user
+ */
public User getCurrentUser() {
return this.currentUser;
}
+ /**
+ * Sets the current user of the application.
+ * @param user the user to set as the current user
+ */
public void setCurrentUser(User user) {
this.currentUser = user;
}
+ /**
+ * Gets the selected organization.
+ * @return the selected organization
+ */
public Organization getCurrentOrganization() {
return this.currentOrganization;
}
+ /**
+ * Sets the selected organization.
+ * @param organization the organization to set as the current organization
+ */
public void setCurrentOrganization(Organization organization) {
this.currentOrganization = organization;
}
+ /**
+ * Gets the current donation amount.
+ * @return the current donation amount
+ */
public BigDecimal getCurrentDonationAmount() {
return this.currentDonationAmount;
}
+ /**
+ * Sets the current donation amount.
+ * @param amount the amount to set as the current donation amount
+ */
public void setCurrentDonationAmount(BigDecimal amount) {
this.currentDonationAmount = amount;
}
+ /**
+ * Gets the current payment method.
+ * @return the current payment method
+ */
public String getCurrentPaymentMethod() {
return this.currentDonation;
}
+ /**
+ * Sets the current payment method.
+ * @param paymentMethod the payment method to set as the current payment method
+ */
public void setCurrentPaymentMethod(String paymentMethod){
this.currentDonation = paymentMethod;
}
diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java
index 3cd1fa9..d36d0e2 100644
--- a/src/main/java/edu/group5/app/model/DBRepository.java
+++ b/src/main/java/edu/group5/app/model/DBRepository.java
@@ -2,6 +2,9 @@
import java.util.HashMap;
import java.util.Map;
+
+import edu.group5.app.utils.ParameterValidator;
+
import java.util.List;
/**
@@ -22,12 +25,24 @@ public abstract class DBRepository extends Repository {
* @param content the HashMap used to store repository entities
*/
protected DBRepository(Map content) {
+ ParameterValidator.objectChecker(content, "Content");
super(content);
this.contentLock = new HashMap<>();
}
+ /**
+ * Updates the content lock with the current state of the content.
+ * This method should be called whenever the content is modified to ensure
+ * that the content lock reflects the latest state of the repository.
+ */
protected abstract void updateContentLock();
+ /**
+ * Adds a new entity to the repository content.
+ * @param value the entity to be added to the repository
+ * @return true if the entity was successfully added, false otherwise
+ * @throws IllegalArgumentException if the value is null
+ */
public abstract boolean addContent(V value);
/**
diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java
index 032f307..6dd425e 100644
--- a/src/main/java/edu/group5/app/model/Repository.java
+++ b/src/main/java/edu/group5/app/model/Repository.java
@@ -2,8 +2,15 @@
import java.util.Map;
+import edu.group5.app.utils.ParameterValidator;
+
/**
- * Represents a repository.
+ * Abstract base class for repositories.
+ *
+ * Repositories are responsible for managing collections of entities, providing
+ * basic operations for accessing and manipulating these entities. The specific
+ * type of entities and the underlying data structure are defined by subclasses.
+ *
*/
public abstract class Repository {
protected final Map content;
@@ -13,12 +20,13 @@ public abstract class Repository {
* @param content the underlying data structure used to store entities
*/
protected Repository(Map content) {
+ ParameterValidator.objectChecker(content, "content");
this.content = content;
}
/**
- * Gets the content of the repo
- * @return content of the repo
+ * Gets the content of the repository.
+ * @return the content of the repository
*/
public Map getContent() {
return content;
diff --git a/src/main/java/edu/group5/app/model/donation/DonationRepository.java b/src/main/java/edu/group5/app/model/donation/DonationRepository.java
index a55ea74..f1102ba 100644
--- a/src/main/java/edu/group5/app/model/donation/DonationRepository.java
+++ b/src/main/java/edu/group5/app/model/donation/DonationRepository.java
@@ -1,6 +1,7 @@
package edu.group5.app.model.donation;
import edu.group5.app.model.DBRepository;
+import edu.group5.app.utils.ParameterValidator;
import java.util.Comparator;
import java.util.HashMap;
@@ -32,9 +33,7 @@ public class DonationRepository extends DBRepository {
*/
public DonationRepository(List