From b91eecd83f462925586c590b55d74c0e7022fe0a Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Thu, 16 Apr 2026 13:43:38 +0200
Subject: [PATCH 01/10] Update[ParameterValidator]: Update codebase to use
ParameterValidator class where it suits the best
---
src/main/java/edu/group5/app/App.java | 9 ++++++-
.../group5/app/control/AuthController.java | 11 +++++---
.../app/control/DonationController.java | 20 +++++++++++++--
.../app/control/NavigationController.java | 10 +++++++-
.../app/control/OrganizationController.java | 4 +++
.../edu/group5/app/model/DBRepository.java | 4 +++
.../java/edu/group5/app/model/Repository.java | 3 +++
.../model/donation/DonationRepository.java | 23 ++++++-----------
.../app/model/donation/DonationService.java | 10 +++-----
.../app/model/organization/Organization.java | 6 ++---
.../organization/OrganizationRepository.java | 22 ++++++++--------
.../organization/OrganizationScraper.java | 18 +++----------
.../organization/OrganizationService.java | 12 ++++-----
.../edu/group5/app/model/user/Customer.java | 11 ++++----
.../java/edu/group5/app/model/user/User.java | 25 ++++++++-----------
.../group5/app/model/user/UserRepository.java | 19 +++++---------
.../group5/app/model/user/UserService.java | 10 +++-----
.../group5/app/utils/ParameterValidator.java | 4 +--
.../app/view/causespage/CausesPageView.java | 5 ++++
.../app/view/causespage/OrganizationCard.java | 1 +
.../view/donationpage/DonationPageView.java | 5 ++++
21 files changed, 123 insertions(+), 109 deletions(-)
diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java
index b9d67da..6402156 100644
--- a/src/main/java/edu/group5/app/App.java
+++ b/src/main/java/edu/group5/app/App.java
@@ -35,6 +35,7 @@ public class App extends Application {
NavigationController nav;
private Logger logger;
+ static int MAX_ReTRIES = 3;
@Override
public void init() {
@@ -44,8 +45,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
diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java
index 249a436..4911a3b 100644
--- a/src/main/java/edu/group5/app/control/AuthController.java
+++ b/src/main/java/edu/group5/app/control/AuthController.java
@@ -3,6 +3,7 @@
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;
@@ -31,6 +32,9 @@ 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;
@@ -69,7 +73,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 +90,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..61ab172 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,6 +35,9 @@ 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;
@@ -46,6 +50,7 @@ public DonationController(AppState appState, NavigationController nav, DonationS
* @return a map of donations.
*/
public Map getUserDonations(int userId) {
+ ParameterValidator.intChecker(userId, "User ID");
return service.getDonationRepository().filterByUser(userId);
}
@@ -56,7 +61,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 +155,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..326389b 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -4,6 +4,7 @@
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.causespage.CausesPageView;
import edu.group5.app.view.donationpage.DonationPageView;
@@ -30,7 +31,14 @@ public class NavigationController {
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) {
+ ParameterValidator.objectChecker(root, "Root BorderPane");
+ ParameterValidator.objectChecker(appState, "AppState");
+ ParameterValidator.objectChecker(userService, "UserService");
+ ParameterValidator.objectChecker(donationService, "DonationService");
+ ParameterValidator.objectChecker(organizationService, "OrganizationService");
+
this.root = root;
this.header = new Header(this);
this.loginHeader = new LoginHeader();
diff --git a/src/main/java/edu/group5/app/control/OrganizationController.java b/src/main/java/edu/group5/app/control/OrganizationController.java
index fb65127..fc1252f 100644
--- a/src/main/java/edu/group5/app/control/OrganizationController.java
+++ b/src/main/java/edu/group5/app/control/OrganizationController.java
@@ -2,6 +2,8 @@
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.organization.OrganizationService;
+import edu.group5.app.utils.ParameterValidator;
+import tools.jackson.databind.deser.bean.CreatorCandidate.Param;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -13,10 +15,12 @@ public class OrganizationController {
private final OrganizationService service;
public OrganizationController(OrganizationService service) {
+ ParameterValidator.objectChecker(service, "OrganizationService");
this.service = service;
}
public Organization getOrganizationById(int orgId) {
+ ParameterValidator.intChecker(orgId, "Organization ID");
return service.findByOrgNumber(orgId);
}
diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java
index 3cd1fa9..88ac3d9 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,6 +25,7 @@ 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<>();
}
diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java
index 032f307..64d4b90 100644
--- a/src/main/java/edu/group5/app/model/Repository.java
+++ b/src/main/java/edu/group5/app/model/Repository.java
@@ -2,6 +2,8 @@
import java.util.Map;
+import edu.group5.app.utils.ParameterValidator;
+
/**
* Represents a repository.
*/
@@ -13,6 +15,7 @@ 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;
}
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
*/
public class OrganizationController {
private final OrganizationService service;
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 88ac3d9..d36d0e2 100644
--- a/src/main/java/edu/group5/app/model/DBRepository.java
+++ b/src/main/java/edu/group5/app/model/DBRepository.java
@@ -30,8 +30,19 @@ protected DBRepository(Map 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 64d4b90..6dd425e 100644
--- a/src/main/java/edu/group5/app/model/Repository.java
+++ b/src/main/java/edu/group5/app/model/Repository.java
@@ -5,7 +5,12 @@
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;
@@ -20,8 +25,8 @@ protected Repository(Map 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/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
index acb1110..d0c84a3 100644
--- a/src/main/java/edu/group5/app/model/organization/OrganizationService.java
+++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
@@ -3,7 +3,6 @@
import java.util.stream.Collectors;
import edu.group5.app.utils.ParameterValidator;
-import tools.jackson.databind.deser.bean.CreatorCandidate.Param;
import java.util.Comparator;
import java.util.LinkedHashMap;
diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
index 53c623b..2268d4d 100644
--- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
+++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
@@ -9,7 +9,6 @@
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
-import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
From c67227b549712138cbaf56e2c00e91c22c336cb8 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Thu, 16 Apr 2026 17:39:39 +0200
Subject: [PATCH 03/10] update[control]: Update controller to handle control of
the appstate
---
.../group5/app/control/AuthController.java | 18 +++++++++++
.../app/control/DonationController.java | 32 +++++++++++++++++++
.../app/control/NavigationController.java | 2 +-
.../app/control/OrganizationController.java | 23 ++++++++++++-
4 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java
index 291e0df..c2f923b 100644
--- a/src/main/java/edu/group5/app/control/AuthController.java
+++ b/src/main/java/edu/group5/app/control/AuthController.java
@@ -1,6 +1,7 @@
package edu.group5.app.control;
import edu.group5.app.model.AppState;
+import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.user.User;
import edu.group5.app.model.user.UserService;
import edu.group5.app.utils.ParameterValidator;
@@ -37,6 +38,23 @@ public AuthController(AppState appState, NavigationController nav, UserService u
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}.
*
diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java
index 61ab172..70d6444 100644
--- a/src/main/java/edu/group5/app/control/DonationController.java
+++ b/src/main/java/edu/group5/app/control/DonationController.java
@@ -43,6 +43,38 @@ public DonationController(AppState appState, NavigationController nav, DonationS
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.
*
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index d94bb53..04fb5e2 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -54,7 +54,7 @@ public NavigationController(BorderPane root, AppState appState, UserService user
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);
}
/**
diff --git a/src/main/java/edu/group5/app/control/OrganizationController.java b/src/main/java/edu/group5/app/control/OrganizationController.java
index 064aab2..6ffe830 100644
--- a/src/main/java/edu/group5/app/control/OrganizationController.java
+++ b/src/main/java/edu/group5/app/control/OrganizationController.java
@@ -1,5 +1,6 @@
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;
@@ -19,13 +20,33 @@
*
*/
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);
From 92b41fd180d67c8c1d6adc6bd82ff34100130a1f Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Thu, 16 Apr 2026 18:28:56 +0200
Subject: [PATCH 04/10] refactor: implement proper MVC architecture with
controller-service abstraction
---
.../group5/app/control/AuthController.java | 1 -
.../app/control/DonationController.java | 2 +-
.../app/control/NavigationController.java | 12 +++++------
.../app/model/donation/DonationService.java | 21 ++++++++++---------
.../organization/OrganizationService.java | 8 -------
.../java/edu/group5/app/model/user/User.java | 1 -
.../group5/app/model/user/UserService.java | 11 ----------
.../app/view/causespage/CausesPageView.java | 11 ++++------
.../app/view/causespage/OrganizationCard.java | 12 +++++------
.../view/donationpage/DonationPageView.java | 10 +++------
.../app/view/homepage/HomePageView.java | 5 +----
.../app/view/loginpage/LoginPageView.java | 5 +----
.../app/view/loginpage/SignUpPageView.java | 5 +----
.../OrganizationPageView.java | 19 ++++++++++-------
.../app/view/userpage/UserPageView.java | 11 +++++-----
.../model/donation/DonationServiceTest.java | 10 ---------
.../organization/OrganizationServiceTest.java | 5 -----
.../app/model/user/UserServiceTest.java | 5 -----
18 files changed, 51 insertions(+), 103 deletions(-)
diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java
index c2f923b..7c34afd 100644
--- a/src/main/java/edu/group5/app/control/AuthController.java
+++ b/src/main/java/edu/group5/app/control/AuthController.java
@@ -1,7 +1,6 @@
package edu.group5.app.control;
import edu.group5.app.model.AppState;
-import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.user.User;
import edu.group5.app.model.user.UserService;
import edu.group5.app.utils.ParameterValidator;
diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java
index 70d6444..07af6fd 100644
--- a/src/main/java/edu/group5/app/control/DonationController.java
+++ b/src/main/java/edu/group5/app/control/DonationController.java
@@ -83,7 +83,7 @@ public String getPaymentMethod() {
*/
public Map getUserDonations(int userId) {
ParameterValidator.intChecker(userId, "User ID");
- return service.getDonationRepository().filterByUser(userId);
+ return service.getUserDonations(userId);
}
/**
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index 04fb5e2..92ea26a 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -63,7 +63,7 @@ public NavigationController(BorderPane root, AppState appState, UserService user
*/
public void showHomePage() {
root.setTop(header);
- root.setCenter(new HomePageView(appState, this));
+ root.setCenter(new HomePageView(this));
}
/**
@@ -72,7 +72,7 @@ public void showHomePage() {
*/
public void showLoginPage() {
root.setTop(loginHeader);
- root.setCenter(new LoginPageView(appState, this, authController));
+ root.setCenter(new LoginPageView(this, authController));
}
/**
@@ -81,7 +81,7 @@ public void showLoginPage() {
*/
public void showSignUpPage() {
root.setTop(loginHeader);
- root.setCenter(new SignUpPageView(appState, this, authController));
+ root.setCenter(new SignUpPageView(this, authController));
}
/**
@@ -99,7 +99,7 @@ public void showPaymentCompletePage() {
*/
public void showCausesPage() {
root.setTop(header);
- root.setCenter(new CausesPageView(appState, this, organizationController));
+ root.setCenter(new CausesPageView(this, organizationController));
}
/**
@@ -109,7 +109,7 @@ public void showCausesPage() {
*/
public void showOrganizationPage() {
root.setTop(header);
- root.setCenter(new OrganizationPageView(appState, this, donationController));
+ root.setCenter(new OrganizationPageView(this, organizationController, donationController));
}
/**
@@ -118,7 +118,7 @@ public void showOrganizationPage() {
*/
public void showDonationPage() {
root.setTop(header);
- root.setCenter(new DonationPageView(appState, this, donationController));
+ root.setCenter(new DonationPageView(this, donationController));
}
public void showAboutUsPage() {
diff --git a/src/main/java/edu/group5/app/model/donation/DonationService.java b/src/main/java/edu/group5/app/model/donation/DonationService.java
index 6f978b1..5eb964b 100644
--- a/src/main/java/edu/group5/app/model/donation/DonationService.java
+++ b/src/main/java/edu/group5/app/model/donation/DonationService.java
@@ -1,5 +1,6 @@
package edu.group5.app.model.donation;
import java.time.Instant;
+import java.util.Map;
import java.math.BigDecimal;
import java.sql.Timestamp;
import edu.group5.app.model.organization.Organization;
@@ -34,21 +35,21 @@ public DonationService(DonationRepository donationRepository,
}
/**
- * Getter for the DonationRepository used by this service.
- * This method allows access to the donation repository for managing donation records and retrieving donation information.
- * @return the DonationRepository instance used by this service
+ * Gets all donations made by a specific user.
+ * @param userId the ID of the user
+ * @return a map of donations made by that user
*/
- public DonationRepository getDonationRepository() {
- return this.donationRepository;
+ public Map getUserDonations(int userId) {
+ return donationRepository.filterByUser(userId);
}
/**
- * Getter for the OrganizationRepository used by this service.
- * This method allows access to the organization repository for validating organization information when processing donations.
- * @return the OrganizationRepository instance used by this service
+ * Gets all donations to a specific organization.
+ * @param organizationId the organization ID
+ * @return map of donations to that organization
*/
- public OrganizationRepository getOrganizationRepository() {
- return this.organizationRepository;
+ public Map getOrganizationDonations(int organizationId) {
+ return donationRepository.filterByOrganization(organizationId);
}
/**
diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
index d0c84a3..df4d04e 100644
--- a/src/main/java/edu/group5/app/model/organization/OrganizationService.java
+++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
@@ -35,14 +35,6 @@ public OrganizationService(OrganizationRepository organizationRepository, Organi
this.scraper = scraper;
}
- /**
- * Getter for the OrganizationRepository used by this service.
- * @return the OrganizationRepository instance used by this service
- */
- public OrganizationRepository getOrganizationRepository() {
- return this.organizationRepository;
- }
-
/**
* Retrieves all trusted organizations.
* @return a map of trusted organizations by organization number
diff --git a/src/main/java/edu/group5/app/model/user/User.java b/src/main/java/edu/group5/app/model/user/User.java
index 54c9e16..806f1f7 100644
--- a/src/main/java/edu/group5/app/model/user/User.java
+++ b/src/main/java/edu/group5/app/model/user/User.java
@@ -1,5 +1,4 @@
package edu.group5.app.model.user;
-import java.lang.reflect.Parameter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
diff --git a/src/main/java/edu/group5/app/model/user/UserService.java b/src/main/java/edu/group5/app/model/user/UserService.java
index a6d316e..9d215d0 100644
--- a/src/main/java/edu/group5/app/model/user/UserService.java
+++ b/src/main/java/edu/group5/app/model/user/UserService.java
@@ -25,17 +25,6 @@ public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
- /**
- * Getter for the UserRepository used by this service.
- * This method allows access to the user repository for managing user data and
- * performing operations such as registration and login.
- *
- * @return the UserRepository instance used by this service
- */
- public UserRepository getUserRepository() {
- return this.userRepository;
- }
-
/**
* Registers a new user with the given information. Validates the input data and
* creates a new User object
diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
index 346df19..271bfa9 100644
--- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
+++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
@@ -34,19 +34,16 @@
* a fallback "no image" is displayed.
*/
public class CausesPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
private final OrganizationController orgController;
private GridPane organizationGrid;
private Map allOrganizations;
- public CausesPageView(AppState appState, NavigationController nav, OrganizationController orgController) {
- ParameterValidator.objectChecker(appState, "AppState");
+ public CausesPageView(NavigationController nav, OrganizationController orgController) {
ParameterValidator.objectChecker(nav, "NavigationController");
ParameterValidator.objectChecker(orgController, "OrganizationController");
- this.appState = appState;
this.nav = nav;
this.orgController = orgController;
@@ -94,9 +91,9 @@ private BorderPane createBody() {
card.updateLogo(entry.getValue().logoUrl());
}
}
- Organization currentOrg = appState.getCurrentOrganization();
+ Organization currentOrg = orgController.getCurrentOrganization();
if (currentOrg != null && orgs.containsKey(currentOrg.orgNumber())) {
- appState.setCurrentOrganization(orgs.get(currentOrg.orgNumber()));
+ orgController.setCurrentOrganization(orgs.get(currentOrg.orgNumber()));
}
});
});
@@ -165,7 +162,7 @@ private GridPane createOrganizationSection(String searchTerm) {
? org.logoUrl()
: null;
- OrganizationCard card = new OrganizationCard(appState, nav, org, img);
+ OrganizationCard card = new OrganizationCard(nav, orgController, org, img);
grid.add(card, column, row);
diff --git a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
index c24b32a..4645d0b 100644
--- a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
+++ b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
@@ -1,9 +1,8 @@
package edu.group5.app.view.causespage;
import edu.group5.app.control.NavigationController;
-import edu.group5.app.model.AppState;
+import edu.group5.app.control.OrganizationController;
import edu.group5.app.model.organization.Organization;
-import edu.group5.app.utils.ParameterValidator;
import javafx.geometry.Pos;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
@@ -22,15 +21,16 @@
* to the organization's detail page.
*/
public class OrganizationCard extends VBox {
- private final AppState appState;
private final Organization organization;
private final NavigationController nav;
+ private final OrganizationController organizationController;
private StackPane imageContainer;
private String currentLogoUrl;
- public OrganizationCard(AppState appstate, NavigationController nav, Organization org, String img) {
- this.appState = appstate;
+ public OrganizationCard(NavigationController nav, OrganizationController organizationController,
+ Organization org, String img) {
this.nav = nav;
+ this.organizationController = organizationController;
this.organization = org;
setId("mainContainer");
getStylesheets().add(getClass().getResource("/browsepage/browse_org.css").toExternalForm());
@@ -43,7 +43,7 @@ public OrganizationCard(AppState appstate, NavigationController nav, Organizatio
);
setOnMouseClicked(e -> {
- appstate.setCurrentOrganization(getOrganizationWithCurrentLogo());
+ organizationController.setCurrentOrganization(getOrganizationWithCurrentLogo());
nav.showOrganizationPage();
});
diff --git a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java
index 61ca29c..ed0288c 100644
--- a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java
+++ b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java
@@ -2,7 +2,6 @@
import edu.group5.app.control.DonationController;
import edu.group5.app.control.NavigationController;
-import edu.group5.app.model.AppState;
import edu.group5.app.utils.ParameterValidator;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -28,7 +27,6 @@
* payment method buttons, donation button, and a back to organization page button.
*/
public class DonationPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
private final DonationController donationController;
@@ -37,12 +35,10 @@ public class DonationPageView extends BorderPane {
private Node selectedPaymentMethod = null;
private Button donateBtn;
- public DonationPageView(AppState appState, NavigationController nav, DonationController donationController) {
- ParameterValidator.objectChecker(appState, "AppState");
+ public DonationPageView(NavigationController nav, DonationController donationController) {
ParameterValidator.objectChecker(nav, "NavigationController");
ParameterValidator.objectChecker(donationController, "DonationController");
- this.appState = appState;
this.nav = nav;
this.donationController = donationController;
@@ -219,7 +215,7 @@ private void selectPaymentMethod(Node element) {
selectedPaymentMethod.getStyleClass().add("payment-method-selected");
String paymentMethod = (String) element.getUserData();
- appState.setCurrentPaymentMethod(paymentMethod);
+ donationController.setPaymentMethod(paymentMethod);
updateDonationButtonState();
}
@@ -243,7 +239,7 @@ private void clearSelection() {
}
private void updateDonationAmount(BigDecimal amount) {
- appState.setCurrentDonationAmount(amount);
+ donationController.setDonationAmount(amount);
}
private BigDecimal parseAmount(String amountStr) {
diff --git a/src/main/java/edu/group5/app/view/homepage/HomePageView.java b/src/main/java/edu/group5/app/view/homepage/HomePageView.java
index c24b89b..137e0b1 100644
--- a/src/main/java/edu/group5/app/view/homepage/HomePageView.java
+++ b/src/main/java/edu/group5/app/view/homepage/HomePageView.java
@@ -1,7 +1,6 @@
package edu.group5.app.view.homepage;
import edu.group5.app.control.NavigationController;
-import edu.group5.app.model.AppState;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
@@ -19,11 +18,9 @@
* and an about us button. The page also has a charity image at the bottom.
*/
public class HomePageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
- public HomePageView(AppState appState, NavigationController nav) {
- this.appState = appState;
+ public HomePageView(NavigationController nav) {
this.nav = nav;
getStylesheets().add(getClass().getResource("/homepage/homepage.css").toExternalForm());
diff --git a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java
index 11b3ba7..927a8af 100644
--- a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java
+++ b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java
@@ -2,7 +2,6 @@
import edu.group5.app.control.NavigationController;
import edu.group5.app.control.AuthController;
-import edu.group5.app.model.AppState;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@@ -21,7 +20,6 @@
* a login box, an email box, a login button, and a register button.
*/
public class LoginPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
private final AuthController authController;
@@ -29,8 +27,7 @@ public class LoginPageView extends BorderPane {
private PasswordField passwordField;
private Label errorLabel;
- public LoginPageView(AppState appState, NavigationController nav, AuthController authController) {
- this.appState = appState;
+ public LoginPageView(NavigationController nav, AuthController authController) {
this.nav = nav;
this.authController = authController;
diff --git a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java
index 8638a1f..bdcfb54 100644
--- a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java
+++ b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java
@@ -2,7 +2,6 @@
import edu.group5.app.control.NavigationController;
import edu.group5.app.control.AuthController;
-import edu.group5.app.model.AppState;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@@ -22,7 +21,6 @@
* a password field, a sign up button, and an back to login button.
*/
public class SignUpPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
private final AuthController authController;
@@ -32,8 +30,7 @@ public class SignUpPageView extends BorderPane {
private PasswordField passwordField;
private Label errorLabel;
- public SignUpPageView(AppState appState, NavigationController nav, AuthController authController) {
- this.appState = appState;
+ public SignUpPageView(NavigationController nav, AuthController authController) {
this.nav = nav;
this.authController = authController;
diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
index 2268d4d..3bea2ff 100644
--- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
+++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
@@ -2,7 +2,7 @@
import edu.group5.app.control.DonationController;
import edu.group5.app.control.NavigationController;
-import edu.group5.app.model.AppState;
+import edu.group5.app.control.OrganizationController;
import edu.group5.app.model.organization.Organization;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -29,16 +29,21 @@
* donation page, and a back button to return to the causes page.
*/
public class OrganizationPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
+ private final OrganizationController organizationController;
private final DonationController donationController;
- public OrganizationPageView(AppState appState, NavigationController nav, DonationController donationController) {
- this.appState = appState;
+ public OrganizationPageView(NavigationController nav,
+ OrganizationController organizationController,
+ DonationController donationController) {
this.nav = nav;
+ this.organizationController = organizationController;
this.donationController = donationController;
- getStylesheets().add(Objects.requireNonNull(getClass().getResource("/organizationpage/organizationpage.css")).toExternalForm());
+ getStylesheets().add(Objects
+ .requireNonNull(getClass()
+ .getResource("/organizationpage/organizationpage.css"))
+ .toExternalForm());
VBox content = new VBox();
content.getChildren().addAll(createBackButton(), createBody());
@@ -84,7 +89,7 @@ private StackPane createImageContainer() {
imageContainer.setPrefWidth(120);
imageContainer.setMaxWidth(120);
- Organization org = appState.getCurrentOrganization();
+ Organization org = organizationController.getCurrentOrganization();
if (org != null && org.logoUrl() != null && !org.logoUrl().isBlank()) {
ImageView logo = new ImageView(new Image(org.logoUrl(), true));
logo.setId("logo");
@@ -106,7 +111,7 @@ private StackPane createImageContainer() {
}
private VBox createOrgInfoSection() {
- Organization org = appState.getCurrentOrganization();
+ Organization org = organizationController.getCurrentOrganization();
VBox orgInfoSection = new VBox();
orgInfoSection.setSpacing(50);
diff --git a/src/main/java/edu/group5/app/view/userpage/UserPageView.java b/src/main/java/edu/group5/app/view/userpage/UserPageView.java
index 5e2370a..c46af3c 100644
--- a/src/main/java/edu/group5/app/view/userpage/UserPageView.java
+++ b/src/main/java/edu/group5/app/view/userpage/UserPageView.java
@@ -19,12 +19,9 @@
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
-import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import java.text.SimpleDateFormat;
-
-import java.math.RoundingMode;
import java.util.*;
@@ -51,13 +48,15 @@ public UserPageView(AppState appState, NavigationController nav, AuthController
}
private HBox createProfileSection() {
- ImageView avatar = new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/userpage/account_circle.png"))));
+ ImageView avatar = new ImageView(new Image(Objects
+ .requireNonNull(getClass()
+ .getResourceAsStream("/userpage/account_circle.png"))));
avatar.setFitWidth(150);
avatar.setFitHeight(150);
avatar.setPreserveRatio(true);
avatar.setId("avatar");
- User currentUser = appState.getCurrentUser();
+ User currentUser = authController.getCurrentUser();
Text name = new Text(currentUser.getFirstName() + " " + currentUser.getLastName());
name.setId("profile-name");
@@ -133,7 +132,7 @@ private VBox createDonationsSection() {
donationsBox.getStyleClass().add("donation-list");
donationsBox.setPadding(new Insets(10));
- User currentUser = appState.getCurrentUser();
+ User currentUser = authController.getCurrentUser();
Map userDonations =
donationController.getUserDonations(currentUser.getUserId());
diff --git a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
index 3ed1aec..f57b559 100644
--- a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
+++ b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
@@ -61,16 +61,6 @@ void testConstructorThrowsIfOrganizationRepositoryIsNull() {
assertEquals("OrganizationRepository cannot be null", exception.getMessage());
}
- @Test
- void testGetDonationRepository() {
- assertEquals(donationRepository, donationService.getDonationRepository());
- }
-
- @Test
- void testGetOrganizationRepository() {
- assertEquals(organizationRepository, donationService.getOrganizationRepository());
- }
-
@Test
void donateReturnsFalseIfCustomerNull() {
boolean result = donationService.donate(null,
diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
index 2d76391..78f3e1a 100644
--- a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
+++ b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
@@ -43,11 +43,6 @@ void constructor_throwsIfScraperIsNull() {
assertEquals("OrganizationScraper cannot be null", ex.getMessage());
}
- @Test
- void testGetOrganizationRepository() {
- assertEquals(repo, service.getOrganizationRepository());
- }
-
@Test
void testGetTrustedOrganizationsWithLogos() {
Map orgsWithLogos = service.getTrustedOrganizationsWithLogos();
diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java
index acec10b..99a6913 100644
--- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java
+++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java
@@ -34,11 +34,6 @@ void constructorthrowsIfNull() {
assertEquals("UserRepository cannot be null", ex.getMessage());
}
- @Test
- void testGetUserRepository() {
- assertEquals(repo, service.getUserRepository());
- }
-
@Test
void registerUserValid() {
boolean result = service.registerUser("Customer", "Alice", "Smith",
From c350ee2a152054e8dd9662d672fa0304ef850232 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Thu, 16 Apr 2026 18:35:59 +0200
Subject: [PATCH 05/10] update&fix[UserPageView]: Remove appstate to increase
MVC Structure
---
.../java/edu/group5/app/control/NavigationController.java | 2 +-
.../java/edu/group5/app/view/causespage/CausesPageView.java | 1 -
.../java/edu/group5/app/view/userpage/UserPageView.java | 6 ++----
3 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index 92ea26a..70079ae 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -132,6 +132,6 @@ public void showAboutUsPage() {
*/
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/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
index 271bfa9..488a868 100644
--- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
+++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
@@ -4,7 +4,6 @@
import edu.group5.app.utils.ParameterValidator;
import edu.group5.app.control.NavigationController;
import edu.group5.app.control.OrganizationController;
-import edu.group5.app.model.AppState;
import javafx.application.Platform;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
diff --git a/src/main/java/edu/group5/app/view/userpage/UserPageView.java b/src/main/java/edu/group5/app/view/userpage/UserPageView.java
index c46af3c..cf6f801 100644
--- a/src/main/java/edu/group5/app/view/userpage/UserPageView.java
+++ b/src/main/java/edu/group5/app/view/userpage/UserPageView.java
@@ -4,7 +4,6 @@
import edu.group5.app.control.NavigationController;
import edu.group5.app.control.OrganizationController;
import edu.group5.app.control.AuthController;
-import edu.group5.app.model.AppState;
import edu.group5.app.model.donation.Donation;
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.user.User;
@@ -26,14 +25,13 @@
public class UserPageView extends BorderPane {
- private final AppState appState;
private final NavigationController nav;
private final AuthController authController;
private final DonationController donationController;
private final OrganizationController organizationController;
- public UserPageView(AppState appState, NavigationController nav, AuthController authController, DonationController donationController, OrganizationController organizationController) {
- this.appState = appState;
+ public UserPageView(NavigationController nav, AuthController authController,
+ DonationController donationController, OrganizationController organizationController) {
this.nav = nav;
this.authController = authController;
this.donationController = donationController;
From d9cfc518054569cc03cba310f288b6436e25de8c Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Thu, 16 Apr 2026 19:19:13 +0200
Subject: [PATCH 06/10] fix: update tests and code to use new
ParameterValidator error messages
---
.../organization/OrganizationScraper.java | 4 +-
.../group5/app/model/user/UserService.java | 7 +-
.../donation/DonationRepositoryTest.java | 10 +-
.../model/donation/DonationServiceTest.java | 4 +-
.../OrganizationRepositoryTest.java | 312 +++++++++---------
.../organization/OrganizationServiceTest.java | 4 +-
.../group5/app/model/user/CustomerTest.java | 20 +-
.../app/model/user/UserRepositoryTest.java | 10 +-
.../app/model/user/UserServiceTest.java | 2 +-
9 files changed, 187 insertions(+), 186 deletions(-)
diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java
index fdeec82..ad93736 100644
--- a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java
+++ b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java
@@ -31,7 +31,7 @@ public class OrganizationScraper {
* @return the description text, or null if not found or pageUrl is invalid
*/
public String fetchDescription(String pageUrl) {
- if (descriptionCache.containsKey(pageUrl)) {
+ if (pageUrl != null && descriptionCache.containsKey(pageUrl)) {
return descriptionCache.get(pageUrl);
}
@@ -80,7 +80,7 @@ public String fetchDescription(String pageUrl) {
* @return the absolute logo URL, or null if not found or pageUrl is invalid
*/
public String fetchLogoUrl(String pageUrl) {
- if (logoCache.containsKey(pageUrl)) {
+ if (pageUrl != null && logoCache.containsKey(pageUrl)) {
return logoCache.get(pageUrl);
}
diff --git a/src/main/java/edu/group5/app/model/user/UserService.java b/src/main/java/edu/group5/app/model/user/UserService.java
index 9d215d0..6bee483 100644
--- a/src/main/java/edu/group5/app/model/user/UserService.java
+++ b/src/main/java/edu/group5/app/model/user/UserService.java
@@ -51,8 +51,7 @@ public boolean registerUser(String role, String firstName, String lastName,
firstName == null || firstName.trim().isEmpty() ||
lastName == null || lastName.trim().isEmpty() ||
email == null || email.trim().isEmpty() ||
- passwordHash == null || passwordHash.trim().isEmpty() ||
- this.getUserByEmail(email) != null) {
+ passwordHash == null || passwordHash.trim().isEmpty()) {
return false;
}
User user;
@@ -95,7 +94,9 @@ public User login(String email, char[] password) {
* @return the User object if found, null otherwise
*/
public User getUserByEmail(String email) {
- ParameterValidator.stringChecker(email, "email");
+ if (email == null || email.trim().isEmpty()) {
+ return null;
+ }
return this.userRepository.findUserByEmail(email);
}
}
diff --git a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java
index ce81b2a..82140a7 100644
--- a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java
+++ b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java
@@ -33,7 +33,7 @@ void setUp() {
void constructorThrowsIfNullList() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> new DonationRepository(null));
- assertEquals("The list of rows cannot be null", ex.getMessage());
+ assertEquals("List of donation rows can't be null", ex.getMessage());
}
@Test
@@ -88,7 +88,7 @@ void addContentDuplicateIdFails() {
void addContentNullThrows() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.addContent(null));
- assertEquals("Donation cannot be null", ex.getMessage());
+ assertEquals("Donation can't be null", ex.getMessage());
}
@Test
@@ -102,7 +102,7 @@ void getDonationByIdSuccessfully() {
void getDonationByIdThrowsIfNegative() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.getDonationById(0));
- assertEquals("Donation ID must be positive", ex.getMessage());
+ assertEquals("Donation ID must be a positive integer", ex.getMessage());
}
@Test
@@ -182,7 +182,7 @@ void filterByOrganizationNoMatch() {
void filterByOrganizationThrowsIfNegative() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.filterByOrganization(0));
- assertEquals("Organization number must be positive", ex.getMessage());
+ assertEquals("Organization number must be a positive integer", ex.getMessage());
}
@Test
@@ -261,7 +261,7 @@ void filterByUserIdNoMatch() {
void filterByUserIdThrowsIfNegative() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.filterByUser(0));
- assertEquals("User ID must be positive", ex.getMessage());
+ assertEquals("User ID must be a positive integer", ex.getMessage());
}
@Test
diff --git a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
index f57b559..58bd170 100644
--- a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
+++ b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java
@@ -50,7 +50,7 @@ void testConstructorThrowsIfDonationRepositoryIsNull() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
new DonationService(null, organizationRepository);
});
- assertEquals("DonationRepository cannot be null", exception.getMessage());
+ assertEquals("DonationRepository can't be null", exception.getMessage());
}
@Test
@@ -58,7 +58,7 @@ void testConstructorThrowsIfOrganizationRepositoryIsNull() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
new DonationService(donationRepository, null);
});
- assertEquals("OrganizationRepository cannot be null", exception.getMessage());
+ assertEquals("OrganizationRepository can't be null", exception.getMessage());
}
@Test
diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java
index 8906395..a5eab05 100644
--- a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java
+++ b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java
@@ -1,157 +1,157 @@
-package edu.group5.app.model.organization;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class OrganizationRepositoryTest {
-
- private OrganizationRepository repository;
- private OrganizationScraper scraper;
-
- @BeforeEach
- void setUp() {
- scraper = new OrganizationScraper();
- Object[] content = new Object[] {
- Map.of(
- "org_number", "1",
- "name", "Trusted Org1",
- "status", "approved",
- "url", "org.com",
- "is_pre_approved", true
- ),
- Map.of(
- "org_number", "2",
- "name", "Trusted Org2",
- "status", "approved",
- "url", "org.com",
- "is_pre_approved", true
- ),
- Map.of(
- "org_number", "3",
- "name", "Untrusted Org1",
- "status", "pending",
- "url", "org.com",
- "is_pre_approved", true
- ),
- Map.of(
- "org_number", "4",
- "name", "Untrusted Org2",
- "status", "pending",
- "url", "org.com",
- "is_pre_approved", true
- )
- };
- repository = new OrganizationRepository(content, scraper);
- }
-
- private void constructorTest(Object[] input, String expectedMessage) {
- IllegalArgumentException exception = assertThrows(
- IllegalArgumentException.class,
- () -> new OrganizationRepository(input, scraper)
- );
- assertEquals(expectedMessage, exception.getMessage());
- }
- @Test
- void constructor_ThrowsWhenContentIsNull() {
- constructorTest(null, "The input cannot be null");
- }
-
- @Test
- void constructor_ThrowsWhenScraperIsNull() {
- Object[] content = new Object[] {
- Map.of(
- "org_number", "1",
- "name", "Org",
- "status", "approved",
- "url", "org.com",
- "is_pre_approved", true
- )
- };
- IllegalArgumentException exception = assertThrows(
- IllegalArgumentException.class,
- () -> new OrganizationRepository(content, null)
- );
- assertEquals("The scraper cannot be null", exception.getMessage());
- }
-
- @Test
- void getTrustedOrganizations_OnlyReturnsTrustedOrganizations() {
- Map trusted = repository.getTrustedOrganizations();
-
- assertEquals(2, trusted.size());
- assertTrue(trusted.containsKey(1));
- assertTrue(trusted.containsKey(2));
- assertFalse(trusted.containsKey(3));
- assertFalse(trusted.containsKey(4));
- assertTrue(trusted.values().stream().allMatch(Organization::trusted));
- }
-
- @Test
- void testFindByOrgNumberReturnsOrganization() {
- assertEquals(new Organization(1, "Trusted Org1", true,
- "org.com", true, "Information about Trusted Org1", null),
- repository.findByOrgNumber(1));
- }
-
- @Test
- void testFindByOrgNumberIfOrgNumberIsIllegal() {
- IllegalArgumentException exception = assertThrows(
- IllegalArgumentException.class,
- () -> repository.findByOrgNumber(-1));
- assertEquals("The Organization number must be a positive integer", exception.getMessage());
- }
-
- @Test
- void testFindByOrgNumberIfOrgNumberNotFound() {
- assertNull(repository.findByOrgNumber(999));
- }
-
- @Test
- void testFindByOrgNameReturnsOrganization() {
- assertEquals(new Organization(1, "Trusted Org1", true,
- "org.com", true, "Information about Trusted Org1", null),
- repository.findByOrgName("Trusted Org1"));
- }
-
- @Test
- void testFindByOrgNameIfNameIsIllegalThrowsException() {
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
- () -> repository.findByOrgName(null));
- assertEquals("The name cannot be null", exception.getMessage());
-
- IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class,
- () -> repository.findByOrgName(""));
- assertEquals("The name cannot be null", exception2.getMessage());
- }
-
- @Test
- void testFindByOrgNameIfNameNotFound() {
- assertNull(repository.findByOrgName("Nonexistent Org"));
- }
-
- @Test
- void testFindByOrgNameIsCaseInsensitive() {
- assertEquals(new Organization(1, "Trusted Org1", true,
- "org.com", true, "Information about Trusted Org1", null),
- repository.findByOrgName("trusted org1"));
- }
-
- @Test
- void testExportAllOrganizations() {
- Object[] allOrgs = repository.export();
- assertEquals(4, allOrgs.length);
- }
-
- @Test
- void testExportAllOrganizationsThrowsWhenRepositoryIsEmpty() {
- OrganizationRepository emptyRepo = new OrganizationRepository(new Object[0], scraper);
- IllegalStateException exception = assertThrows(
- IllegalStateException.class, () -> emptyRepo.export()
- );
- assertEquals("The repository is empty", exception.getMessage());
- }
+package edu.group5.app.model.organization;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OrganizationRepositoryTest {
+
+ private OrganizationRepository repository;
+ private OrganizationScraper scraper;
+
+ @BeforeEach
+ void setUp() {
+ scraper = new OrganizationScraper();
+ Object[] content = new Object[] {
+ Map.of(
+ "org_number", "1",
+ "name", "Trusted Org1",
+ "status", "approved",
+ "url", "org.com",
+ "is_pre_approved", true
+ ),
+ Map.of(
+ "org_number", "2",
+ "name", "Trusted Org2",
+ "status", "approved",
+ "url", "org.com",
+ "is_pre_approved", true
+ ),
+ Map.of(
+ "org_number", "3",
+ "name", "Untrusted Org1",
+ "status", "pending",
+ "url", "org.com",
+ "is_pre_approved", true
+ ),
+ Map.of(
+ "org_number", "4",
+ "name", "Untrusted Org2",
+ "status", "pending",
+ "url", "org.com",
+ "is_pre_approved", true
+ )
+ };
+ repository = new OrganizationRepository(content, scraper);
+ }
+
+ private void constructorTest(Object[] input, String expectedMessage) {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new OrganizationRepository(input, scraper)
+ );
+ assertEquals(expectedMessage, exception.getMessage());
+ }
+ @Test
+ void constructor_ThrowsWhenContentIsNull() {
+ constructorTest(null, "Input data can't be null");
+ }
+
+ @Test
+ void constructor_ThrowsWhenScraperIsNull() {
+ Object[] content = new Object[] {
+ Map.of(
+ "org_number", "1",
+ "name", "Org",
+ "status", "approved",
+ "url", "org.com",
+ "is_pre_approved", true
+ )
+ };
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new OrganizationRepository(content, null)
+ );
+ assertEquals("Scraper can't be null", exception.getMessage());
+ }
+
+ @Test
+ void getTrustedOrganizations_OnlyReturnsTrustedOrganizations() {
+ Map trusted = repository.getTrustedOrganizations();
+
+ assertEquals(2, trusted.size());
+ assertTrue(trusted.containsKey(1));
+ assertTrue(trusted.containsKey(2));
+ assertFalse(trusted.containsKey(3));
+ assertFalse(trusted.containsKey(4));
+ assertTrue(trusted.values().stream().allMatch(Organization::trusted));
+ }
+
+ @Test
+ void testFindByOrgNumberReturnsOrganization() {
+ assertEquals(new Organization(1, "Trusted Org1", true,
+ "org.com", true, "Information about Trusted Org1", null),
+ repository.findByOrgNumber(1));
+ }
+
+ @Test
+ void testFindByOrgNumberIfOrgNumberIsIllegal() {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> repository.findByOrgNumber(-1));
+ assertEquals("Organization number must be a positive integer", exception.getMessage());
+ }
+
+ @Test
+ void testFindByOrgNumberIfOrgNumberNotFound() {
+ assertNull(repository.findByOrgNumber(999));
+ }
+
+ @Test
+ void testFindByOrgNameReturnsOrganization() {
+ assertEquals(new Organization(1, "Trusted Org1", true,
+ "org.com", true, "Information about Trusted Org1", null),
+ repository.findByOrgName("Trusted Org1"));
+ }
+
+ @Test
+ void testFindByOrgNameIfNameIsIllegalThrowsException() {
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> repository.findByOrgName(null));
+ assertEquals("Organization name can't be null", exception.getMessage());
+
+ IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class,
+ () -> repository.findByOrgName(""));
+ assertEquals("Organization name can't be blank", exception2.getMessage());
+ }
+
+ @Test
+ void testFindByOrgNameIfNameNotFound() {
+ assertNull(repository.findByOrgName("Nonexistent Org"));
+ }
+
+ @Test
+ void testFindByOrgNameIsCaseInsensitive() {
+ assertEquals(new Organization(1, "Trusted Org1", true,
+ "org.com", true, "Information about Trusted Org1", null),
+ repository.findByOrgName("trusted org1"));
+ }
+
+ @Test
+ void testExportAllOrganizations() {
+ Object[] allOrgs = repository.export();
+ assertEquals(4, allOrgs.length);
+ }
+
+ @Test
+ void testExportAllOrganizationsThrowsWhenRepositoryIsEmpty() {
+ OrganizationRepository emptyRepo = new OrganizationRepository(new Object[0], scraper);
+ IllegalStateException exception = assertThrows(
+ IllegalStateException.class, () -> emptyRepo.export()
+ );
+ assertEquals("The repository is empty", exception.getMessage());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
index 78f3e1a..3765bf5 100644
--- a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
+++ b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java
@@ -33,14 +33,14 @@ public void setUp() {
void constructor_throwsIfRepositoryIsNull() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> new OrganizationService(null, scraper));
- assertEquals("OrganizationRepository cannot be null", ex.getMessage());
+ assertEquals("OrganizationRepository can't be null", ex.getMessage());
}
@Test
void constructor_throwsIfScraperIsNull() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> new OrganizationService(repo, null));
- assertEquals("OrganizationScraper cannot be null", ex.getMessage());
+ assertEquals("Scraper can't be null", ex.getMessage());
}
@Test
diff --git a/src/test/java/edu/group5/app/model/user/CustomerTest.java b/src/test/java/edu/group5/app/model/user/CustomerTest.java
index 7c96438..147343f 100644
--- a/src/test/java/edu/group5/app/model/user/CustomerTest.java
+++ b/src/test/java/edu/group5/app/model/user/CustomerTest.java
@@ -59,55 +59,55 @@ void testInstanceOfCustomer() {
@Test
void constructorWithNegativeUserIdThrowsException() {
constructorTest(-1, testFirstName, testLastName,
- testEmail, testPasswordHash, "User ID must be positive");
+ testEmail, testPasswordHash, "User ID must be a positive integer");
}
@Test
void constructorWithNullFirstNameThrowsException() {
constructorTest(testUserId, null, testLastName,
- testEmail, testPasswordHash, "First name cannot be null or empty");
+ testEmail, testPasswordHash, "First name can't be null");
}
@Test
void constructorWithEmptyFirstNameThrowsException() {
constructorTest(testUserId, "", testLastName,
- testEmail, testPasswordHash, "First name cannot be null or empty");
+ testEmail, testPasswordHash, "First name can't be blank");
}
@Test
void constructorWithNullLastNameThrowsException() {
constructorTest(testUserId, testFirstName, null,
- testEmail, testPasswordHash, "Last name cannot be null or empty");
+ testEmail, testPasswordHash, "Last name can't be null");
}
@Test
void constructorWithEmptyLastNameThrowsException() {
- constructorTest(testUserId, testFirstName,
- "", testEmail, testPasswordHash, "Last name cannot be null or empty");
+ constructorTest(testUserId, testFirstName,
+ "", testEmail, testPasswordHash, "Last name can't be blank");
}
@Test
void constructorWithNullEmailThrowsException() {
constructorTest(testUserId, testFirstName, testLastName,
- null, testPasswordHash, "Email cannot be null or empty");
+ null, testPasswordHash, "Email can't be null");
}
@Test
void constructorWithEmptyEmailThrowsException() {
constructorTest(testUserId, testFirstName, testLastName,
- "", testPasswordHash, "Email cannot be null or empty");
+ "", testPasswordHash, "Email can't be blank");
}
@Test
void constructorWithNullPasswordHashThrowsException() {
constructorTest(testUserId, testFirstName, testLastName,
- testEmail, null, "Password hash cannot be null or empty");
+ testEmail, null, "Password hash can't be null");
}
@Test
void constructorWithEmptyPasswordHashThrowsException() {
constructorTest(testUserId, testFirstName, testLastName,
- testEmail, "", "Password hash cannot be null or empty");
+ testEmail, "", "Password hash can't be blank");
}
diff --git a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java
index eaf2c2c..fd13b13 100644
--- a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java
+++ b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java
@@ -27,7 +27,7 @@ void setUp() {
void constructorThrowsIfNull() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> new UserRepository(null));
- assertEquals("The list of rows cannot be null", ex.getMessage());
+ assertEquals("List of User rows can't be null", ex.getMessage());
}
@Test
@@ -77,7 +77,7 @@ void addContentSuccessfully() {
void addContentNullThrows() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.addContent(null));
- assertEquals("User cannot be null", ex.getMessage());
+ assertEquals("User can't be null", ex.getMessage());
}
@Test
@@ -101,11 +101,11 @@ void findUserByEmailReturnsNullIfNotFound() {
void findUserByEmailThrowsIfNullOrEmpty() {
IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class,
() -> repo.findUserByEmail(null));
- assertEquals("Email cannot be null or empty", ex1.getMessage());
+ assertEquals("Email can't be null", ex1.getMessage());
IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class,
() -> repo.findUserByEmail(" "));
- assertEquals("Email cannot be null or empty", ex2.getMessage());
+ assertEquals("Email can't be blank", ex2.getMessage());
}
@Test
@@ -123,7 +123,7 @@ void getUserByIdReturnsNullIfNotFound() {
void getUserByIdThrowsIfNonPositive() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> repo.getUserById(0));
- assertEquals("User ID must be positive", ex.getMessage());
+ assertEquals("User ID must be a positive integer", ex.getMessage());
}
diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java
index 99a6913..104e2f1 100644
--- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java
+++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java
@@ -31,7 +31,7 @@ void setUp() {
void constructorthrowsIfNull() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
() -> new UserService(null));
- assertEquals("UserRepository cannot be null", ex.getMessage());
+ assertEquals("UserRepository can't be null", ex.getMessage());
}
@Test
From 829537973866a2050a8008ecf1218867feaab783 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Sat, 18 Apr 2026 15:57:19 +0200
Subject: [PATCH 07/10] Update[Organization]: Update rendering of organization
Logo to render more quicker
---
.../app/view/causespage/CausesPageView.java | 42 +++++++++++++++----
.../app/view/causespage/OrganizationCard.java | 4 +-
.../OrganizationPageView.java | 40 ++++++++++++------
3 files changed, 63 insertions(+), 23 deletions(-)
diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
index 488a868..0bdddbc 100644
--- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
+++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java
@@ -38,6 +38,7 @@ public class CausesPageView extends BorderPane {
private GridPane organizationGrid;
private Map allOrganizations;
+ private Map cardCache = new HashMap<>();
public CausesPageView(NavigationController nav, OrganizationController orgController) {
ParameterValidator.objectChecker(nav, "NavigationController");
@@ -205,18 +206,41 @@ private Map filterOrganizations(String searchTerm) {
));
}
+
private void updateOrganizationGrid(String searchTerm) {
- if (organizationGrid == null) {
- return;
+ if (organizationGrid == null) return;
+
+ // Save existing cards into cache before clearing
+ for (var node : organizationGrid.getChildren()) {
+ if (node instanceof OrganizationCard card) {
+ cardCache.put(card.getOrganization().orgNumber(), card);
+ }
}
-
+
+ Map filtered = filterOrganizations(searchTerm);
+
organizationGrid.getChildren().clear();
organizationGrid.getColumnConstraints().clear();
-
- // Rebuild grid with filtered organizations
- GridPane updated = createOrganizationSection(searchTerm);
-
- organizationGrid.getChildren().addAll(updated.getChildren());
- organizationGrid.getColumnConstraints().addAll(updated.getColumnConstraints());
+
+ int column = 0;
+ int row = 0;
+
+ for (Organization org : filtered.values()) {
+ OrganizationCard card = cardCache.get(org.orgNumber());
+ if (card != null) {
+ organizationGrid.add(card, column, row);
+ column++;
+ if (column == 4) {
+ column = 0;
+ row++;
+ }
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ ColumnConstraints col = new ColumnConstraints();
+ col.setPercentWidth(25);
+ organizationGrid.getColumnConstraints().add(col);
+ }
}
}
diff --git a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
index 4645d0b..483d0e7 100644
--- a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
+++ b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java
@@ -61,7 +61,7 @@ public void updateLogo(String logoUrl) {
if (imageContainer == null) return;
imageContainer.getChildren().clear();
if (logoUrl != null && !logoUrl.isBlank()) {
- ImageView logo = new ImageView(new Image(logoUrl, true));
+ ImageView logo = new ImageView(new Image(logoUrl, 80, 80, true, true, true));
logo.setId("logo");
logo.setSmooth(true);
logo.setPreserveRatio(true);
@@ -96,7 +96,7 @@ private StackPane createImageContainer(String img) {
if (img != null && !img.isBlank()) {
- ImageView logo = new ImageView(new Image(img, true));
+ ImageView logo = new ImageView(new Image(img, 80, 80, true, true, true));
logo.setId("logo");
logo.setSmooth(true);
logo.setPreserveRatio(true);
diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
index 3bea2ff..f591d26 100644
--- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
+++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java
@@ -4,6 +4,7 @@
import edu.group5.app.control.NavigationController;
import edu.group5.app.control.OrganizationController;
import edu.group5.app.model.organization.Organization;
+import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
@@ -90,23 +91,38 @@ private StackPane createImageContainer() {
imageContainer.setMaxWidth(120);
Organization org = organizationController.getCurrentOrganization();
+
if (org != null && org.logoUrl() != null && !org.logoUrl().isBlank()) {
- ImageView logo = new ImageView(new Image(org.logoUrl(), true));
- logo.setId("logo");
- logo.setSmooth(true);
- logo.setPreserveRatio(true);
- logo.setFitHeight(350);
- logo.setFitWidth(350);
- imageContainer.getChildren().add(logo);
+ // Load image in background thread to avoid blocking UI
+ new Thread(() -> {
+ try {
+ Image image = new Image(org.logoUrl(), 120, 120, true, true);
+ Platform.runLater(() -> {
+ ImageView logo = new ImageView(image);
+ logo.setId("logo");
+ logo.setSmooth(true);
+ logo.setPreserveRatio(true);
+ logo.setFitHeight(350);
+ logo.setFitWidth(350);
+ imageContainer.getChildren().clear();
+ imageContainer.getChildren().add(logo);
+ });
+ } catch (Exception e) {
+ // Logo failed to load, show placeholder
+ Platform.runLater(() -> {
+ imageContainer.getChildren().clear();
+ Text text = new Text("No image");
+ text.setStyle("-fx-font-size: 10;");
+ imageContainer.getChildren().add(text);
+ });
+ }
+ }, "LogoLoader").start();
} else {
- StackPane placeholder = new StackPane();
-
Text text = new Text("No image");
text.setStyle("-fx-font-size: 10;");
-
- placeholder.getChildren().add(text);
- imageContainer.getChildren().add(placeholder);
+ imageContainer.getChildren().add(text);
}
+
return imageContainer;
}
From c4d31dcebe112a03305fdb95f1649d4d5ed4a839 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Sat, 18 Apr 2026 16:45:31 +0200
Subject: [PATCH 08/10] feat[NavigationController]: Add a small about us pop up
feature that summarizes what this application is about
---
src/main/java/edu/group5/app/App.java | 2 +-
.../app/control/NavigationController.java | 38 +++++++++++++++++--
2 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java
index 6402156..17f99de 100644
--- a/src/main/java/edu/group5/app/App.java
+++ b/src/main/java/edu/group5/app/App.java
@@ -82,7 +82,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, getHostServices());
}
@Override
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index 70079ae..2c47621 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -15,7 +15,13 @@
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 handling navigation between different pages of the application.
@@ -33,23 +39,25 @@ public class NavigationController {
private final LoginHeader loginHeader;
private final AppState appState;
+ private 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) {
+ 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.hostServices = hostServices;
this.appState = appState;
this.authController = new AuthController(appState, this, userService);
@@ -122,7 +130,31 @@ public void showDonationPage() {
}
public void showAboutUsPage() {
- root.setTop(header);
+ Alert aboutUs = new Alert(Alert.AlertType.INFORMATION);
+ aboutUs.setTitle("About us");
+ aboutUs.setHeaderText("Help Me Help - About Us");
+
+ Label description = new Label(
+ "Help Me Help is a charity donation application designed to connect donors with organizations in need. " +
+ "Our mission is to make it easy for people to support causes they care about and make a positive impact in the world.\n\n" +
+ "This application was developed by Team 5 as part of a IDATT1005 course project at NTNU spring 2026."
+ );
+ description.setWrapText(true);
+ description.maxWidthProperty().bind(aboutUs.getDialogPane().widthProperty().subtract(40));
+
+ Hyperlink websiteLink = new Hyperlink("For more information about the project, visit our GitHub repository");
+ websiteLink.setOnAction(e -> hostServices.showDocument("https://git.ntnu.no/Group-5/Help-Me-Help"));
+
+ VBox content = new VBox(10, description, websiteLink);
+ content.setPrefWidth(420);
+
+ aboutUs.getDialogPane().setContentText(null);
+ aboutUs.getDialogPane().setContent(content);
+
+ // Optional: single Close button instead of OK
+ aboutUs.getButtonTypes().setAll(ButtonType.CLOSE);
+
+ aboutUs.showAndWait();
}
/**
From 3b3376f6dd024c239299be1132efc2a1434db0f5 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Mon, 20 Apr 2026 13:35:24 +0200
Subject: [PATCH 09/10] fix[App]: fix spelling of static variable MAX_RETRIES
---
src/main/java/edu/group5/app/App.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java
index 17f99de..8658fb1 100644
--- a/src/main/java/edu/group5/app/App.java
+++ b/src/main/java/edu/group5/app/App.java
@@ -35,7 +35,7 @@ public class App extends Application {
NavigationController nav;
private Logger logger;
- static int MAX_ReTRIES = 3;
+ static int MAX_RETRIES = 3;
@Override
public void init() {
@@ -46,12 +46,12 @@ public void init() {
OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all");
int retries = 0;
- while (!dbWrapper.connect() && retries < MAX_ReTRIES) {
+ 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");
+ if (retries == MAX_RETRIES) {
+ this.logger.severe("Failed to connect to database after " + MAX_RETRIES + " attempts");
throw new RuntimeException("Failed to connect to database");
}
From df0905eaafce202420c9d38a0f964cc4a12f3ab8 Mon Sep 17 00:00:00 2001
From: Fredrik Marjoni
Date: Mon, 20 Apr 2026 22:18:09 +0200
Subject: [PATCH 10/10] Update[AboutUsPage]: Update AboutUs page by moving the
construction of the pop-up into a new class, increassing Seperation of
concern
---
src/main/java/edu/group5/app/App.java | 3 +-
.../app/control/NavigationController.java | 35 +++---------
.../app/view/aboutuspage/AboutUsView.java | 55 +++++++++++++++++++
3 files changed, 65 insertions(+), 28 deletions(-)
create mode 100644 src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java
diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java
index 8658fb1..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;
@@ -82,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, getHostServices());
+ this.nav = new NavigationController(root, appState, userService, donationService, organizationService, this.getHostServices());
}
@Override
diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java
index 2c47621..8bbbbb3 100644
--- a/src/main/java/edu/group5/app/control/NavigationController.java
+++ b/src/main/java/edu/group5/app/control/NavigationController.java
@@ -6,6 +6,7 @@
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;
@@ -39,7 +40,7 @@ public class NavigationController {
private final LoginHeader loginHeader;
private final AppState appState;
- private HostServices hostServices;
+ private final HostServices hostServices;
private final AuthController authController;
private final DonationController donationController;
@@ -57,8 +58,8 @@ public NavigationController(BorderPane root, AppState appState, UserService user
this.root = root;
this.header = new Header(this);
this.loginHeader = new LoginHeader();
- this.hostServices = hostServices;
this.appState = appState;
+ this.hostServices = hostServices;
this.authController = new AuthController(appState, this, userService);
this.donationController = new DonationController(appState, this, donationService);
@@ -129,32 +130,12 @@ public void showDonationPage() {
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() {
- Alert aboutUs = new Alert(Alert.AlertType.INFORMATION);
- aboutUs.setTitle("About us");
- aboutUs.setHeaderText("Help Me Help - About Us");
-
- Label description = new Label(
- "Help Me Help is a charity donation application designed to connect donors with organizations in need. " +
- "Our mission is to make it easy for people to support causes they care about and make a positive impact in the world.\n\n" +
- "This application was developed by Team 5 as part of a IDATT1005 course project at NTNU spring 2026."
- );
- description.setWrapText(true);
- description.maxWidthProperty().bind(aboutUs.getDialogPane().widthProperty().subtract(40));
-
- Hyperlink websiteLink = new Hyperlink("For more information about the project, visit our GitHub repository");
- websiteLink.setOnAction(e -> hostServices.showDocument("https://git.ntnu.no/Group-5/Help-Me-Help"));
-
- VBox content = new VBox(10, description, websiteLink);
- content.setPrefWidth(420);
-
- aboutUs.getDialogPane().setContentText(null);
- aboutUs.getDialogPane().setContent(content);
-
- // Optional: single Close button instead of OK
- aboutUs.getButtonTypes().setAll(ButtonType.CLOSE);
-
- aboutUs.showAndWait();
+ new AboutUsView(hostServices).displayAboutUs();
}
/**
diff --git a/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java b/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java
new file mode 100644
index 0000000..f25a95e
--- /dev/null
+++ b/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java
@@ -0,0 +1,55 @@
+package edu.group5.app.view.aboutuspage;
+
+import edu.group5.app.utils.ParameterValidator;
+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.VBox;
+
+/**
+ * A view for displaying information about the "Help Me Help" application and its creators.
+ * The view is presented as an informational dialog that includes a description of the app's mission
+ * and a hyperlink to the project's GitHub repository for more details. This page serves to provide
+ * users with background information about the application and its development team.
+ */
+public class AboutUsView {
+ private HostServices hostServices;
+
+ public AboutUsView(HostServices hostServices) {
+ ParameterValidator.objectChecker(hostServices, "HostServices");
+ this.hostServices = hostServices;
+ }
+
+ /**
+ * Displays the "About Us" information in an alert dialog.
+ */
+ public void displayAboutUs() {
+ Alert aboutUs = new Alert(Alert.AlertType.INFORMATION);
+ aboutUs.setTitle("About us");
+ aboutUs.setHeaderText("Help Me Help - About Us");
+
+ Label description = new Label(
+ "Help Me Help is a charity donation application designed to connect donors with organizations in need. " +
+ "Our mission is to make it easy for people to support causes they care about and make a positive impact in the world.\n\n" +
+ "This application was developed by Team 5 as part of a IDATT1005 course project at NTNU spring 2026."
+ );
+ description.setWrapText(true);
+ description.maxWidthProperty().bind(aboutUs.getDialogPane().widthProperty().subtract(40));
+
+ Hyperlink websiteLink = new Hyperlink("For more information about the project, visit our GitHub repository");
+ websiteLink.setOnAction(e -> hostServices.showDocument("https://git.ntnu.no/Group-5/Help-Me-Help"));
+
+ VBox content = new VBox(10, description, websiteLink);
+ content.setPrefWidth(420);
+
+ aboutUs.getDialogPane().setContentText(null);
+ aboutUs.getDialogPane().setContent(content);
+
+ // Optional: single Close button instead of OK
+ aboutUs.getButtonTypes().setAll(ButtonType.CLOSE);
+
+ aboutUs.showAndWait();
+ }
+}