diff --git a/google_checks.xml b/google_checks.xml deleted file mode 100644 index 2434e8d..0000000 --- a/google_checks.xml +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 5bbba22..0000000 --- a/pom.xml +++ /dev/null @@ -1,161 +0,0 @@ - - - - 4.0.0 - - edu.group5.app - Help-Me-Help - 1.0-SNAPSHOT - Help-Me-Help - https://git.ntnu.no/Group-5/Help-Me-Help - - - UTF-8 - 25 - 25.0.1 - 6.0.1 - - - - - - org.junit.jupiter - junit-jupiter - ${junit.version} - test - - - - - org.openjfx - javafx-controls - ${javafx.version} - win - - - org.openjfx - javafx-graphics - ${javafx.version} - win - - - - org.springframework.security - spring-security-crypto - 7.0.2 - - - - tools.jackson.core - jackson-databind - 3.1.0 - compile - - - org.springframework - spring-core - 6.2.0 - - - org.slf4j - slf4j-simple - 2.0.9 - - - com.h2database - h2 - 2.2.224 - runtime - - - org.jsoup - jsoup - 1.17.2 - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.14.1 - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.5.4 - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.4.2 - - - - edu.group5.app.App - - - - - - - - org.openjfx - javafx-maven-plugin - 0.0.8 - - edu.group5.app.App - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.12.0 - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.6.1 - - - package - - shade - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/NOTICE - META-INF/LICENSE - META-INF.versions.9.module-info - - - - - - edu.group5.app.App - - - - - - - - - diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java deleted file mode 100644 index 3aea332..0000000 --- a/src/main/java/edu/group5/app/App.java +++ /dev/null @@ -1,113 +0,0 @@ -package edu.group5.app; - -import edu.group5.app.control.NavigationController; -import edu.group5.app.model.AppState; -import edu.group5.app.model.donation.DonationRepository; -import edu.group5.app.model.donation.DonationService; -import edu.group5.app.model.organization.OrganizationRepository; -import edu.group5.app.model.organization.OrganizationScraper; -import edu.group5.app.model.organization.OrganizationService; -import edu.group5.app.model.user.UserRepository; -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; -import javafx.scene.layout.BorderPane; -import javafx.stage.Stage; - -import java.util.List; - -import java.util.logging.Logger; - -/** - * Main entry point for the Help-Me-Help charity donation application. - * Handles database connection, data loading, and application setup. - */ -public class App extends Application { - DbWrapper dbWrapper; - UserRepository userRepository; - DonationRepository donationRepository; - - BorderPane root; - AppState appState; - NavigationController nav; - - private Logger logger; - static int MAX_RETRIES = 3; - - @Override - public void init() { - this.logger = Logger.getLogger(App.class.getName()); - this.logger.info("Application starting"); - - this.dbWrapper = new DbWrapper(false); - OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all"); - - 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 - List userData = dbWrapper.importUsers(); - List donationData = dbWrapper.fetchAllDonations(); - dbWrapper.disconnect(); - - // Load organizations from API - Object[] organizationData = new Object[0]; - try { - if (orgApiWrapper.importData()) { - organizationData = orgApiWrapper.getData(); - } - } catch (InterruptedException e) { - System.err.println("Failed to load organization data: " + e.getMessage()); - } - - // Create scraper and repositories with fetched data - OrganizationScraper orgScraper = new OrganizationScraper(); - this.userRepository = new UserRepository(userData); - this.donationRepository = new DonationRepository(donationData); - OrganizationRepository organizationRepository = new OrganizationRepository(organizationData, orgScraper); - - // Create services (backend wiring) - UserService userService = new UserService(this.userRepository); - DonationService donationService = new DonationService(this.donationRepository, organizationRepository); - OrganizationService organizationService = new OrganizationService(organizationRepository, orgScraper); - this.root = new BorderPane(); - this.appState = new AppState(); - this.nav = new NavigationController(root, appState, userService, donationService, organizationService, this.getHostServices()); - } - - @Override - public void start(Stage stage) { - this.nav.showLoginPage(); - - Scene scene = new Scene(root, 1280, 720); - stage.getIcons().add(new Image(getClass().getResource("/header/images/hmh-logo.png").toExternalForm())); - stage.setTitle("Help-Me-Help"); - stage.setScene(scene); - stage.show(); - } - - @Override - public void stop() throws Exception { - super.stop(); - this.logger.info("Application stopping"); - this.dbWrapper.connect(); - this.dbWrapper.exportUsers(this.userRepository.export()); - this.dbWrapper.exportDonations(this.donationRepository.export()); - this.dbWrapper.disconnect(); - } - - public static void main(String[] args) { - launch(args); - } -} diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java deleted file mode 100644 index d3d4ebe..0000000 --- a/src/main/java/edu/group5/app/control/AuthController.java +++ /dev/null @@ -1,233 +0,0 @@ -package edu.group5.app.control; - -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.ButtonType; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -/** - * Controller responsible for authentication-related operations. - * - *

Coordinates between {@link AppState}, {@link NavigationController} - * and {@link UserService} to: - *

    - *
  • Sign-up a new user
  • - *
  • Login in a user
  • - *
  • Logout a user
  • - *
- *

- */ -public class AuthController { - private final AppState appState; - private final NavigationController nav; - 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}. - * - *
    - *
  • Validates the firstname, lastname, email and password fields
  • - *
  • Encrypts the password
  • - *
  • Invokes {@link UserService#registerUser(String, String, String, String, String)} to register the user
  • - *
- * - *

If the registration is successful, the user is stored in {@link AppState} and - * the application navigates to the home page. Otherwise, an error message - * is displayed in the provided view.

- * - * @param view the view used to display feedback to the user - * @param firstName the user's first name - * @param lastName the user's last name - * @param email the user's email - * @param passwordChars the user's password - */ - public void handleSignUp(SignUpPageView view, String firstName, String lastName, String email, char[] passwordChars) { - if (firstName == null || firstName.trim().isEmpty() - || lastName == null || lastName.trim().isEmpty() - || email == null || email.trim().isEmpty() - || passwordChars == null || passwordChars.length == 0) { - view.showError("All fields are required"); - return; - } - - // Checks if any input is too long. - if (firstName.length() > 32 || lastName.length() > 32 - || email.length() > 32 || passwordChars.length > 72) { - - HashMap> fields = new HashMap>(); - List fields32 = new ArrayList(); - List fields72 = new ArrayList(); - fields.put("32", fields32); - fields.put("72", fields72); - - if (firstName.length() > 32) { - fields32.add("First Name"); - } - if (lastName.length() > 32) { - fields32.add("Last Name"); - } - if (email.length() > 32) { - fields32.add("Email"); - } - if (passwordChars.length > 72) { - fields72.add("Password"); - } - - int length32 = fields.get("32").size(); - int length72 = fields.get("72").size(); - - String string32 = ""; - if (length32 > 0) { - if (length32 > 1) { - for (int i = 0; i < length32; i++) { - if (i == length32 - 1) { - string32 += String.format("and %s", fields.get("32").get(i)); - } else { - string32 += String.format("%s, ", fields.get("32").get(i)); - } - } - string32 = string32 + " must have lengths of 32 characters.\n"; - } else { - string32 = fields.get("32").getFirst() + " must have a length of 32 characters.\n"; - } - } - - String string72 = ""; - if (length72 > 0) { - if (length72 > 1) { - for (int i = 0; i < length72; i++) { - if (i == length72 - 1) { - string72 += String.format("and %s", fields.get("72").get(i)); - } else { - string72 += String.format("%s, ", fields.get("72").get(i)); - } - } - string72 = string72 + " must have lengths of 72 characters.\n"; - } else { - string72 = fields.get("72").getFirst() - + " must have a length of 72 characters.\n"; - } - } - - view.showError(string32 + string72 + "Try again."); - return; - } - - // Privacy policy pop-up. - Alert privacyPolicy = new Alert(Alert.AlertType.CONFIRMATION); - privacyPolicy.setTitle("Accept Privacy Policy"); - privacyPolicy.setHeaderText("Accept Privacy Policy"); - privacyPolicy.setContentText( - "Your user information like:\n" - + "Name and email—as well as donations tied to your account—" - + "will be saved locally on your machine.\n" - + "This information is only used to create your account," - + "and no data will be sold to third parties.\n" - + "By creating an account," - + "you accept the right of our app to store this information on your computer."); - - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - // Clears password char array after creating a hash. - String hashedPassword = encoder.encode(new String(passwordChars)); - for (int i = 0; i < passwordChars.length; i++) { - passwordChars[i] = '\u0000'; - } - - if (privacyPolicy.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { - boolean success = userService.registerUser( - "Customer", firstName, lastName, email, hashedPassword); - - if (success) { - - User user = userService.getUserByEmail(email); - appState.setCurrentUser(user); - nav.showHomePage(); - } else { - view.showError("Registration failed. Email may already be in use."); - } - } - } - - - /** - * Handles the login of a {@link User}. - * - *
    - *
  • Validates the email and password of the user
  • - *
  • Invokes {@link UserService#login(String, char[])} to login in the user
  • - *
- * - * If the login is successful, the user is stored in {@link AppState} and the - * application navigates to the home page. Otherwise, an error message is - * displayed within the provided view. - * - * @param view the view used to display feedback to the user - * @param email the user's email - * @param passwordChars the user's password - */ - public void handleLogin(LoginPageView view, String email, char[] passwordChars) { - if (email == null || email.trim().isEmpty() || passwordChars == null || passwordChars.length == 0) { - view.showError("Email and password are required"); - return; - } - - User user = userService.login(email, passwordChars); - - if (user != null) { - appState.setCurrentUser(user); - nav.showHomePage(); - } else { - view.showError("Invalid email or password"); - } - } - - /** - * Handles the logout of a {@link User}. - * - *

Clears states in {@link AppState} and the application - * navigates to the login page.

- */ - public void handleLogout() { - appState.setCurrentUser(null); - appState.setCurrentOrganization(null); - appState.setCurrentDonationAmount(null); - appState.setCurrentPaymentMethod(null); - nav.showLoginPage(); - } -} \ No newline at end of file diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java deleted file mode 100644 index 3ca392d..0000000 --- a/src/main/java/edu/group5/app/control/DonationController.java +++ /dev/null @@ -1,222 +0,0 @@ -package edu.group5.app.control; - -import edu.group5.app.model.AppState; -import edu.group5.app.model.donation.Donation; -import edu.group5.app.model.donation.DonationService; -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; -import java.util.Map; -import java.util.Set; - -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; - -/** - * Controller responsible for donation-related operations. - * - *

- * Coordinates between {@link AppState}, {@link DonationService} - * and {@link NavigationController} to: - *

    - *
  • Retrieve donation data for the current user
  • - *
  • Process new donations
  • - *
  • Handle navigation after donation completion
  • - *
- *

- */ -public class DonationController { - private final AppState appState; - private final NavigationController nav; - 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. - * - * @param userId the ID of the user - * @return a map of donations. - */ - public Map getUserDonations(int userId) { - ParameterValidator.intChecker(userId, "User ID"); - return service.getUserDonations(userId); - } - - /** - * Returns a set of unique organization IDs that the current user - * has donated to. - * - * @return a set of organization IDs - */ - public Set getUniqueOrganizationIDs() { - 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()) { - uniqueOrganizations.add(donation.organizationId()); - } - - return uniqueOrganizations; - } - - /** - * Processes a donation using data stored in {@link AppState}. - * - *

- *

    - *
  • Validates the current user, organization, amount and payment method
  • - *
  • Invokes {@link DonationService#donate(Customer, int, BigDecimal, String)} to create the donation
  • - *
  • Clears temporary donation state
  • - *
  • Navigates to the payment complete view
  • - *
- *

- */ - public void requestDonationConfirmation() { - // Get session data - User currentUser = appState.getCurrentUser(); - Organization currentOrg = appState.getCurrentOrganization(); - BigDecimal amount = appState.getCurrentDonationAmount(); - String paymentMethod = appState.getCurrentPaymentMethod(); - - // Validate before showing dialog - if (currentUser == null) { - showError("Error: No user logged in"); - return; - } - if (!(currentUser instanceof Customer)) { - showError("Error: Only customers can donate"); - return; - } - if (currentOrg == null) { - showError("Error: No organization selected"); - return; - } - if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { - showError("Please select a donation amount first"); - return; - } - - // Show confirmation dialog - Alert confirmDialog = new Alert(Alert.AlertType.CONFIRMATION); - confirmDialog.setTitle("Confirm Donation"); - confirmDialog.setHeaderText("Confirm Your Donation"); - confirmDialog.setContentText( - "Organization: " + currentOrg.name() + "\n" + - "Amount: " + amount + " kr\n" + - "Payment Method: " + paymentMethod + "\n\n" + - "Are you sure you want to proceed?" - ); - - // If user clicks OK, process donation - if (confirmDialog.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { - handleDonate(); - } - // If Cancel, dialog just closes and nothing happens - } - - private void handleDonate() { - // This now only handles the actual donation processing - User currentUser = appState.getCurrentUser(); - Organization currentOrg = appState.getCurrentOrganization(); - BigDecimal amount = appState.getCurrentDonationAmount(); - String paymentMethod = appState.getCurrentPaymentMethod(); - - if (!(currentUser instanceof Customer customer)) { - System.err.println("Error: Only customers can donate"); - return; - } - if (paymentMethod == null) { - System.out.println("Error: Invalid payment method"); - return; - } - - // Prevents donations that are too complex from being made - if (amount.stripTrailingZeros().precision() > 32 || amount.stripTrailingZeros().scale() > 16) { - this.showError("The number is too complex, please donate a smaller or less precise number"); - return; - } - - // Create donation via service - boolean success = service.donate( - customer, - currentOrg.orgNumber(), - amount, - paymentMethod - ); - - if (success) { - 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"); - errorAlert.setContentText(message); - errorAlert.showAndWait(); - } -} diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java deleted file mode 100644 index 8bbbbb3..0000000 --- a/src/main/java/edu/group5/app/control/NavigationController.java +++ /dev/null @@ -1,150 +0,0 @@ -package edu.group5.app.control; - -import edu.group5.app.model.AppState; -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; -import edu.group5.app.view.homepage.HomePageView; -import edu.group5.app.view.loginpage.LoginHeader; -import edu.group5.app.view.loginpage.LoginPageView; -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. - * 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; - private final Header header; - 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, 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(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(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(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(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(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(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(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() { - 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(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 deleted file mode 100644 index 49201aa..0000000 --- a/src/main/java/edu/group5/app/control/OrganizationController.java +++ /dev/null @@ -1,62 +0,0 @@ -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(AppState appState, OrganizationService service) { - ParameterValidator.objectChecker(appState, "AppState"); - ParameterValidator.objectChecker(service, "OrganizationService"); - this.appState = appState; - this.service = service; - } - - - /** - * Sets the current selected organization. - * @param org 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); - } - - public Map getTrustedOrganizations() { - return service.getTrustedOrganizations(); - } - - public CompletableFuture> getOrganizationsWithLogosAsync() { - return service.getTrustedOrganizationsWithLogosAsync(); - } -} diff --git a/src/main/java/edu/group5/app/model/AppState.java b/src/main/java/edu/group5/app/model/AppState.java deleted file mode 100644 index e17bda9..0000000 --- a/src/main/java/edu/group5/app/model/AppState.java +++ /dev/null @@ -1,86 +0,0 @@ -package edu.group5.app.model; - -import edu.group5.app.model.organization.Organization; -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 currentPaymentMethod; - - /** - * 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.currentPaymentMethod; - } - - /** - * Sets the current payment method. - * @param paymentMethod the payment method to set as the current payment method - */ - public void setCurrentPaymentMethod(String paymentMethod){ - this.currentPaymentMethod = paymentMethod; - } -} diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java deleted file mode 100644 index d36d0e2..0000000 --- a/src/main/java/edu/group5/app/model/DBRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -package edu.group5.app.model; - -import java.util.HashMap; -import java.util.Map; - -import edu.group5.app.utils.ParameterValidator; - -import java.util.List; - -/** - * Abstract base class for repositories that store their data - * in a database-related structure. - * - *

    - * Extends {@link Repository} and specifies that the content - * is stored as a {@link Map}. - *

    - */ -public abstract class DBRepository extends Repository { - protected final Map contentLock; - - /** - * Constructs a DBRepository with the given content. - * - * @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); - - /** - * Exports the repository content as a list of Object arrays, where each array - * represents a row of data. - * This method is intended for converting the repository content into a format - * suitable for database storage or export. - * - * @return a List of Object arrays representing the repository content for - * database export - */ - public abstract List export(); -} diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java deleted file mode 100644 index 8b4ec61..0000000 --- a/src/main/java/edu/group5/app/model/Repository.java +++ /dev/null @@ -1,26 +0,0 @@ -package edu.group5.app.model; - -import java.util.Map; - -import edu.group5.app.utils.ParameterValidator; - -/** - * 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; - /** - * Constructs a new Repository with the specified content. - * - * @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/Donation.java b/src/main/java/edu/group5/app/model/donation/Donation.java deleted file mode 100644 index 92adfbe..0000000 --- a/src/main/java/edu/group5/app/model/donation/Donation.java +++ /dev/null @@ -1,53 +0,0 @@ -package edu.group5.app.model.donation; - -import java.math.BigDecimal; -import java.sql.Timestamp; - -/** - * Represents a verified donation made by a user to an organization. - * @param donationId - the unique ID of this donation - * @param userId - the ID of the user making the donation - * @param organizationId - the ID of the organization receiving the donation - * @param amount - the donation amount - * @param date - the timestamp when the donation was made - * @param paymentMethod - the payment method used - */ -public record Donation( - int donationId, - int userId, - int organizationId, - BigDecimal amount, - Timestamp date, - String paymentMethod) { - - /** - * Constructor with throws. - * - * @param donationId - throws if donationID is negative or 0. - * @param userId - throws if userID is negative or 0. - * @param organizationId - throws if organizationID is negative or 0. - * @param amount - throws if amount is negative or null. - * @param date - throws if date is null. - * @param paymentMethod - throws if payment is null or blank. - */ - public Donation { - if (donationId <= 0) { - throw new IllegalArgumentException("Donation ID must be positive"); - } - if (userId <= 0) { - throw new IllegalArgumentException("User ID must be positive"); - } - if (organizationId <= 0) { - throw new IllegalArgumentException("Organization ID must be positive"); - } - if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { - throw new IllegalArgumentException("Amount must be positive and not null"); - } - if (date == null) { - throw new IllegalArgumentException("Date must not be null"); - } - if (paymentMethod == null || paymentMethod.isBlank()) { - throw new IllegalArgumentException("Payment method must not be empty"); - } - } -} diff --git a/src/main/java/edu/group5/app/model/donation/DonationRepository.java b/src/main/java/edu/group5/app/model/donation/DonationRepository.java deleted file mode 100644 index f1102ba..0000000 --- a/src/main/java/edu/group5/app/model/donation/DonationRepository.java +++ /dev/null @@ -1,206 +0,0 @@ -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; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.List; -import java.math.BigDecimal; -import java.sql.Timestamp; - -/** - * Repository class for Donation. - * - *

    - * Extends {@link DBRepository} and manages Donation entities. - *

    - */ -public class DonationRepository extends DBRepository { - private final HashMap content; - - /** - * Constructs DonationRepository from a list of Object[] rows from the database. - * - * @param rows List of Object[] representing donations from the DB. - * Each row must have 6 elements: - * [donationId, userId, organizationId, amount, date, paymentMethod] - * @throws IllegalArgumentException if the input list is null or any row is - * invalid - */ - public DonationRepository(List rows) { - super(new HashMap<>()); - ParameterValidator.objectChecker(rows, "List of donation rows"); - this.content = new HashMap<>(); - for (Object[] row : rows) { - if (row == null || row.length != 6) { - throw new IllegalArgumentException("Each row must contain exactly 6 elements"); - } - int donationId = (int) row[0]; - int customerId = (int) row[1]; - int organizationId = (int) row[2]; - BigDecimal amount = (BigDecimal) row[3]; - Timestamp date = (Timestamp) row[4]; - String paymentMethod = (String) row[5]; - - Donation donation = new Donation(donationId, customerId, organizationId, amount, date, paymentMethod); - this.content.put(donationId, donation); - } - this.updateContentLock(); - } - - @Override - protected void updateContentLock() { - synchronized (contentLock) { - this.contentLock.clear(); - this.contentLock.putAll(this.content); - } - } - - @Override - public List export() { - Map output = new HashMap<>(this.content); - for (int i : super.contentLock.keySet()) { - output.remove(i); - } - this.updateContentLock(); - return output.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map(entry -> { - Donation donation = entry.getValue(); - return new Object[] { - donation.donationId(), donation.userId(), - donation.organizationId(), donation.amount(), - donation.date(), donation.paymentMethod() }; - }) - .toList(); - } - - /** - * Retrieves a donation by its ID. - * - * @param donationId the ID of the donation to retrieve - * @return the Donation object with the specified ID, or null if not found - * @throws IllegalArgumentException if the donationId is not positive - */ - public Donation getDonationById(int donationId) { - ParameterValidator.intChecker(donationId, "Donation ID"); - return content.get(donationId); - } - - /** - * Generates the next donation ID based on the current maximum ID in the - * repository. - * - * @return the next donation ID to be used for a new donation - */ - public int getNextDonationId() { - return content.keySet().stream().max(Integer::compareTo).orElse(0) + 1; - } - - public Map getAllDonations() { - return new HashMap<>(content); - } - - /** - * Adds a new donation to the repository - *

    - * The donation is stored using its {@code donationId} as the key. - * If a donation with the same ID already exists, the donation - * will not be added. - *

    - * - * @param donation the donation to add - * @return {@code true} if the donation was successfully added, and - * {@code false} if a donation with the same ID already exists - */ - @Override - public boolean addContent(Donation donation) { - ParameterValidator.objectChecker(donation, "Donation"); - if (content.containsKey(donation.donationId())) { - return false; - } - this.content.put(donation.donationId(), donation); - return true; - } - - /** - * Returns all donations sorted by date (ascending). - * - *

    - * The returned map preserves the sorted order. - *

    - * - * @return a new {@link HashMap} containing the donations sorted by date - */ - public HashMap sortByDate() { - return content.entrySet().stream() - .sorted(Map.Entry.comparingByValue( - Comparator.comparing(Donation::date))) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (e1, e2) -> e1, - LinkedHashMap::new)); - } - - /** - * Returns all donations sorted by amount (ascending). - * - *

    - * The returned map preserves the sorted order from lowest to highest amount. - *

    - * - * @return a new {@link HashMap} containing the donations sorted by amount. - */ - public HashMap sortByAmount() { - return content.entrySet().stream() - .sorted(Map.Entry.comparingByValue( - Comparator.comparing(Donation::amount))) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (e1, e2) -> e1, - LinkedHashMap::new)); - } - - /** - * Returns all donations associated with a specific organization. - * - * @param orgNumber the organization ID to filter by - * @return a map containing all donations that belong to the given organization - * @throws IllegalArgumentException if the orgNumber is not positive - */ - public HashMap filterByOrganization(int orgNumber) { - ParameterValidator.intChecker(orgNumber, "Organization number"); - return content.entrySet() - .stream() - .filter(entry -> entry.getValue().organizationId() == orgNumber) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (e1, e2) -> e1, - LinkedHashMap::new)); - } - - /** - * Returns all donations made by a specific user. - * - * @param userId the user ID to filter by - * @return a map containing all donations that belong to the given user - * @throws IllegalArgumentException if the userId is not positive - */ - public HashMap filterByUser(int userId) { - ParameterValidator.intChecker(userId, "User ID"); - return content.entrySet().stream() - .filter(entry -> entry.getValue().userId() == userId) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (e1, e2) -> e1, - LinkedHashMap::new)); - } -} diff --git a/src/main/java/edu/group5/app/model/donation/DonationService.java b/src/main/java/edu/group5/app/model/donation/DonationService.java deleted file mode 100644 index 5eb964b..0000000 --- a/src/main/java/edu/group5/app/model/donation/DonationService.java +++ /dev/null @@ -1,78 +0,0 @@ -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; -import edu.group5.app.model.organization.OrganizationRepository; -import edu.group5.app.model.user.Customer; -import edu.group5.app.utils.ParameterValidator; - -/** - * DonationService class provides functionality for handling donations in the system. - * It interacts with the DonationRepository to manage donation records - * and the OrganizationRepository to validate organization information. - * The donate method allows a customer to make a donation to a specified organization, - * ensuring that the customer, organization, and donation amount are valid before processing the donation. - */ -public class DonationService { - - private final DonationRepository donationRepository; - private final OrganizationRepository organizationRepository; - - /** - * Constructor for DonationService. Initializes the service with the required repositories. - * @param donationRepository the repository for managing donation records - * @param organizationRepository the repository for managing organization information - * @throws IllegalArgumentException if either repository is null - */ - public DonationService(DonationRepository donationRepository, - OrganizationRepository organizationRepository) { - ParameterValidator.objectChecker(donationRepository, "DonationRepository"); - ParameterValidator.objectChecker(organizationRepository, "OrganizationRepository"); - this.donationRepository = donationRepository; - this.organizationRepository = organizationRepository; - } - - /** - * 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 Map getUserDonations(int userId) { - return donationRepository.filterByUser(userId); - } - - /** - * Gets all donations to a specific organization. - * @param organizationId the organization ID - * @return map of donations to that organization - */ - public Map getOrganizationDonations(int organizationId) { - return donationRepository.filterByOrganization(organizationId); - } - - /** - * Processes a donation from a customer to a specified organization with a given amount. - * Validates the customer, organization number, and donation amount before creating a donation record. - * @param customer the customer making the donation - * @param orgNumber the organization number to which the donation is made - * @param amount the amount of the donation - * @return true if the donation is successfully processed, false otherwise - */ - public boolean donate(Customer customer, int orgNumber, BigDecimal amount, String paymentMethod) { - if (customer == null || amount == null - || amount.compareTo(BigDecimal.ZERO) <= 0 || paymentMethod.isBlank()) { - return false; - } - Organization org = organizationRepository.findByOrgNumber(orgNumber); - if (org == null) { - return false; - } - Donation donation = - new Donation(donationRepository.getNextDonationId(), - customer.getUserId(), org.orgNumber(), amount, Timestamp.from(Instant.now()), paymentMethod); - this.donationRepository.addContent(donation); - return true; - } -} \ No newline at end of file diff --git a/src/main/java/edu/group5/app/model/organization/Organization.java b/src/main/java/edu/group5/app/model/organization/Organization.java deleted file mode 100644 index 4c66be0..0000000 --- a/src/main/java/edu/group5/app/model/organization/Organization.java +++ /dev/null @@ -1,66 +0,0 @@ -package edu.group5.app.model.organization; - -import java.util.Objects; - -import edu.group5.app.utils.ParameterValidator; - -/** - * Represents an organization. - * - *

    - * An organization is identified by an organization number, a name, - * trust status, website Url, pre-approval status, and a textual description, - * and a logo URL. - * - *

    - * Instances are validated on creation: - *

      - *
    • orgNumber must be non-negative
    • - *
    • name and websiteUrl must not be null or blank
    • - *
    • description must not be null
    • - *
    • logoUrl may be null if no logo is available
    • - *
    - */ -public record Organization( - int orgNumber, - String name, - boolean trusted, - String websiteUrl, - boolean isPreApproved, - String description, - String logoUrl) { - /** - * Creates a new organization. - * - * @param orgNumber the organization number; must be non-negative - * @param name the organization name; must not be null or blank - * @param trusted whether the organization is trusted - * @param websiteUrl the organization's website Url; must not be null or - * blank - * @param isPreApproved whether the organization is pre-approved - * @param description a textual description of the organization; must not be - * null - * @param logoUrl the URL to the organization's logo image; may be null - * @throws NullPointerException if name, websiteUrl or description is null - * @throws IllegalArgumentException if orgNumber is negative, or if name or - * websiteUrl is blank - */ - public Organization(int orgNumber, String name, boolean trusted, String websiteUrl, boolean isPreApproved, - String description, String logoUrl) { - ParameterValidator.intChecker(orgNumber, "Organization number"); - this.orgNumber = orgNumber; - this.name = Objects.requireNonNull(name, "name cannot be null"); - this.trusted = trusted; - this.websiteUrl = Objects.requireNonNull(websiteUrl, "websiteUrl cannot be null"); - this.isPreApproved = isPreApproved; - this.description = Objects.requireNonNull(description, "description cannot be null"); - this.logoUrl = logoUrl; - - if (name.isBlank()) { - throw new IllegalArgumentException("name cannot be blank"); - } - if (websiteUrl.isBlank()) { - throw new IllegalArgumentException("websiteUrl cannot be blank"); - } - } -} diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java deleted file mode 100644 index 5da9a97..0000000 --- a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java +++ /dev/null @@ -1,123 +0,0 @@ -package edu.group5.app.model.organization; - -import edu.group5.app.model.Repository; -import edu.group5.app.utils.ParameterValidator; -import tools.jackson.core.type.TypeReference; -import tools.jackson.databind.ObjectMapper; - -import java.util.HashMap; -import java.util.Map; - -/** - * Repository class for managing Organization entities. It provides methods to retrieve trusted organizations, - * find organizations by their organization number or name, and initializes the repository with input data. - * The repository uses a HashMap to store Organization objects for efficient retrieval based on their organization number. - * Delegates web scraping to OrganizationScraper for separation of concerns. - */ -public class OrganizationRepository extends Repository { - private final HashMap grandMap; - private final OrganizationScraper scraper; - - /** - * Initializes the repository with the given input data and scraper. - * Converts input into Organization objects and stores them in a map for efficient retrieval. - * The input is expected to be an array of objects, where each object contains - * the necessary information to create an Organization. - * - * @param input the input data used to populate the repository, must not be null - * @param scraper the OrganizationScraper to use for fetching web data, must not be null - * @throws IllegalArgumentException if input or scraper is null - */ - public OrganizationRepository(Object[] input, OrganizationScraper scraper) { - super(new HashMap<>()); - this.grandMap = new HashMap<>(); - ParameterValidator.objectChecker(input, "Input data"); - ParameterValidator.objectChecker(scraper, "Scraper"); - this.scraper = scraper; - - ObjectMapper mapper = new ObjectMapper(); - - for (Object obj : input) { - HashMap contentMap = - mapper.convertValue(obj, new TypeReference>() {}); - - Object orgNumberObj = contentMap.get("org_number"); - if (orgNumberObj == null) { - System.err.println("Skipping organization: missing org_number"); - continue; - } - String orgNumberStr = ((String) contentMap.get("org_number")).replaceAll("\\s", ""); - int orgNumber = Integer.parseInt(orgNumberStr); - String name = (String) contentMap.get("name"); - boolean trusted = "approved".equalsIgnoreCase((String) contentMap.get("status")); - String websiteURL = (String) contentMap.get("url"); - boolean isPreApproved = Boolean.TRUE.equals(contentMap.get("is_pre_approved")); - String description = scraper.fetchDescription(websiteURL); - description = description != null ? description : "Information about " + name; - Organization org = new Organization(orgNumber, name, trusted, websiteURL, isPreApproved, description, null); - - grandMap.put(org.orgNumber(), org); - } - } - - /** - * Exports the organization data in a format suitable for output, such as JSON. Each organization is represented as a map containing its attributes. - * @return an array of maps, where each map represents an organization with its attributes (org_number, name, status, url, is_pre_approved) - * @throws IllegalStateException if the repository is empty - */ - public Object[] export() { - if (grandMap.isEmpty()) { - throw new IllegalStateException("The repository is empty"); - } - return grandMap.values().stream() - .map(org -> { Map orgMap = new HashMap<>(); - orgMap.put("org_number", org.orgNumber()); - orgMap.put("name", org.name()); - orgMap.put("status", org.trusted() ? "approved" : "unapproved"); - orgMap.put("url", org.websiteUrl()); - orgMap.put("is_pre_approved", org.isPreApproved()); - return orgMap; - }) - .toArray(); - } - -/** - * Gets all trusted organizations in the repository - * @return all organizations with trusted = true - */ - public Map getTrustedOrganizations() { - Map trustedOrganizations = new HashMap<>(); - - grandMap.forEach((orgNr, org) -> { - if (org.trusted()) { - trustedOrganizations.put(orgNr, org); - } - }); - return trustedOrganizations; - } - - /** - * Finds an organization by its organization number - * @param orgNumber the organization number of the Organization - * @return the Organization with the given organization number, or null if not found - * @throws IllegalArgumentException if the organization number is not a positive integer - */ - public Organization findByOrgNumber(int orgNumber) { - ParameterValidator.intChecker(orgNumber, "Organization number"); - return grandMap.get(orgNumber); - } - - /** - * Finds an organization by its name, ignoring case - * @param name the name of the Organization - * @return the Organization with the given name, or null if not found - * @throws IllegalArgumentException if the name is null or empty - */ - public Organization findByOrgName(String name) { - ParameterValidator.stringChecker(name, "Organization name"); - return grandMap.values().stream() - .filter(org -> org.name().equalsIgnoreCase(name)) - .findFirst() - .orElse(null); - } -} diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java deleted file mode 100644 index beb16aa..0000000 --- a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java +++ /dev/null @@ -1,133 +0,0 @@ -package edu.group5.app.model.organization; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import java.util.stream.Collectors; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Handles web scraping of organization information from Innsamlingskontrollen. - * Responsible for fetching logos and descriptions from organization pages. - * All results are cached to avoid redundant network requests. - */ -public class OrganizationScraper { - private final Map logoCache = new ConcurrentHashMap<>(); - private final Map descriptionCache = new ConcurrentHashMap<>(); - - /** - * Fetches the description for the given URL by scraping all text content - * inside {@code
    }. Results are cached. - * - *

    Strategy:

    - *
      - *
    1. Tries to get all <p> tags (skipping the first one) and concatenates them
    2. - *
    3. If no paragraphs found, gets all text content from the section
    4. - *
    5. Returns null if section not found or is empty
    6. - *
    - * - * @param pageUrl the URL for the organization's page; may be null or blank - * @return the description text, or null if not found or pageUrl is invalid - */ - public String fetchDescription(String pageUrl) { - if (pageUrl != null && descriptionCache.containsKey(pageUrl)) { - return descriptionCache.get(pageUrl); - } - - try { - Document doc = Jsoup.connect(pageUrl) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") - .timeout(5000).get(); - - String description = parseDescription(doc); - if (!description.isBlank()) { - descriptionCache.put(pageUrl, description); - return description; - } - } catch (Exception e) { - System.out.println("Could not get description for: " + pageUrl); - } - return null; - } - - /** - * Parses the description from a Document by extracting text content - * from {@code
    }. - * - * @param doc the Document to parse - * @return the description text, or empty string if not found - */ - protected String parseDescription(Document doc) { - Element section = doc.selectFirst("section.information"); - if (section != null) { - section.select("div.extra-info").remove(); - section.select("a.read-more").remove(); - - // Extract all

    tags and

    elements as separate paragraphs - String description = section.select("p, div").stream() - .filter(el -> el.tagName().equals("p") || el.select("p").isEmpty()) - .filter(el -> !el.hasClass("extra-info") && !el.hasClass("logo")) - .map(Element::text) - .map(text -> text.replace("Les mer", "").trim()) - .filter(text -> !text.isBlank()) - .collect(Collectors.joining("\n\n")); - - // Fallback: if no paragraphs found, get all text from section - if (description.isBlank()) { - description = section.text().trim(); - } - description = description.replace("Les mer", "").trim(); - - // Only return if we found something meaningful - if (!description.isBlank()) { - return description; - } - } - return ""; - } - - /** - * Fetches the logo URL for the given page by scraping the {@code div.logo img} - * element. Results are cached so each URL is only fetched once. - * - * @param pageUrl the URL for the organization's page; may be null or blank - * @return the absolute logo URL, or null if not found or pageUrl is invalid - */ - public String fetchLogoUrl(String pageUrl) { - if (pageUrl != null && logoCache.containsKey(pageUrl)) { - return logoCache.get(pageUrl); - } - - try { - Document doc = Jsoup.connect(pageUrl) - .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") - .timeout(5000).get(); - - String logoUrl = parseLogoUrl(doc); - if (!logoUrl.isBlank()) { - logoCache.put(pageUrl, logoUrl); - return logoUrl; - } - } catch (Exception e) { - System.out.println("Could not get logo for: " + pageUrl); - } - return null; - } - - /** - * Parses the logo URL from a Document by extracting the image src - * from {@code div.logo img}. - * - * @param doc the Document to parse - * @return the absolute logo URL, or empty string if not found - */ - protected String parseLogoUrl(Document doc) { - Element img = doc.selectFirst("div.logo img"); - if (img != null) { - String logoUrl = img.absUrl("src"); - return logoUrl.isBlank() ? "" : logoUrl; - } - return ""; - } -} diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java deleted file mode 100644 index df4d04e..0000000 --- a/src/main/java/edu/group5/app/model/organization/OrganizationService.java +++ /dev/null @@ -1,116 +0,0 @@ -package edu.group5.app.model.organization; - -import java.util.stream.Collectors; - -import edu.group5.app.utils.ParameterValidator; - -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -/** - * Service class for managing organization-related operations. - * It interacts with the OrganizationRepository to retrieve organization information - * and contains business logic associated with organization management. - * - *

    It provides fetching logo URLs by delegating to OrganizationScraper.

    - * - * Fetched logo URLs are cached to avoid redundant network requests. - */ -public class OrganizationService { - private OrganizationRepository organizationRepository; - private OrganizationScraper scraper; - - /** - * Constructs an OrganizationService with the given OrganizationRepository and scraper. - * @param organizationRepository the OrganizationRepository to use for managing organization data; must not be null - * @param scraper the OrganizationScraper to use for fetching web data; must not be null - * @throws IllegalArgumentException if organizationRepository or scraper is null - */ - public OrganizationService(OrganizationRepository organizationRepository, OrganizationScraper scraper) { - ParameterValidator.objectChecker(organizationRepository, "OrganizationRepository"); - ParameterValidator.objectChecker(scraper, "Scraper"); - this.organizationRepository = organizationRepository; - this.scraper = scraper; - } - - /** - * Retrieves all trusted organizations. - * @return a map of trusted organizations by organization number - */ - public Map getTrustedOrganizations() { - return organizationRepository.getTrustedOrganizations().values().stream() - .sorted(Comparator.comparing(Organization::name)) - .collect(Collectors.toMap(Organization::orgNumber, - org -> org, (e1, e2) -> e1, LinkedHashMap::new)); - } - - /** - * Finds an organization by its organization number. - * @param orgNumber the organization number to find - * @return the Organization if found, null otherwise - */ - public Organization findByOrgNumber(int orgNumber) { - return organizationRepository.findByOrgNumber(orgNumber); - } - - /** - * Finds an organization by its name. - * @param name the name of the organization - * @return the Organization if found, null otherwise - */ - public Organization findByOrgName(String name) { - ParameterValidator.stringChecker(name, "name"); - return organizationRepository.findByOrgName(name); - } - - /** - * Fetches the logo URL for the given page by scraping the {@code div.logo img} - * element. Results are cached so each URL is only fetched once. - * - *

    - * Using Jsoup to web scrape through the URLs in the API. - *

    - * @param pageUrl the URL for the organization's page; may be null or blank - * @return the absolute logo URL, or null if not found or pageUrl is invalid - */ - - /** - * Fetches all trusted organizations with their logo URLs and descriptions. - * - *

    - * For each trusted organization, attempts to get its logo using the scraper. - * Creates a new Organization object including the logo URL (description is - * already fetched during repository initialization). - *

    - * @return a map of trusted organizations keyed by organization number, with logos included - */ - public Map getTrustedOrganizationsWithLogos() { - Map original = getTrustedOrganizations(); - return original.values().parallelStream() - .map(org -> new Organization( - org.orgNumber(), - org.name(), - org.trusted(), - org.websiteUrl(), - org.isPreApproved(), - org.description(), - scraper.fetchLogoUrl(org.websiteUrl()) - )) - .sorted(Comparator.comparing(Organization::name)) - .collect(Collectors.toMap(Organization::orgNumber, org -> org, (e1, e2) -> e1, LinkedHashMap::new)); - } - - /** - * Asynchronously fetches trusted organizations with logos. - * - *

    Runs in the background so the UI thread is no blocked. - * Returns a CompletableFuture that completes when all logos are loaded.

    - * - * @return a CompletableFuture containing a map of organizations with logos - */ - public CompletableFuture> getTrustedOrganizationsWithLogosAsync() { - return CompletableFuture.supplyAsync(this::getTrustedOrganizationsWithLogos); - } -} diff --git a/src/main/java/edu/group5/app/model/user/Customer.java b/src/main/java/edu/group5/app/model/user/Customer.java deleted file mode 100644 index d04139d..0000000 --- a/src/main/java/edu/group5/app/model/user/Customer.java +++ /dev/null @@ -1,58 +0,0 @@ -package edu.group5.app.model.user; - -import java.util.List; - -import edu.group5.app.utils.ParameterValidator; - -import java.util.ArrayList; - -/** - * Customer class represents a customer in the system. - * It extends the User class and includes additional functionality specific to customers, - * such as managing their preferences for organizations. - * Each customer has a map of preferences that associates organization numbers with the corresponding Organization objects. - */ -public class Customer extends User { - private List preferences; -/** - * Constructs a new Customer object for new registrations. - * The role is automatically set to "Customer" and the preferences list is initialized empty. - * - * @param userId the unique identifier for the user, must be positive - * @param firstName the first name of the customer - * @param lastName the last name of the customer - * @param email the email address of the customer - * @param passwordHash the hashed password of the customer - * @throws IllegalArgumentException if any parameter is invalid (null, empty, or userId ≤ 0) - */ -public Customer(int userId, String firstName, String lastName, - String email, String passwordHash) { - super(userId, firstName, lastName, email, passwordHash); - this.preferences = new ArrayList<>(); -} - - public List getPreferences() { - return preferences; - } - - @Override - public String getRole() { - return UserRepository.ROLE_CUSTOMER; - } - - public void addPreference(int orgNumber) { - ParameterValidator.intChecker(orgNumber,"Organization number"); - if (preferences.contains(orgNumber)) { - throw new IllegalArgumentException("Organization number already in preferences"); - } - preferences.add(orgNumber); - } - - public void removePreference(int orgNumber) { - ParameterValidator.intChecker(orgNumber, "Organization number"); - if (!preferences.contains(orgNumber)) { - throw new IllegalArgumentException("Organization number not found in preferences"); - } - preferences.remove(Integer.valueOf(orgNumber)); - } -} \ No newline at end of file diff --git a/src/main/java/edu/group5/app/model/user/User.java b/src/main/java/edu/group5/app/model/user/User.java deleted file mode 100644 index 806f1f7..0000000 --- a/src/main/java/edu/group5/app/model/user/User.java +++ /dev/null @@ -1,111 +0,0 @@ -package edu.group5.app.model.user; - -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -import edu.group5.app.utils.ParameterValidator; -/** - * User class represents a user in the system. It is an abstract class that will be extended by specific user types such as Donor, Recipient, and Admin. - * Each user has a unique userId, a role that defines their permissions in the system, and personal information such as first name, last name, email, and password hash. - * The constructor validates that all required fields are provided and throws an IllegalArgumentException if any of the fields are null or empty. - * This ensures that the User objects are always in a valid state when created. - * The class also includes a method to verify the user's password - * by comparing the provided plaintext password with the stored hashed password using BCrypt. - * - */ -public abstract class User { - private int userId; - private String firstName; - private String lastName; - private String email; - private String passwordHash; - - /** - * Constructor for User class. Validates that all required fields - * are provided and throws an IllegalArgumentException if any of the fields are null or empty. - * @param userId the unique identifier for the user, must be a positive integer - * @param firstName the first name of the user - * @param lastName the last name of the user - * @param email the email address of the user - * @param passwordHash the hashed password of the user, used for authentication purposes - */ - public User(int userId, String firstName, - String lastName, String email, String passwordHash) { - ParameterValidator.intChecker(userId, "User ID"); - ParameterValidator.stringChecker(firstName, "First name"); - ParameterValidator.stringChecker(lastName, "Last name"); - ParameterValidator.stringChecker(email, "Email"); - ParameterValidator.stringChecker(passwordHash, "Password hash"); - - this.userId = userId; - this.firstName = firstName.trim(); - this.lastName = lastName.trim(); - this.email = email.trim(); - this.passwordHash = passwordHash; -} - - /** - * Gets the unique identifier for the user. - * @return the userId of the user - */ - public int getUserId() { - return userId; - } - - /** - * Gets the role of the user (e.g., "Customer", "Admin"). - * @return the role of the user - */ - public abstract String getRole(); - - - /** - * Gets the first name of the user. - * @return the first name of the user - */ - public String getFirstName() { - return firstName; - } - - /** - * Gets the last name of the user. - * @return the last name of the user - */ - public String getLastName() { - return lastName; - } - - /** - * Gets the email address of the user. - * @return the email of the user - */ - public String getEmail() { - return email; - } - - /** - * Gets the hashed password of the user. - * This is used for authentication purposes and should not be exposed in plaintext. - * @return the password hash of the user - */ - public String getPasswordHash() { - return passwordHash; - } - - /** - * Verifies if the provided password matches the stored password hash. - * This method uses BCrypt to compare the plaintext password with the hashed password. - * @param password the plaintext password to verify - * @return true if the password is correct, false otherwise - * @throws IllegalArgumentException if the password is null, empty, or longer than 72 characters (BCrypt limit) - */ - public boolean verifyPassword(char[] password) { - if (password == null || password.length == 0) { - return false; - } - if (password.length > 72) { // BCrypt has a maximum password length of 72 bytes - throw new IllegalArgumentException("Password cannot be longer than 72 characters"); - } - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - return encoder.matches(new String(password), this.passwordHash); - } -} diff --git a/src/main/java/edu/group5/app/model/user/UserRepository.java b/src/main/java/edu/group5/app/model/user/UserRepository.java deleted file mode 100644 index aec3e9d..0000000 --- a/src/main/java/edu/group5/app/model/user/UserRepository.java +++ /dev/null @@ -1,153 +0,0 @@ -package edu.group5.app.model.user; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import edu.group5.app.model.DBRepository; -import edu.group5.app.utils.ParameterValidator; -/** - * Repository class for managing User entities. - * It provides methods to retrieve users, find users by their unique identifier - * or email address, and initializes the repository with input data. - * The repository uses a HashMap to store - * User objects for efficient retrieval based on their unique identifier. - */ -public class UserRepository extends DBRepository { - public final static String ROLE_CUSTOMER = "Customer"; - - /** - * Constructs UserRepository using Hashmap, - * and extends the content from DBRepository. - * - * @param rows the underlying map used to store users, - * where the key represents the user ID - */ - public UserRepository(List rows) { - super(new HashMap<>()); - ParameterValidator.objectChecker(rows, "List of User rows"); - for (Object[] row : rows) { - if (row == null || row.length != 6) { - throw new IllegalArgumentException("Each row must contain exactly 6 elements"); - } - int userId = (int) row[0]; - String role = (String) row[1]; - String firstName = (String) row[2]; - String lastName = (String) row[3]; - String email = (String) row[4]; - String passwordHash = (String) row[5]; - - User user; - if (ROLE_CUSTOMER.equalsIgnoreCase(role)) { - user = new Customer(userId, firstName, lastName, email, passwordHash); - } else { - throw new IllegalArgumentException("Unknown role: " + role); - } - this.content.put(userId, user); - } - this.updateContentLock(); - } - - @Override - protected void updateContentLock() { - synchronized (contentLock) { - this.contentLock.clear(); - this.contentLock.putAll(this.content); - } - } - - @Override - public List export() { - Map output = new HashMap<>(this.content); - for (int i : contentLock.keySet()) { - output.remove(i); - } - this.updateContentLock(); - return output.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map(entry -> { - User user = entry.getValue(); - return new Object[] { user.getUserId(), user.getRole(), - user.getFirstName(), user.getLastName(), - user.getEmail(), user.getPasswordHash() }; - }) - .toList(); - - } - - /** - * Retrieves a copy of the current users in the repository. - * @return a HashMap containing the current users, - * where the key is the user ID and the value is the User object - */ - public HashMap getUsers() { - return new HashMap<>(content); - } - - /** - * Retrieves a user by their unique identifier. - * - * @param userId the unique identifier of the user to retrieve - * @return the user with the specified ID, or {@code null} if no such user - * exists - * @throws IllegalArgumentException if the userId is not positive - */ - public User getUserById(int userId) { - ParameterValidator.intChecker(userId, "User ID"); - return content.get(userId); - } - - /** - * Generates the next user ID based on repository size. - * Uses size+1 and then moves forward if that ID is already taken. - * - * @return the next available user ID - * @throws IllegalStateException if no available user ID can be found - */ - public int getNextUserId() { - if (content.isEmpty()) { - return 1; - } - int maxKey = content.keySet().stream().max(Integer::compareTo).orElseThrow( - () -> new IllegalStateException("No keys found")); - int nextId = maxKey + 1; - return nextId; - } - - /** - * Adds a new user to the repository - *

    - * The user is stored using its {@code userId} as the key. - * If a user with the same ID already exists, the user - * will not be added. - *

    - * - * @param user the user to add - * @return {@code true} if the user was successfully added, and - * {@code false} if a user with the same ID already exists - */ - @Override - public boolean addContent(User user) { - ParameterValidator.objectChecker(user, "User"); - if (content.containsKey(user.getUserId())) { - return false; - } - this.content.put(user.getUserId(), user); - return true; - } - - /** - * Finds a user by their email address. - * - * @param email the email address of the user to find - * @return the user with the specified email address, or {@code null} if no such - * user exists - */ - public User findUserByEmail(String email) { - ParameterValidator.stringChecker(email, "Email"); - return content.values().stream() - .filter(user -> user.getEmail().equals(email)) - .findFirst() - .orElse(null); - } -} diff --git a/src/main/java/edu/group5/app/model/user/UserService.java b/src/main/java/edu/group5/app/model/user/UserService.java deleted file mode 100644 index e6439f1..0000000 --- a/src/main/java/edu/group5/app/model/user/UserService.java +++ /dev/null @@ -1,103 +0,0 @@ -package edu.group5.app.model.user; - -import edu.group5.app.utils.ParameterValidator; - -/** - * Service class for managing user-related operations, such as registration and - * login. - * It interacts with the UserRepository to perform these operations and contains - * the business logic - * associated with user management, including validation of input data and - * handling of user authentication. - */ -public class UserService { - private UserRepository userRepository; - - /** - * Constructs a UserService with the given UserRepository. - * - * @param userRepository the UserRepository to use for managing user data; must - * not be null - * @throws IllegalArgumentException if userRepository is null - */ - public UserService(UserRepository userRepository) { - ParameterValidator.objectChecker(userRepository, "UserRepository"); - this.userRepository = userRepository; - } - - /** - * Registers a new user with the given information. Validates the input data and - * creates a new User object - * based on the specified role. Currently supports registration for customers - * only. - * - * @param role the role of the user (e.g., "Customer"); must not be null - * or empty - * @param firstName the first name of the user; must not be null or empty - * @param lastName the last name of the user; must not be null or empty - * @param email the email address of the user; must not be null or empty - * @param passwordHash the hashed password of the user; must not be null or - * empty - * @return true if the user was successfully registered, false if any input is - * invalid or - * if the role is not supported - * @throws IllegalArgumentException if any of the input parameters are null or - * empty - * or if the role is not supported - */ - public boolean registerUser(String role, String firstName, String lastName, - String email, String passwordHash) { - if (role == null || role.trim().isEmpty() || - firstName == null || firstName.trim().isEmpty() || - lastName == null || lastName.trim().isEmpty() || - email == null || email.trim().isEmpty() || - passwordHash == null || passwordHash.trim().isEmpty() || - this.getUserByEmail(email) != null) { - return false; - } - User user; - if (role.equalsIgnoreCase("Customer")) { - user = new Customer(userRepository.getNextUserId(), firstName, lastName, email, passwordHash); - } else { - return false; - } - this.userRepository.addContent(user); - return true; - } - - /** - * Authenticates a user based on the provided email and password. - * - * @param email the email address of the user attempting to log in; must not - * be null or empty - * @param password the plaintext password of the user attempting to log in; must - * not be null or empty - * @return the authenticated User object if the login is successful - * (i.e., the user exists and the password is correct), null otherwise - * @throws IllegalArgumentException if email is null or empty, or if password is - * null or empty - */ - public User login(String email, char[] password) { - if (email == null || email.trim().isEmpty() || password == null || password.length == 0) { - return null; - } - User user = this.userRepository.findUserByEmail(email); - if (user != null && user.verifyPassword(password)) { - return user; - } - return null; - } - - /** - * Retrieves a user by email address. - * - * @param email the email address of the user to find; must not be null or empty - * @return the User object if found, null otherwise - */ - public User getUserByEmail(String email) { - if (email == null || email.trim().isEmpty()) { - return null; - } - return this.userRepository.findUserByEmail(email); - } -} diff --git a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java deleted file mode 100644 index d0239ae..0000000 --- a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java +++ /dev/null @@ -1,280 +0,0 @@ -package edu.group5.app.model.wrapper; - -import edu.group5.app.utils.ParameterValidator; -import java.math.BigDecimal; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A class for wrapping the database. It provides methods for connecting and disconnecting to the database, - * importing and exporting users and donations, and handling SQL exceptions. - * The class uses a logger to log important events and exceptions that occur during database operations. - */ -public class DbWrapper { - protected Connection connection; - private static final String CONNECTION_TYPE = "jdbc:h2:"; - private static final String DB_SCRIPT = "INIT=RUNSCRIPT FROM 'classpath:"; - private String connectionString; - private List users; - private List donations; - private Logger logger = Logger.getLogger(DbWrapper.class.getName()); - - /** - * The constructor, which constructs a String for connecting to the database. - * - * @param test Whether to construct the connection String for testing (in-memory) or not. - */ - public DbWrapper(boolean test) { - if (test) { - this.connectionString = CONNECTION_TYPE + "mem:test;" + DB_SCRIPT + "test_init.sql'"; - } else { - this.connectionString = CONNECTION_TYPE + "file:./help-me-help;" + DB_SCRIPT + "init.sql'"; - } - this.logger.info("connectionString constructed"); - } - - /** - * Connects to the database, and returns the result, logging failures. - * - * @return True if successful, false if not. - */ - public boolean connect() { - try { - this.connection = DriverManager.getConnection(this.connectionString); - if (this.connection.isValid(0)) { - this.logger.info("Database connected"); - return true; - } else { - this.logger.warning("Failed to connect to database"); - return false; - } - } catch (SQLException e) { - this.logger.log(Level.SEVERE, "Failed to connect to database due to exception", e); - return false; - } - } - - /** - * Disconnects the database connection, logging failures. - * - * @return True if successful, false if not. - */ - public boolean disconnect() { - // We are not interested in whether it fails to close, as we check its closed status later. - try { this.connection.close(); } catch (Exception e) {}; - try { - return this.connection.isClosed(); - } catch (Exception e) { - this.logger.log(Level.WARNING, "Failed to check if connection is closed due to exception", e); - return false; - } - } - - /** - * Closes queries and results. - * - * @param results The ResultSet to close, can be null. - * @param ps The PreparedStatement to close, can be null. - */ - private void close(ResultSet results, PreparedStatement ps) { - // This method can take null arguments, so an exception is expected. - try { results.close(); } catch (Exception e) {} - try { ps.close(); } catch (Exception e) {} - this.logger.info("results and ps closed"); - } - - /** - * Gets all users from the database. - * - * @return The users from the database returned as a List of Object arrays, where each Object - * array represents a user and a row in the users table in the database. - */ - public List importUsers() { - PreparedStatement ps = null; - ResultSet results = null; - try { - ps = this.connection.prepareStatement("SELECT * FROM users"); - results = ps.executeQuery(); - List data = new ArrayList(); - while (results.next()) { - data.add( - new Object[] { - results.getInt("user_id"), - results.getString("role"), - results.getString("first_name"), - results.getString("last_name"), - results.getString("email"), - results.getString("password_hash") - }); - } - this.users = data; - this.logger.info("Users imported"); - } catch (SQLException e) { - this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); - } finally { - this.close(results, ps); - } - return this.users; - } - - /** - * Puts new users into the database. - * - * @param data The new users to put into the database. Each Object array in the List is a new - * user to add as a row. - * @return The number of rows affected in the transaction. - * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not - * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in - * the database, or any of the values in the rows can't be cast to the correct data-types. - * @throws SQLException Is thrown when an unexpected exception like trying to export a number - * that's too big happens. - */ - public int exportUsers(List data) throws IllegalArgumentException, SQLException { - this.importUsers(); - - ParameterValidator.exportChecker(data, "data", this.users, 6); - - PreparedStatement ps = null; - int rowsAffected = 0; - try { - ps = this.connection.prepareStatement( - """ - INSERT INTO users - (user_id, role, first_name, last_name, email, password_hash) - VALUES - (?, ?, ?, ?, ?, ?) - """ - ); - for (Object[] row : data) { - try { - ps.setInt(1, (int) row[0]); - for (int i = 1; i < row.length; i++) { - ps.setString(i + 1, (String) row[i]); - } - } catch (Exception e) { - throw new IllegalArgumentException("One or more rows in data contains a wrong datatype"); - } - rowsAffected += ps.executeUpdate(); - } - this.logger.info("Users exported"); - } catch (SQLException e) { - this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); - throw new SQLException("An unexpected SQL exception has occurred. This might be caused by inserting an item that is too large."); - } finally { - this.close(null, ps); - } - return rowsAffected; - } - - /** - * Imports all donations. - * - * @return A List of Object arrays for each donation in the database. - */ - public List fetchAllDonations() { - return this.importDonations(0, true); - } - - /** - * Imports the donations of a specific user based on a given user_id. - * - * @param user_id The id of the user to get the donations of. - * @return A List of Object arrays for each donation in the database. - */ - public List importDonations(int user_id) { - return this.importDonations(user_id, false); - } - - private List importDonations(int user_id, boolean all) { - PreparedStatement ps = null; - ResultSet results = null; - try { - if (all) { - ps = this.connection.prepareStatement("SELECT * FROM donations"); - } else { - ps = this.connection.prepareStatement("SELECT * FROM donations WHERE user_id = ?"); - ps.setInt(1, user_id); - } - results = ps.executeQuery(); - List data = new ArrayList(); - while (results.next()) { - data.add( - new Object[] { - results.getInt(1), - results.getInt(2), - results.getInt(3), - results.getBigDecimal(4), - results.getTimestamp(5), - results.getString(6) - }); - } - this.donations = data; - this.logger.info("Donations imported"); - } catch (SQLException e) { - this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); - } finally { - this.close(results, ps); - } - return this.donations; - } - - /** - * Puts new donations into the database. - * - * @param data The new donation to put into the database. Each Object array in the List is a new - * donations to add as a row. - * @return The number of rows affected in the transaction. - * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not - * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in - * the database, or any of the values in the rows can't be cast to the correct data-types. - * @throws SQLException Is thrown when an unexpected exception like trying to export a number that's - * too big happens. - */ - public int exportDonations(List data) throws IllegalArgumentException, SQLException { - this.fetchAllDonations(); - - ParameterValidator.exportChecker(data, "data", this.donations, 6); - - PreparedStatement ps = null; - int rowsAffected = 0; - try { - ps = this.connection.prepareStatement( - """ - INSERT INTO donations - (donation_id, user_id, organization_id, amount, dating, payment_method) - VALUES - (?, (SELECT user_id FROM users WHERE user_id = ?), ?, ?, ?, ?) - """ - ); - for (Object[] row : data) { - try { - for (int i = 0; i < 3; i++) { - ps.setInt(i + 1, (int) row[i]); - } - ps.setBigDecimal(4, (BigDecimal) row[3]); - ps.setTimestamp(5, (Timestamp) row[4]); - ps.setString(6, (String) row[5]); - } catch (Exception e) { - throw new IllegalArgumentException("One or more rows in data contains a wrong datatype"); - } - rowsAffected += ps.executeUpdate(); - } - this.logger.info("Donations exported"); - } catch (SQLException e) { - this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); - throw new SQLException("An unexpected SQL exception has occurred. This might be caused by inserting an item that is too large."); - } finally { - this.close(null, ps); - } - return rowsAffected; - } - -} diff --git a/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java b/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java deleted file mode 100644 index d2ebb1a..0000000 --- a/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java +++ /dev/null @@ -1,78 +0,0 @@ -package edu.group5.app.model.wrapper; - -import edu.group5.app.utils.ParameterValidator; -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import tools.jackson.core.exc.StreamReadException; -import tools.jackson.databind.ObjectMapper; - -/** - * A Class for Wrapping an API. - * It provides methods for importing data from the API and accessing the imported data. - */ -public class OrgApiWrapper extends Wrapper { - private Object[] data; - private HttpClient client; - private HttpRequest request; - - /** - * The constructor, which takes a url String and constructs a URI and - * HttpRequest object from it. - * If the url is invalid, it will throw a fitting exception. - * - * @param urlString A string of the URL that's being connected to. - */ - public OrgApiWrapper(String urlString) { - ParameterValidator.stringChecker(urlString, "url"); - try { - URI uri = URI.create(urlString); - this.client = HttpClient.newHttpClient(); - this.request = HttpRequest.newBuilder() - .uri(uri) - .GET() - .build(); - } catch (IllegalArgumentException IAe) { - throw new IllegalArgumentException("url has to be valid"); - } - - } - - /** - * A method for importing data from the wrapped API. - * - * @return Returns a boolean, which indicates if the import was successful. Will - * be False if, for - * example, there is no internet connection. - * - * @throws InterruptedException This exception is thrown whenever the program is - * interrupted, like - * by ctrl + c. - */ - @Override - public boolean importData() throws InterruptedException { - try { - HttpResponse response = this.client.send( - this.request, HttpResponse.BodyHandlers.ofString()); - this.data = new ObjectMapper().readValue(response.body(), Object[].class); - return true; - } catch (IOException IOe) { - return false; - } catch (StreamReadException e) { - throw new StreamReadException("The URL leads to a website that can't be parsed"); - } - } - - /** - * A method for accessing the imported data. - * - * @return Returns an array with HashMaps, which is how data is structured in - * the API. - */ - @Override - public Object[] getData() { - return this.data; - } -} diff --git a/src/main/java/edu/group5/app/model/wrapper/Wrapper.java b/src/main/java/edu/group5/app/model/wrapper/Wrapper.java deleted file mode 100644 index cc54c29..0000000 --- a/src/main/java/edu/group5/app/model/wrapper/Wrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package edu.group5.app.model.wrapper; - -/** - * An abstract class for all Wrappers of datasets. - * This class defines the structure for dataset wrappers, which are responsible for importing data from various sources - * and providing access to the imported data. Each wrapper must implement the importData method to handle the specific - * data import logic and the getData method to return the imported data in a suitable format. - */ -abstract class Wrapper { - - protected Wrapper() { - } - - /** - * An abstract method for importing data from the dataset that child methods - * wrap. - * - * @return Returns a boolean, which indicates if the import was successful. Will - * be False if, for - * example, there is no internet connection. - * - * @throws InterruptedException This exception is thrown whenever the program is - * interrupted, like - * by ctrl + c. - */ - public abstract boolean importData() throws InterruptedException; - - /** - * An abstract method to access the imported data. - * - * @return Returns a fitting parsed Object directly from the dataset. - */ - public abstract Object getData(); -} diff --git a/src/main/java/edu/group5/app/utils/ParameterValidator.java b/src/main/java/edu/group5/app/utils/ParameterValidator.java deleted file mode 100644 index 28a8dc0..0000000 --- a/src/main/java/edu/group5/app/utils/ParameterValidator.java +++ /dev/null @@ -1,127 +0,0 @@ -package edu.group5.app.utils; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * ParameterValidator is a utility class that provides static methods for validating various types - * of parameters. - * It includes methods for checking strings, integers, objects, and BigDecimal values to ensure - * they meet specific criteria such as not being null, not being blank, or being positive. - */ -public final class ParameterValidator { - - /** - * Validates that a string parameter is not null and not blank. - * - * @param stringArg the string parameter to validate - * @param variableName the name of the variable being validated, used in exception messages - * @throws IllegalArgumentException if the string is null or blank - */ - public static final void stringChecker(String stringArg, String variableName) - throws IllegalArgumentException { - nullCheck(stringArg, variableName); - if (stringArg.trim().isBlank()) { - throw new IllegalArgumentException(String.format("%s can't be blank", variableName)); - } - } - - /** - * Validates that an integer parameter is not null and is a positive integer. - * - * @param intArg the integer parameter to validate - * @param variableName the name of the variable being validated, used in exception messages - * @throws IllegalArgumentException if the integer is null or not a positive integer - */ - public static final void intChecker(int intArg, String variableName) - throws IllegalArgumentException { - if (intArg <= 0) { - throw new IllegalArgumentException( - String.format("%s must be a positive integer", variableName) - ); - } - } - - /** - * Validates that an object parameter is not null. - * - * @param objectArg the object parameter to validate - * @param variableName the name of the variable being validated, used in exception messages - * @throws IllegalArgumentException if the object is null - */ - public static final void objectChecker(Object objectArg, String variableName) - throws IllegalArgumentException { - nullCheck(objectArg, variableName); - } - - /** - * Validates that a BigDecimal parameter is not null and is greater than zero. - * - * @param bigDecimalArg the BigDecimal parameter to validate - * @param variableName the name of the variable being validated, used in exception messages - * @throws IllegalArgumentException if the BigDecimal is null or not greater than zero - */ - public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String variableName) - throws IllegalArgumentException { - nullCheck(bigDecimalArg, variableName); - if (bigDecimalArg.compareTo(BigDecimal.ZERO) <= 0) { - throw new IllegalArgumentException(String.format("%s must be larger than 0", variableName)); - } - } - - /** - * Helper method to check if a variable is null and throw an IllegalArgumentException with a - * formatted message if it is. - * - * @param variable the variable to check for null - * @param variableName the name of the variable being checked, used in the exception message - * @throws IllegalArgumentException if the variable is null - */ - private static final void nullCheck(Object variable, String variableName) - throws IllegalArgumentException { - if (variable == null) { - throw new IllegalArgumentException(String.format("%s can't be null", variableName)); - } - } - - /** - * A method for checking if the data to be exported is valid. - * - * @param data The data to export. - * @param dataName The name of the variable of the data to export. - * @param oldData The existing data to compare to for checking row existence. - * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not - * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in - * the database, or any of the values in the rows can't be cast to the correct data-types. - */ - public static final void exportChecker( - List data, String dataName, List oldData, int rowLength - ) throws IllegalArgumentException { - objectChecker(data, dataName); - if (!data.isEmpty()) { - if (data.stream().anyMatch(i -> i.length != rowLength)) { - throw new IllegalArgumentException( - String.format("%s's arrays must have a length of %d", dataName, rowLength) - ); - } - if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) { - throw new IllegalArgumentException( - String.format("One or more rows in %s contains null values", dataName) - ); - } - - Set ids = new HashSet<>(); - if (data.stream().anyMatch(i -> !ids.add(i[0]))) { - throw new IllegalArgumentException("data can't contain duplicate rows"); - } - if (oldData.size() > 0) { - if (oldData.stream().anyMatch(i -> !ids.add(i[0]))) { - throw new IllegalArgumentException("data can't contain existing rows"); - } - } - } - } -} diff --git a/src/main/java/edu/group5/app/view/Header.java b/src/main/java/edu/group5/app/view/Header.java deleted file mode 100644 index 6bc6133..0000000 --- a/src/main/java/edu/group5/app/view/Header.java +++ /dev/null @@ -1,86 +0,0 @@ -package edu.group5.app.view; - -import edu.group5.app.control.NavigationController; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.*; - -/** - * A main header for the app. - * - *

    The header consists of a logo button to homepage, - * a navigation bar with buttons to home page, causes page, - * and about us page. The header also has a profile button - * in the upper right corner.

    - */ -public class Header extends BorderPane { - private final NavigationController controller; - - public Header(NavigationController controller) { - this.controller = controller; - getStylesheets().add(getClass().getResource("/header/header.css").toExternalForm()); - setId("header"); - - setLeft(getLogoSection()); - setCenter(getNavBar()); - setRight(getProfileSection()); - } - - private StackPane getLogoSection() { - StackPane logoSection = new StackPane(); - logoSection.setId("logo-section"); - logoSection.setAlignment(Pos.CENTER); - logoSection.setOnMouseClicked(e -> controller.showHomePage()); - logoSection.setStyle("-fx-cursor: hand;"); - - ImageView logo = new ImageView( - new Image(getClass().getResource("/header/images/hmh-logo.png").toExternalForm()) - ); - logo.setFitHeight(60); - logo.setPreserveRatio(true); - - logoSection.getChildren().add(logo); - return logoSection; - } - - private HBox getNavBar() { - HBox navbar = new HBox(); - navbar.setId("navbar"); - navbar.setAlignment(Pos.CENTER); - navbar.setSpacing(10); - - Button home = new Button("Home"); - home.setOnAction(e -> controller.showHomePage()); - home.setStyle("-fx-cursor: hand;"); - - Button causes = new Button("Causes"); - causes.setOnAction(e -> controller.showCausesPage()); - causes.setStyle("-fx-cursor: hand;"); - - Button about = new Button("About us"); - about.setOnAction(e -> controller.showAboutUsPage()); - about.setStyle("-fx-cursor: hand;"); - - navbar.getChildren().addAll(home, causes, about); - return navbar; - } - - private StackPane getProfileSection() { - StackPane profileSection = new StackPane(); - profileSection.setId("profile-section"); - profileSection.setAlignment(Pos.CENTER); - profileSection.setOnMouseClicked(e -> controller.showUserPage()); - profileSection.setStyle("-fx-cursor: hand;"); - - ImageView avatar = new ImageView( - new Image(getClass().getResource("/header/images/avatar.png").toExternalForm()) - ); - avatar.setFitHeight(60); - avatar.setPreserveRatio(true); - - profileSection.getChildren().add(avatar); - return profileSection; - } -} diff --git a/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java b/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java deleted file mode 100644 index f25a95e..0000000 --- a/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java +++ /dev/null @@ -1,55 +0,0 @@ -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(); - } -} diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java deleted file mode 100644 index 0bdddbc..0000000 --- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java +++ /dev/null @@ -1,246 +0,0 @@ -package edu.group5.app.view.causespage; - -import edu.group5.app.model.organization.Organization; -import edu.group5.app.utils.ParameterValidator; -import edu.group5.app.control.NavigationController; -import edu.group5.app.control.OrganizationController; -import javafx.application.Platform; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.layout.*; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.Comparator; - -/** - * A view for the causes page. - * - *

    This page allows users to browse and search - * for organizations they may want to donate to. - * Organizations are displayed in a grid layout, - * with four organizations per row. Each organization - * is represented as a clickable card containing its name and logo, - * which navigates to the organization's detail page.

    - * - *

    The page includes a search field that filters the - * displayed organizations based on user input.

    - * - *

    Logos are fetched asynchronously, and a loading indicator - * is shown while the data is being retrieved. If a logo cannot be loaded, - * a fallback "no image" is displayed.

    - */ -public class CausesPageView extends BorderPane { - private final NavigationController nav; - private final OrganizationController orgController; - - private GridPane organizationGrid; - private Map allOrganizations; - private Map cardCache = new HashMap<>(); - - public CausesPageView(NavigationController nav, OrganizationController orgController) { - ParameterValidator.objectChecker(nav, "NavigationController"); - ParameterValidator.objectChecker(orgController, "OrganizationController"); - - this.nav = nav; - this.orgController = orgController; - - getStylesheets().add(getClass().getResource("/browsepage/browsepage.css").toExternalForm()); - setCenter(createBody()); - } - - private BorderPane createBody() { - BorderPane bodyRoot = new BorderPane(); - bodyRoot.setTop(createSearchSection()); - - ScrollPane body = new ScrollPane(); - body.setId("body"); - body.setFitToWidth(true); - body.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - body.setStyle("-fx-focus-color: transparent; -fx-faint-focus-color: transparent;"); - - VBox vBox = new VBox(); - vBox.setStyle("-fx-padding: 10;"); - vBox.setSpacing(10); - vBox.setMaxWidth(Double.MAX_VALUE); - - // Load organizations INSTANTLY from cache - allOrganizations = orgController.getTrustedOrganizations(); - - vBox.getChildren().add(createOrganizationSection(null)); - body.setContent(vBox); - bodyRoot.setCenter(body); - - // Build a map of org ID -> card for quick lookup - Map cardMap = new HashMap<>(); - for (var node : organizationGrid.getChildren()) { - if (node instanceof OrganizationCard card) { - cardMap.put(card.getOrganization().orgNumber(), card); - } - } - - // Fetch logos and update existing cards (don't rebuild grid) - orgController.getOrganizationsWithLogosAsync() - .thenAccept(orgs -> {this.allOrganizations = orgs; - Platform.runLater(() -> { - for (var entry : orgs.entrySet()) { - OrganizationCard card = cardMap.get(entry.getKey()); - if (card != null && entry.getValue().logoUrl() != null) { - card.updateLogo(entry.getValue().logoUrl()); - } - } - Organization currentOrg = orgController.getCurrentOrganization(); - if (currentOrg != null && orgs.containsKey(currentOrg.orgNumber())) { - orgController.setCurrentOrganization(orgs.get(currentOrg.orgNumber())); - } - }); - }); - - return bodyRoot; - } - - private HBox createSearchSection() { - HBox searchSection = new HBox(); - TextField searchField = new TextField(); - searchField.setPromptText("Search organizations..."); - - // Add listener for search text changes - searchField.textProperty().addListener((obs, oldVal, newVal) -> { - updateOrganizationGrid(newVal.trim()); - }); - - searchSection.getChildren().add(searchField); - return searchSection; - } - - private GridPane createOrganizationSection(String searchTerm) { - GridPane grid = new GridPane(); - grid.setId("card-grid"); - grid.setHgap(20); - grid.setVgap(20); - grid.setStyle("-fx-padding: 0;"); - grid.setMaxWidth(Double.MAX_VALUE); - - // Store reference for later updates - if (organizationGrid == null) { - organizationGrid = grid; - } - - if (allOrganizations == null) { - allOrganizations = orgController.getTrustedOrganizations(); - - //Show loading text while organizations and logos are fetched - grid.add(new javafx.scene.control.Label("Loading..."), 0, 0); - - //Fetch trusted organizations with logos asynchronously (runs in background) - orgController.getOrganizationsWithLogosAsync() - .thenAccept(orgs -> { - this.allOrganizations = orgs; - - // Update UI when data is ready - Platform.runLater(() -> updateOrganizationGrid("")); - }); - return grid; - } - - Map organizations = new HashMap<>(); - if (searchTerm != null && !searchTerm.isEmpty()) { - // Filter organizations by search term - organizations = filterOrganizations(searchTerm); - } else { - organizations = allOrganizations; - } - - int column = 0; - int row = 0; - - for (Organization org : organizations.values()) { - //Adds default text if organization does not have any - String img = (org.logoUrl() != null && !org.logoUrl().isBlank()) - ? org.logoUrl() - : null; - - OrganizationCard card = new OrganizationCard(nav, orgController, org, img); - - grid.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); - grid.getColumnConstraints().add(col); - } - - // Store reference for later updates - if (organizationGrid == null) { - organizationGrid = grid; - } - - return grid; - } - - private Map filterOrganizations(String searchTerm) { - // If no search term, return all organizations - if (searchTerm == null || searchTerm.isEmpty()) { - return allOrganizations; - } - - String lowerSearchTerm = searchTerm.toLowerCase(); - return allOrganizations.values().stream() - .filter(org -> org.name().toLowerCase().contains(lowerSearchTerm)) - .sorted(Comparator.comparing(Organization::name)) - .collect(Collectors.toMap( - Organization::orgNumber, - org -> org, - (e1, e2) -> e1, - LinkedHashMap::new - )); - } - - - private void updateOrganizationGrid(String searchTerm) { - 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(); - - 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 deleted file mode 100644 index 483d0e7..0000000 --- a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java +++ /dev/null @@ -1,143 +0,0 @@ -package edu.group5.app.view.causespage; - -import edu.group5.app.control.NavigationController; -import edu.group5.app.control.OrganizationController; -import edu.group5.app.model.organization.Organization; -import javafx.geometry.Pos; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; - -/** - * OrganizationCard represent a single organization card - * in the causes page. - * - *

    The card displays the organization's logo, name, and verification - * checkmark. If no logo is available, a fallback text ("No image") is shown.

    - * - *

    The card is clickable. When pressed it navigates - * to the organization's detail page.

    - */ -public class OrganizationCard extends VBox { - private final Organization organization; - private final NavigationController nav; - private final OrganizationController organizationController; - private StackPane imageContainer; - private String currentLogoUrl; - - 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()); - - imageContainer = createImageContainer(img); - getChildren().addAll( - imageContainer, - orgName(org.name()), - checkMarkContainer() - ); - - setOnMouseClicked(e -> { - organizationController.setCurrentOrganization(getOrganizationWithCurrentLogo()); - nav.showOrganizationPage(); - }); - - setSpacing(10); - setFillWidth(true); - setAlignment(Pos.CENTER); - } - - public Organization getOrganization() { - return organization; - } - - public void updateLogo(String logoUrl) { - this.currentLogoUrl = logoUrl; - if (imageContainer == null) return; - imageContainer.getChildren().clear(); - if (logoUrl != null && !logoUrl.isBlank()) { - ImageView logo = new ImageView(new Image(logoUrl, 80, 80, true, true, true)); - logo.setId("logo"); - logo.setSmooth(true); - logo.setPreserveRatio(true); - logo.setFitHeight(80); - logo.setFitWidth(80); - imageContainer.getChildren().add(logo); - } - } - - private Organization getOrganizationWithCurrentLogo() { - if (currentLogoUrl == null) { - return organization; - } - return new Organization( - organization.orgNumber(), - organization.name(), - organization.trusted(), - organization.websiteUrl(), - organization.isPreApproved(), - organization.description(), - currentLogoUrl - ); - } - - private StackPane createImageContainer(String img) { - StackPane imageContainer = new StackPane(); - imageContainer.setId("imageContainer"); - - imageContainer.setPrefHeight(80); - imageContainer.setPrefWidth(80); - imageContainer.setMaxWidth(Double.MAX_VALUE); - - - if (img != null && !img.isBlank()) { - ImageView logo = new ImageView(new Image(img, 80, 80, true, true, true)); - logo.setId("logo"); - logo.setSmooth(true); - logo.setPreserveRatio(true); - logo.setFitHeight(80); - logo.setFitWidth(80); - - imageContainer.getChildren().add(logo); - } else { - StackPane placeholder = new StackPane(); - placeholder.setPrefSize(80, 80); - - Text text = new Text("No image"); - text.setStyle("-fx-font-size: 10;"); - - placeholder.getChildren().add(text); - imageContainer.getChildren().add(placeholder); - } - - return imageContainer; - } - - private Text orgName(String text) { - Text orgName = new Text(text); - orgName.setId("orgName"); - orgName.setWrappingWidth(150); - return orgName; - } - - private StackPane checkMarkContainer() { - StackPane checkMarkContainer = new StackPane(); - checkMarkContainer.setId("checkMarkContainer"); - checkMarkContainer.setAlignment(Pos.CENTER_LEFT); - - ImageView verifiedCheck = new ImageView( - new Image(getClass().getResource("/verified_check.png").toExternalForm()) - ); - - verifiedCheck.setPreserveRatio(true); - verifiedCheck.setSmooth(true); - - checkMarkContainer.getChildren().add(verifiedCheck); - return checkMarkContainer; - } -} diff --git a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java deleted file mode 100644 index ed0288c..0000000 --- a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java +++ /dev/null @@ -1,257 +0,0 @@ -package edu.group5.app.view.donationpage; - -import edu.group5.app.control.DonationController; -import edu.group5.app.control.NavigationController; -import edu.group5.app.utils.ParameterValidator; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.TextField; -import javafx.scene.control.Button; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.TilePane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import javafx.scene.text.TextAlignment; -import javafx.scene.Node; - -import java.math.BigDecimal; -import java.util.Objects; - -/** - * A view for the Donation Page. - * In the donation page a user can donate a chosen amount - * to the organization they have chosen to donate to. - * - *

    The donation page consists of payment amount buttons, - * payment method buttons, donation button, and a back to organization page button.

    - */ -public class DonationPageView extends BorderPane { - private final NavigationController nav; - private final DonationController donationController; - - private Node currentlySelected = null; - private TextField customAmountField; - private Node selectedPaymentMethod = null; - private Button donateBtn; - - public DonationPageView(NavigationController nav, DonationController donationController) { - ParameterValidator.objectChecker(nav, "NavigationController"); - ParameterValidator.objectChecker(donationController, "DonationController"); - - this.nav = nav; - this.donationController = donationController; - - getStylesheets().add(Objects.requireNonNull(getClass().getResource("/donationpage/donation.css")).toExternalForm()); - - VBox content = new VBox(); - content.getChildren().addAll(createBackButton(), createDonationGrid(), createPaymentMethodSection(), createDonateSection()); - - content.setOnMouseClicked(e -> { - if (e.getTarget() == content) { - clearSelection(); - } - }); - setCenter(content); - - } - - private HBox createBackButton() { - Button backBtn = new Button("←"); - backBtn.getStyleClass().add("back-button"); - backBtn.setOnAction(e -> nav.showOrganizationPage()); - - HBox container = new HBox(backBtn); - container.setPadding(new Insets(10, 0, 0, 10)); - return container; - } - - private TilePane createDonationGrid(){ - TilePane body = new TilePane(); - body.setAlignment(Pos.CENTER); - body.setPrefColumns(3); - body.setPrefRows(2); - body.setPrefTileWidth(300); - body.setPrefTileHeight(150); - body.setHgap(20); - body.setVgap(20); - body.setPadding(new Insets(40, 40, 20, 40)); - - String[][] labels = { - {"Tiny", "50 kr"}, - {"Small", "100 kr"}, - {"Medium", "250 kr"}, - {"Big", "500 kr"}, - {"Grandiose", "1000 kr"}, - }; - for (String[] label : labels) { - body.getChildren().add(createDonationButton(label[0], label[1])); - } - body.getChildren().add(createCustomButton()); - - return body; - } - - public Button createDonationButton(String title, String amount) { - Button button = new Button(title + "\n" + amount); - button.setWrapText(true); - button.setTextAlignment(TextAlignment.CENTER); - button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); - button.getStyleClass().add("donation-button"); - - BigDecimal parsedAmount = parseAmount(amount); - button.setUserData(parsedAmount); - - button.setOnAction(e -> selectDonation(button)); - return button; - } - - private VBox createCustomButton() { - Text titleText = new Text("Custom Donation"); - titleText.getStyleClass().add("donation-title"); - - HBox amountRow = new HBox(4); - amountRow.setAlignment(Pos.CENTER); - - Text krText = new Text("kr"); - krText.getStyleClass().add("donation-amount"); - - this.customAmountField = new TextField(); - TextField amountField = customAmountField; - amountField.getStyleClass().add("donation-input"); - amountField.setPromptText("Enter amount"); - - amountRow.getChildren().addAll(amountField, krText); - - VBox box = new VBox(6, titleText, amountRow); - box.setAlignment(Pos.CENTER); - box.getStyleClass().add("donation-button"); - - box.setOnMouseClicked(e -> { - selectDonation(box); - amountField.requestFocus(); - }); - - amountField.setOnMouseClicked(e -> - { - selectDonation(box); - amountField.requestFocus(); - }); - - // NEW: On text field input - update the amount in real-time - amountField.textProperty().addListener((obs, oldVal, newVal) -> { - if (!newVal.trim().isEmpty()) { - try { - BigDecimal amount = new BigDecimal(newVal.trim()); - box.setUserData(amount); - updateDonationAmount(amount); - } catch (NumberFormatException ignored) { - // User is still typing, silently ignore - } - } else { - box.setUserData(null); - if (currentlySelected == box) { - updateDonationAmount(null); - } - } - }); - return box; - } - - private HBox createDonateSection() { - donateBtn = new Button("Donate"); - donateBtn.getStyleClass().add("donate-button"); - donateBtn.setDisable(true); - donateBtn.setOnAction(e -> donationController.requestDonationConfirmation()); - - Button clearBtn = new Button("Clear"); - clearBtn.getStyleClass().add("clear-button"); - clearBtn.setOnAction(e -> clearSelection()); - - HBox section = new HBox(20, clearBtn, donateBtn); - section.setAlignment(Pos.CENTER); - section.setPadding(new Insets(20, 0, 30, 0)); - return section; - } - - public HBox createPaymentMethodSection() { - Button appleBtn = new Button("Apple Pay"); - Button vippsBtn = new Button("Vipps"); - Button visaBtn = new Button("Visa"); - - appleBtn.setUserData("Apple Pay"); - vippsBtn.setUserData("Vipps"); - visaBtn.setUserData("Visa"); - - for (Button btn : new Button[]{appleBtn, vippsBtn, visaBtn}) { - btn.getStyleClass().add("payment-method-button"); - btn.setOnAction(e -> selectPaymentMethod(btn)); - } - - HBox sectionPm = new HBox(appleBtn, vippsBtn, visaBtn); - sectionPm.setAlignment(Pos.CENTER); - sectionPm.setSpacing(20); - sectionPm.setPadding(new Insets(20, 20, 20, 20)); - return sectionPm; - } - - private void selectDonation(Node element) { - if (currentlySelected != null) { - currentlySelected.getStyleClass().remove("donation-button-selected"); - } - currentlySelected = element; - currentlySelected.getStyleClass().add("donation-button-selected"); - - BigDecimal amount = (BigDecimal) element.getUserData(); - updateDonationAmount(amount); - updateDonationButtonState(); - } - - private void selectPaymentMethod(Node element) { - if (selectedPaymentMethod != null) { - selectedPaymentMethod.getStyleClass().remove("payment-method-selected"); - } - selectedPaymentMethod = element; - selectedPaymentMethod.getStyleClass().add("payment-method-selected"); - - String paymentMethod = (String) element.getUserData(); - donationController.setPaymentMethod(paymentMethod); - updateDonationButtonState(); - } - - private void clearSelection() { - if (currentlySelected != null) { - currentlySelected.getStyleClass().remove("donation-button-selected"); - currentlySelected = null; - updateDonationAmount(null); - } - - if (selectedPaymentMethod != null) { - selectedPaymentMethod.getStyleClass().remove("payment-method-selected"); - selectedPaymentMethod = null; - } - - if (customAmountField != null) { - customAmountField.clear(); - } - - updateDonationButtonState(); - } - - private void updateDonationAmount(BigDecimal amount) { - donationController.setDonationAmount(amount); - } - - private BigDecimal parseAmount(String amountStr) { - try { - return new BigDecimal(amountStr.replace("kr", "").trim()); - } catch (NumberFormatException e) { - return BigDecimal.ZERO; - } - } - - private void updateDonationButtonState() { - donateBtn.setDisable(currentlySelected == null || selectedPaymentMethod == null); - } - -} \ No newline at end of file diff --git a/src/main/java/edu/group5/app/view/donationpage/PaymentCompletePageView.java b/src/main/java/edu/group5/app/view/donationpage/PaymentCompletePageView.java deleted file mode 100644 index e20cfee..0000000 --- a/src/main/java/edu/group5/app/view/donationpage/PaymentCompletePageView.java +++ /dev/null @@ -1,54 +0,0 @@ -package edu.group5.app.view.donationpage; - -import edu.group5.app.control.NavigationController; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.VBox; - -import java.util.Objects; - -/** - * A view for the payment complete page. - * When a user have donated an amount, this page opens up. - * - *

    The page consist of an image that says "Tank You For The Donation", - * and a "back to home" button at the bottom center.

    - */ -public class PaymentCompletePageView extends BorderPane { - private final NavigationController nav; - - public PaymentCompletePageView(NavigationController nav) { - this.nav = nav; - getStylesheets().add(getClass().getResource("/donationpage/paymentcomplete.css").toExternalForm()); - - VBox content = new VBox(20); - content.setAlignment(Pos.CENTER); - content.setPadding(new Insets(40)); - content.getChildren().addAll(getImageSection(), getHomeBtn()); - setCenter(content); - } - public VBox getImageSection() { - Image image = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/donationpage/Payment Complete.png"))); - ImageView imageView = new ImageView(image); - imageView.setPreserveRatio(true); - - imageView.fitWidthProperty().bind(widthProperty()); - imageView.setFitHeight(500); - - VBox imageSection = new VBox(imageView); - imageSection.setAlignment(Pos.CENTER); - return imageSection; - } - - public Button getHomeBtn() { - Button home = new Button("Home"); - home.setOnAction(e -> nav.showHomePage()); - home.setId("home-button"); - return home; - } - -} diff --git a/src/main/java/edu/group5/app/view/homepage/HomePageView.java b/src/main/java/edu/group5/app/view/homepage/HomePageView.java deleted file mode 100644 index 137e0b1..0000000 --- a/src/main/java/edu/group5/app/view/homepage/HomePageView.java +++ /dev/null @@ -1,74 +0,0 @@ -package edu.group5.app.view.homepage; - -import edu.group5.app.control.NavigationController; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.ScrollPane; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; - -/** - * A view for the homepage. - * In the home page a user can navigate to pages in the heading, - * and they can press the "donate to a cause" button or the "about us" button. - * - *

    The homepage includes a heading, a "donate to a cause" button, - * and an about us button. The page also has a charity image at the bottom.

    - */ -public class HomePageView extends BorderPane { - private final NavigationController nav; - - public HomePageView(NavigationController nav) { - this.nav = nav; - - getStylesheets().add(getClass().getResource("/homepage/homepage.css").toExternalForm()); - setCenter(createBody()); - } - - private ScrollPane createBody() { - ScrollPane body = new ScrollPane(); - body.setFitToWidth(true); - VBox vBox = new VBox(); - vBox.getChildren().addAll( - createIntroductionSection(), - createCharityImageSection() - ); - body.setContent(vBox); - return body; - } - - private VBox createIntroductionSection() { - VBox introductionSection = new VBox(); - introductionSection.setId("introduction-section"); - introductionSection.setAlignment(Pos.CENTER); - introductionSection.setSpacing(10); - - Text h1 = new Text("MAKE A DIFFERENCE TODAY"); - h1.setId("h1"); - Text h2 = new Text("SUPPORT THOSE IN NEED AROUND THE WORLD"); - h2.setId("h2"); - - Button donateToACauseBtn = new Button("Donate to a cause"); - donateToACauseBtn.setId("donate-to-cause-btn"); - donateToACauseBtn.setOnAction(e -> nav.showCausesPage()); - - Button aboutUsBtn = new Button("About us"); - aboutUsBtn.setId("about-us-btn"); - aboutUsBtn.setOnAction(e -> nav.showAboutUsPage()); - - introductionSection.getChildren().addAll(h1, h2, donateToACauseBtn, aboutUsBtn); - return introductionSection; - } - - private StackPane createCharityImageSection() { - StackPane charityImageSection = new StackPane(); - charityImageSection.setId("charity-image-section"); - charityImageSection.setPrefHeight(300); - StackPane image = new StackPane(); - image.setId("charity-image"); - charityImageSection.getChildren().add(image); - return charityImageSection; - } -} diff --git a/src/main/java/edu/group5/app/view/loginpage/LoginHeader.java b/src/main/java/edu/group5/app/view/loginpage/LoginHeader.java deleted file mode 100644 index b5f4296..0000000 --- a/src/main/java/edu/group5/app/view/loginpage/LoginHeader.java +++ /dev/null @@ -1,35 +0,0 @@ -package edu.group5.app.view.loginpage; - -import javafx.geometry.Pos; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.StackPane; - -/** - * A header for the login page and for the SignIn page. - *

    The header includes a logo of the Help Me Help app.

    - */ -public class LoginHeader extends BorderPane { - - public LoginHeader() { - getStylesheets().add(getClass().getResource("/header/header.css").toExternalForm()); - setId("header"); - - setCenter(getLogoSection()); - } - private StackPane getLogoSection() { - StackPane logoSection = new StackPane(); - logoSection.setId("logo-section"); - logoSection.setAlignment(Pos.CENTER); - - ImageView logo = new ImageView( - new Image(getClass().getResource("/header/images/hmh-logo.png").toExternalForm()) - ); - logo.setFitHeight(60); - logo.setPreserveRatio(true); - - logoSection.getChildren().add(logo); - return logoSection; - } -} diff --git a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java deleted file mode 100644 index 927a8af..0000000 --- a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java +++ /dev/null @@ -1,129 +0,0 @@ -package edu.group5.app.view.loginpage; - -import edu.group5.app.control.NavigationController; -import edu.group5.app.control.AuthController; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.PasswordField; -import javafx.scene.control.TextField; -import javafx.scene.layout.*; - -import java.util.Objects; - -/** - * A view for the login page. - * A user can login with email and password. - * If the user does not have an account they can - * press the register button to the SignUp page. - *

    This page involves a {@code LoginHeader}, an image at the right, - * a login box, an email box, a login button, and a register button.

    - */ -public class LoginPageView extends BorderPane { - private final NavigationController nav; - private final AuthController authController; - - private TextField emailField; - private PasswordField passwordField; - private Label errorLabel; - - public LoginPageView(NavigationController nav, AuthController authController) { - this.nav = nav; - this.authController = authController; - - HBox content = new HBox(); - content.setFillHeight(true); - HBox.setHgrow(content, Priority.ALWAYS); - - content.getChildren().addAll(getOuterSection(), getImageSection()); - - String css = Objects.requireNonNull( - getClass().getResource("/loginpage/login.css")).toExternalForm(); - content.getStylesheets().add(css); - - setCenter(content); - } - - public String getEmail() { - return emailField.getText(); - } - - public char[] getPassword() { - return passwordField.getText().toCharArray(); - } - - public void showError(String message) { - errorLabel.setText(message); - errorLabel.setStyle("-fx-text-fill: red;"); - } - - private VBox getOuterSection() { - VBox outerSection = new VBox(12); - outerSection.setAlignment(Pos.CENTER); - HBox.setHgrow(outerSection, Priority.ALWAYS); - outerSection.getChildren().addAll(getLoginBox(), getRegisterBtn()); - return outerSection; - } - - private VBox getLoginBox() { - VBox loginSection = new VBox(12); - loginSection.setAlignment(Pos.CENTER); - loginSection.setId("login-box"); - loginSection.getChildren().addAll(getErrorLabel(), getEmailBox(), getPasswordBox(), getLoginBtn()); - return loginSection; - } - - private Label getErrorLabel() { - errorLabel = new Label(); - errorLabel.setPrefHeight(20); - return errorLabel; - } - - private VBox getEmailBox() { - VBox emailBox = new VBox(); - emailBox.setMaxWidth(300); - emailField = new TextField(); - emailField.setPromptText("aurafarmer@gmail.com"); - emailField.setMaxWidth(300); - emailBox.getChildren().addAll(new Label("Email"), emailField); - return emailBox; - } - - private VBox getPasswordBox() { - VBox passwordBox = new VBox(); - passwordBox.setMaxWidth(300); - passwordField = new PasswordField(); - passwordField.setMaxWidth(300); - passwordBox.getChildren().addAll(new Label("Password"), passwordField); - return passwordBox; - } - - private Button getLoginBtn() { - Button loginBtn = new Button("Log In"); - loginBtn.setMaxWidth(300); - loginBtn.setId("login-btn"); - loginBtn.setOnMouseClicked(e -> authController.handleLogin( - this, - getEmail(), - getPassword() - )); - return loginBtn; - } - - public Button getRegisterBtn() { - Button registerBtn = new Button("Don't have an account? Sign Up"); - registerBtn.setMaxWidth(300); - registerBtn.setOnMouseClicked(e -> nav.showSignUpPage()); - registerBtn.setId("register-btn"); - return registerBtn; - } - - private StackPane getImageSection() { - StackPane imageSection = new StackPane(); - imageSection.setId("image-section"); - HBox.setHgrow(imageSection, Priority.ALWAYS); - return imageSection; - } - - -} diff --git a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java deleted file mode 100644 index 70f40ea..0000000 --- a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java +++ /dev/null @@ -1,163 +0,0 @@ -package edu.group5.app.view.loginpage; - -import edu.group5.app.control.NavigationController; -import edu.group5.app.control.AuthController; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.PasswordField; -import javafx.scene.control.TextField; -import javafx.scene.layout.*; - -import java.util.Objects; - -/** - * A view for the SignUp page. - * In this page a user can create an account by writing in first and last name, - * and by adding email and password. If the user already have an account, - * they can press the back to login button to login. - * - *

    This view contains a first name field, a surname field, a email field, - * a password field, a sign up button, and an back to login button.

    - */ -public class SignUpPageView extends BorderPane { - private final NavigationController nav; - private final AuthController authController; - - private TextField nameField; - private TextField surnameField; - private TextField emailField; - private PasswordField passwordField; - private Label errorLabel; - - public SignUpPageView(NavigationController nav, AuthController authController) { - this.nav = nav; - this.authController = authController; - - HBox content = new HBox(); - content.setFillHeight(true); - HBox.setHgrow(content, Priority.ALWAYS); - - content.getChildren().addAll(getOuterSection(), getImageSection()); - - String css = Objects.requireNonNull( - getClass().getResource("/loginpage/signup.css")).toExternalForm(); - content.getStylesheets().add(css); - - setCenter(content); - - } - - public String getFirstName() { - return nameField.getText(); - } - - public String getLastName() { - return surnameField.getText(); - } - - public String getEmail() { - return emailField.getText(); - } - - public char[] getPassword() { - return passwordField.getText().toCharArray(); - } - - public void showError(String message) { - errorLabel.setText(message); - errorLabel.setWrapText(true); - errorLabel.setStyle("-fx-text-fill: red;"); - } - - private VBox getOuterSection() { - VBox outerSection = new VBox(12); - outerSection.setAlignment(Pos.CENTER); - HBox.setHgrow(outerSection, Priority.ALWAYS); - outerSection.getChildren().addAll(getSignUpBox(), getBackToLoginBtn()); - return outerSection; - } - - private VBox getSignUpBox() { - VBox signUpSection = new VBox(12); - signUpSection.setAlignment(Pos.CENTER); - signUpSection.setId("login-box"); - signUpSection.getChildren().addAll(getErrorLabel(), getNameRow(), getEmailBox(), getPasswordBox(), getSignUpBtn()); - return signUpSection; - } - - private Label getErrorLabel() { - errorLabel = new Label(); - return errorLabel; - } - - private HBox getNameRow() { - HBox nameRow = new HBox(12); - nameRow.setMaxWidth(300); - - VBox nameBox = new VBox(); - nameField = new TextField(); - nameField.setPromptText("Jinwoo"); - HBox.setHgrow(nameBox, Priority.ALWAYS); - nameBox.getChildren().addAll(new Label("First name"), nameField); - - VBox surnameBox = new VBox(); - surnameField = new TextField(); - surnameField.setPromptText("Son"); - HBox.setHgrow(surnameBox, Priority.ALWAYS); - surnameBox.getChildren().addAll(new Label("Last Name"), surnameField); - - nameRow.getChildren().addAll(nameBox, surnameBox); - return nameRow; - - } - - private VBox getEmailBox() { - VBox emailBox = new VBox(); - emailBox.setMaxWidth(300); - emailField = new TextField(); - emailField.setPromptText("aurafarmer@gmail.com"); - emailField.setMaxWidth(300); - emailBox.getChildren().addAll(new Label("Email"), emailField); - return emailBox; - } - - private VBox getPasswordBox() { - VBox passwordBox = new VBox(); - passwordBox.setMaxWidth(300); - passwordField = new PasswordField(); - passwordField.setMaxWidth(300); - passwordBox.getChildren().addAll(new Label("Password"), passwordField); - return passwordBox; - } - - private Button getSignUpBtn() { - Button signUpBtn = new Button("Sign Up"); - signUpBtn.setMaxWidth(300); - signUpBtn.setId("login-btn"); - signUpBtn.setOnMouseClicked(e -> authController.handleSignUp( - this, - getFirstName(), - getLastName(), - getEmail(), - getPassword() - )); - return signUpBtn; - } - - public Button getBackToLoginBtn() { - Button backBtn = new Button("Already have an account? Log in"); - backBtn.setMaxWidth(300); - backBtn.setOnMouseClicked(e -> nav.showLoginPage()); - backBtn.setId("register-btn"); - return backBtn; - } - - private StackPane getImageSection() { - StackPane imageSection = new StackPane(); - imageSection.setId("image-section"); - HBox.setHgrow(imageSection, Priority.ALWAYS); - return imageSection; - } - -} diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java deleted file mode 100644 index 7ea3b6b..0000000 --- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java +++ /dev/null @@ -1,178 +0,0 @@ -package edu.group5.app.view.organizationpage; - -import edu.group5.app.control.DonationController; -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; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; - -import java.util.Objects; - -/** - * A view for displaying information about a selected organization. - * - *

    The page shows the organization's logo, name, and description. - * If no logo is available, a fallback "No image" is displayed.

    - * - *

    The page also includes a donate button that navigates to the - * donation page, and a back button to return to the causes page.

    - */ -public class OrganizationPageView extends BorderPane { - private final NavigationController nav; - private final OrganizationController organizationController; - private final DonationController donationController; - - 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()); - - VBox content = new VBox(); - content.getChildren().addAll(createBackButton(), createBody()); - setCenter(content); - } - - private ScrollPane createBody() { - ScrollPane body = new ScrollPane(); - body.setFitToWidth(true); - body.setFitToHeight(true); - - VBox vBox = new VBox(); - vBox.setId("main-container"); - - vBox.getChildren().addAll(createOrgSection()); - body.setContent(vBox); - return body; - } - private HBox createBackButton() { - Button backBtn = new Button("←"); - backBtn.getStyleClass().add("back-button"); - backBtn.setOnAction(e -> nav.showCausesPage()); - - HBox container = new HBox(backBtn); - container.setPadding(new Insets(10, 0, 0, 10)); - return container; - } - - private HBox createOrgSection() { - HBox orgSection = new HBox(); - orgSection.setId("org-section"); - orgSection.setAlignment(Pos.CENTER); - orgSection.setSpacing(40); - - orgSection.getChildren().addAll(createImageContainer(), createOrgInfoSection()); - return orgSection; - } - - private StackPane createImageContainer() { - StackPane imageContainer = new StackPane(); - imageContainer.setId("imageContainer"); - imageContainer.setPrefHeight(120); - imageContainer.setPrefWidth(120); - imageContainer.setMaxWidth(120); - - Organization org = organizationController.getCurrentOrganization(); - - if (org != null && org.logoUrl() != null && !org.logoUrl().isBlank()) { - // Load image in background thread to avoid blocking UI - new Thread(() -> { - try { - Image image = new Image(org.logoUrl(), 350, 350, 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 { - Text text = new Text("No image"); - text.setStyle("-fx-font-size: 10;"); - imageContainer.getChildren().add(text); - } - - return imageContainer; - } - - private VBox createOrgInfoSection() { - Organization org = organizationController.getCurrentOrganization(); - - VBox orgInfoSection = new VBox(); - orgInfoSection.setSpacing(50); - - VBox orgNameAndDescription = new VBox(); - orgNameAndDescription.setSpacing(5); - - Label orgName = new Label(org != null ? org.name() : "Unknown Organization"); - orgName.setId("orgName"); - - VBox descriptionBox = new VBox(); - descriptionBox.setSpacing(15); - descriptionBox.setId("description-container"); - descriptionBox.setMaxWidth(750); - - if (org != null && org.description() != null) { - String[] rawParagraphs = org.description().split("\n{2,}"); - - for (String para : rawParagraphs) { - String cleaned = para.trim(); - if (!cleaned.isBlank()) { - Label paragraph = new Label(cleaned); - paragraph.setId("description-paragraph"); - paragraph.setWrapText(true); - descriptionBox.getChildren().add(paragraph); - } - } - } - - ScrollPane descriptionScroll = new ScrollPane(descriptionBox); - descriptionScroll.setId("description-scroll"); - descriptionScroll.setFitToWidth(true); - descriptionScroll.setStyle("-fx-focus-color: transparent; -fx-faint-focus-color: transparent;"); - descriptionScroll.setPrefHeight(400); - descriptionScroll.setMaxHeight(400); - descriptionScroll.setPrefWidth(750); - descriptionScroll.setMinWidth(750); - - orgNameAndDescription.getChildren().addAll(orgName, descriptionScroll); - - Button donateBtn = new Button("Donate"); - donateBtn.setId("donate-button"); - donateBtn.setOnAction(e -> nav.showDonationPage()); - - orgInfoSection.getChildren().addAll(orgNameAndDescription, donateBtn); - return orgInfoSection; - } -} diff --git a/src/main/java/edu/group5/app/view/userpage/UserPageView.java b/src/main/java/edu/group5/app/view/userpage/UserPageView.java deleted file mode 100644 index aff5b32..0000000 --- a/src/main/java/edu/group5/app/view/userpage/UserPageView.java +++ /dev/null @@ -1,237 +0,0 @@ -package edu.group5.app.view.userpage; - -import edu.group5.app.control.DonationController; -import edu.group5.app.control.NavigationController; -import edu.group5.app.control.OrganizationController; -import edu.group5.app.control.AuthController; -import edu.group5.app.model.donation.Donation; -import edu.group5.app.model.organization.Organization; -import edu.group5.app.model.user.User; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import java.text.SimpleDateFormat; -import java.util.*; - -/** - * A view for the user profile page. - * - *

    Displays the current user's profile information, - * including avatar, full name, email and location, along with a logout button. - * The page is divided into three sections: - * Profile information, Supported causes, and previous donations.

    - * - *

    Supported causes shows all the organizations that the user has donated to. - * Previous donations shows a list of all the user's donations, - * and includes a search bar to filter through them.

    - */ -public class UserPageView extends BorderPane { - private final NavigationController nav; - private final AuthController authController; - private final DonationController donationController; - private final OrganizationController organizationController; - - public UserPageView(NavigationController nav, AuthController authController, - DonationController donationController, OrganizationController organizationController) { - this.nav = nav; - this.authController = authController; - this.donationController = donationController; - this.organizationController = organizationController; - - getStylesheets().add(getClass().getResource("/userpage/userpage.css").toExternalForm()); - - VBox content = new VBox(30); - content.setPadding(new Insets(40)); - content.getChildren().addAll(createProfileSection(), createCausesSection(), createDonationsSection()); - setCenter(content); - } - - private HBox createProfileSection() { - 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 = authController.getCurrentUser(); - - Text name = new Text(currentUser.getFirstName() + " " + currentUser.getLastName()); - name.setId("profile-name"); - - Label email = new Label(currentUser.getEmail()); - email.getStyleClass().add("profile-info"); - - Label location = new Label("Trondheim, Norway"); - location.getStyleClass().add("profile-info"); - - Button logoutBtn = new Button("Logout"); - logoutBtn.getStyleClass().add("logout-button"); - logoutBtn.setOnAction(e -> authController.handleLogout()); - - VBox info = new VBox(10, name, email, location, logoutBtn); - info.setAlignment(Pos.CENTER_LEFT); - - HBox profile = new HBox(40, avatar, info); - profile.setAlignment(Pos.CENTER_LEFT); - return profile; - } - private VBox createCausesSection() { - Text title = new Text("YOUR SUPPORTED CAUSES"); - title.getStyleClass().add("section-title"); - - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setFitToWidth(true); - scrollPane.setStyle("-fx-focus-color: transparent; -fx-faint-focus-color: transparent;"); - - FlowPane causesFlow = new FlowPane(10, 10); - causesFlow.setStyle("-fx-padding: 10;"); - causesFlow.getStyleClass().add("section-box"); - - Set uniqueOrganizations = donationController.getUniqueOrganizationIDs(); - - if (uniqueOrganizations.isEmpty()) { - Label noCauses = new Label("No causes supported yet"); - noCauses.setStyle("-fx-text-fill: #999;"); - causesFlow.getChildren().add(noCauses); - } else { - for (int orgId : uniqueOrganizations) { - Organization org = organizationController.getOrganizationById(orgId); - if (org != null) { - causesFlow.getChildren().add(createCauseChip(org)); - } - } - } - scrollPane.setPrefHeight(275); - scrollPane.setContent(causesFlow); - - return new VBox(10, title, scrollPane); - } - - private VBox createDonationsSection() { - Text title = new Text("PREVIOUS DONATIONS"); - title.getStyleClass().add("section-title"); - - HBox searchBox = new HBox(10); - searchBox.setStyle("-fx-padding: 10;"); - TextField searchField = new TextField(); - searchField.setPromptText("Search by organization name..."); - searchField.setPrefWidth(300); - searchBox.getChildren().add(searchField); - - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setFitToWidth(false); - scrollPane.setPrefWidth(650); - scrollPane.setMaxWidth(650); - scrollPane.setPrefHeight(400); - scrollPane.setStyle("-fx-focus-color: transparent; -fx-faint-focus-color: transparent;"); - - VBox donationsBox = new VBox(12); - donationsBox.getStyleClass().add("donation-list"); - donationsBox.setPadding(new Insets(10)); - - User currentUser = authController.getCurrentUser(); - Map userDonations = - donationController.getUserDonations(currentUser.getUserId()); - - // Filter donations based on search - searchField.textProperty().addListener((obs, oldVal, newVal) -> { - donationsBox.getChildren().clear(); - - if (userDonations.isEmpty()) { - Label noDonations = new Label("No donations yet"); - noDonations.setStyle("-fx-text-fill: #999;"); - donationsBox.getChildren().add(noDonations); - return; - } - - String searchTerm = newVal.toLowerCase().trim(); - boolean found = false; - - for (Donation donation : userDonations.values()) { - Organization org = organizationController.getOrganizationById(donation.organizationId()); - String orgName = (org != null) ? org.name() : "Unknown Organization"; - - // Filter by search term - if (searchTerm.isEmpty() || orgName.toLowerCase().contains(searchTerm)) { - donationsBox.getChildren().add(createDonationCard(donation)); - found = true; - } - } - - if (!found && !searchTerm.isEmpty()) { - Label noResults = new Label("No donations found for \"" + newVal + "\""); - noResults.setStyle("-fx-text-fill: #999;"); - donationsBox.getChildren().add(noResults); - } - }); - - if (userDonations.isEmpty()) { - Label noDonations = new Label("No donations yet"); - noDonations.setStyle("-fx-text-fill: #999;"); - donationsBox.getChildren().add(noDonations); - } else { - for (Donation donation : userDonations.values()) { - Organization org = organizationController.getOrganizationById(donation.organizationId()); - String orgName = (org != null) ? org.name() : "Unknown Organization"; - donationsBox.getChildren().add(createDonationCard(donation)); - } - } - - scrollPane.setContent(donationsBox); - return new VBox(10, title, searchBox, scrollPane); - } - - private BorderPane createDonationCard(Donation donation) { - Organization org = organizationController.getOrganizationById(donation.organizationId()); - String orgName = (org != null) ? org.name() : "Unknown Organization"; - - // Use BorderPane to fix columns: LEFT | SPACE | RIGHT - BorderPane card = new BorderPane(); - card.getStyleClass().add("donation-card"); - card.setPadding(new Insets(12, 15, 12, 15)); - - // LEFT: Organization name - Text orgText = new Text(orgName); - orgText.getStyleClass().add("donation-org-name"); - card.setLeft(orgText); - - // RIGHT: Amount and date (stacked vertically) - VBox details = new VBox(4); - details.setAlignment(Pos.CENTER_RIGHT); - - Label amountLabel = new Label(String.format("%.2f", donation.amount()) + " kr"); - amountLabel.getStyleClass().add("donation-amount"); - - Label dateLabel = new Label( - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(donation.date())); - dateLabel.getStyleClass().add("donation-date"); - - details.getChildren().addAll(amountLabel, dateLabel); - card.setRight(details); - - return card; - } - - private FlowPane createCauseChip(Organization org) { - FlowPane chip = new FlowPane(); - chip.getStyleClass().add("cause-chip"); - chip.setPadding(new Insets(8, 12, 8, 12)); - - Label label = new Label(org.name()); - chip.getChildren().add(label); - - return chip; - } -} diff --git a/src/main/resources/browsepage/browse_org.css b/src/main/resources/browsepage/browse_org.css deleted file mode 100644 index 9a50c71..0000000 --- a/src/main/resources/browsepage/browse_org.css +++ /dev/null @@ -1,25 +0,0 @@ -#mainContainer { - -fx-border-color: black; - -fx-border-width: 1px; - -fx-border-radius: 1em; - -fx-padding: 5px; - -fx-background-color: white; - -fx-background-radius: 1em; -} - -#mainContainer:hover { - -fx-cursor: hand; -} - -#imageContainer {} - - -#logo {} - -#orgName { - -fx-font-size: x-large; - -fx-font-weight: bold; - -fx-text-alignment: center; -} - -#checkMarkContainer {} \ No newline at end of file diff --git a/src/main/resources/browsepage/browsepage.css b/src/main/resources/browsepage/browsepage.css deleted file mode 100644 index 1dd8643..0000000 --- a/src/main/resources/browsepage/browsepage.css +++ /dev/null @@ -1,6 +0,0 @@ -#body { - -fx-padding: 20px; -} - -#card-grid { -} \ No newline at end of file diff --git a/src/main/resources/browsepage/images/children_of_shambala.png b/src/main/resources/browsepage/images/children_of_shambala.png deleted file mode 100644 index 3b1a7f7..0000000 Binary files a/src/main/resources/browsepage/images/children_of_shambala.png and /dev/null differ diff --git a/src/main/resources/browsepage/images/kfum_kfum_global.png b/src/main/resources/browsepage/images/kfum_kfum_global.png deleted file mode 100644 index bd5365a..0000000 Binary files a/src/main/resources/browsepage/images/kfum_kfum_global.png and /dev/null differ diff --git a/src/main/resources/donationpage/Payment Complete.png b/src/main/resources/donationpage/Payment Complete.png deleted file mode 100644 index e2b3d6a..0000000 Binary files a/src/main/resources/donationpage/Payment Complete.png and /dev/null differ diff --git a/src/main/resources/donationpage/donation.css b/src/main/resources/donationpage/donation.css deleted file mode 100644 index bc777b6..0000000 --- a/src/main/resources/donationpage/donation.css +++ /dev/null @@ -1,149 +0,0 @@ -.donation-button { - -fx-background-color: white; - -fx-border-color: #ccc; - -fx-border-width: 2px; - -fx-border-radius: 8; - -fx-background-radius: 8; - -fx-cursor: hand; - -fx-font-size: 18px; - -fx-font-weight: bold; -} - -.donation-button:hover { - -fx-border-color: #f0f0f0; -} - -.donation-button-selected { - -fx-background-color: #111; - -fx-text-fill: white; - -fx-border-color: #111; -} - -.donation-button-selected:hover { - -fx-background-color: #222; - -fx-border-color: #222; -} - -.donation-button-selected Text, -.donation-button-selected .donation-input { - -fx-fill: white; - -fx-border-color: transparent transparent white transparent; - -fx-text-fill: white; - -fx-prompt-text-fill: #ccc; -} - -.donation-title { - -fx-font-size: 18px; - -fx-font-weight: bold; - -fx-fill: #111; -} - - -.donation-amount { - -fx-font-size: 18px; - -fx-fill: #111; -} - -.donation-input { - -fx-font-size: 16px; - -fx-pref-width: 140px; - -fx-background-color: transparent; - -fx-border-color: transparent transparent #333 transparent; - -fx-alignment: center; -} - -.donation-input:focused { - -fx-border-color: transparent transparent #4a90d9 transparent; -} - -.donate-button { - -fx-pref-height: 55px; - -fx-background-color: #e03030; - -fx-text-fill: white; - -fx-font-size: 22px; - -fx-font-weight: bold; - -fx-background-radius: 8; - -fx-cursor: hand; - -fx-padding: 0 40 0 40; -} - -.donate-button:hover { - -fx-background-color: #c02020; -} - -.clear-button { - -fx-pref-height: 55px; - -fx-background-color: #f0f0f0; - -fx-text-fill: #333; - -fx-font-size: 16px; - -fx-font-weight: bold; - -fx-background-radius: 8; - -fx-cursor: hand; - -fx-padding: 0 30 0 30; - -fx-border-color: #ccc; - -fx-border-width: 1; -} - -.clear-button:hover { - -fx-background-color: #e0e0e0; -} - -.clear-button:pressed { - -fx-background-color: #d0d0d0; -} - -.donation-button-selected .donation-title { - -fx-fill: white; -} - -.donation-button-selected .donation-amount { - -fx-fill: white; -} - -.donation-button-selected .donation-input { - -fx-text-fill: white; -} - - -.donation-button-selected .donation-input:focused { - -fx-text-fill: white; - -fx-border-color: transparent transparent white transparent; -} -.payment-method-button { - -fx-background-color: #111; - -fx-text-fill: white; - -fx-font-size: 16px; - -fx-font-weight: bold; - -fx-pref-width: 180px; - -fx-pref-height: 45px; - -fx-background-radius: 6; - -fx-border-radius: 6; - -fx-cursor: hand; -} -.payment-method-button:hover { - -fx-background-color: #222; -} -.payment-method-selected, -.payment-method-selected:hover { - -fx-background-color: #e03030; -} -.donation-button-selected .donation-title, -.donation-button-selected .donation-amount { - -fx-fill: white; -} -.back-button { - -fx-background-color: white; - -fx-text-fill: black; - -fx-font-weight: bold; - -fx-font-size: 20px; - -fx-background-radius: 50; - -fx-padding: 4px 10px; - -fx-cursor: hand; - -fx-border-radius: 50; - -fx-border-color: black; - -fx-border-width: 2px; -} -.back-button:hover { - -fx-background-color: #333; - -fx-border-color:#333; -} \ No newline at end of file diff --git a/src/main/resources/donationpage/paymentcomplete.css b/src/main/resources/donationpage/paymentcomplete.css deleted file mode 100644 index 0e1e786..0000000 --- a/src/main/resources/donationpage/paymentcomplete.css +++ /dev/null @@ -1,14 +0,0 @@ -#home-button { - -fx-background-color: #4caf50; - -fx-text-fill: white; - -fx-font-size: 18px; - -fx-font-weight: bold; - -fx-pref-width: 200px; - -fx-pref-height: 50px; - -fx-background-radius: 8; - -fx-cursor: hand; -} - -#home-button:hover { - -fx-background-color: #388e3c; -} \ No newline at end of file diff --git a/src/main/resources/header/header.css b/src/main/resources/header/header.css deleted file mode 100644 index 23115eb..0000000 --- a/src/main/resources/header/header.css +++ /dev/null @@ -1,13 +0,0 @@ -#header { - -fx-background-color: #A6CDF2; - -fx-padding: 10px; -} - -#logo-section { -} - -#navbar { -} - -#profile-section { -} \ No newline at end of file diff --git a/src/main/resources/header/images/avatar.png b/src/main/resources/header/images/avatar.png deleted file mode 100644 index e46b74d..0000000 Binary files a/src/main/resources/header/images/avatar.png and /dev/null differ diff --git a/src/main/resources/header/images/hmh-logo.png b/src/main/resources/header/images/hmh-logo.png deleted file mode 100644 index 78be8d9..0000000 Binary files a/src/main/resources/header/images/hmh-logo.png and /dev/null differ diff --git a/src/main/resources/homepage/homepage.css b/src/main/resources/homepage/homepage.css deleted file mode 100644 index 2579557..0000000 --- a/src/main/resources/homepage/homepage.css +++ /dev/null @@ -1,32 +0,0 @@ -#introduction-section { - -fx-padding: 20px 0; -} - -#h1 { - -fx-text-fill: black; - -fx-font-size: 50pt; - -fx-font-weight: bold; -} - -#h2 { - -fx-fill: #757575; - -fx-font-size: 25pt; -} - -#charity-image-section { -} - -#charity-image { - -fx-background-image: url("/homepage/images/charityimage.jpg"); - -fx-background-position: center 55%; - -fx-background-size: 100% auto; - -fx-background-repeat: no-repeat; -} - -#donate-to-cause-btn { - -fx-cursor: hand; -} - -#about-us-btn { - -fx-cursor: hand; -} \ No newline at end of file diff --git a/src/main/resources/homepage/images/charityimage.jpg b/src/main/resources/homepage/images/charityimage.jpg deleted file mode 100644 index 9072612..0000000 Binary files a/src/main/resources/homepage/images/charityimage.jpg and /dev/null differ diff --git a/src/main/resources/init.sql b/src/main/resources/init.sql deleted file mode 100644 index 8b99314..0000000 --- a/src/main/resources/init.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE IF NOT EXISTS -users ( - user_id INT PRIMARY KEY, - role VARCHAR(32) NOT NULL, - first_name VARCHAR(32) NOT NULL, - last_name VARCHAR(32) NOT NULL, - email VARCHAR(32) NOT NULL, - password_hash VARCHAR(72) NOT NULL -); - -CREATE TABLE IF NOT EXISTS -donations ( - donation_id INT PRIMARY KEY, - user_id INT NOT NULL, - organization_id INT NOT NULL, - amount DECIMAL(32, 16) NOT NULL, - dating TIMESTAMP NOT NULL, - payment_method VARCHAR(32) NOT NULL, - CONSTRAINT fk_user - FOREIGN KEY (user_id) - REFERENCES users(user_id) -); - diff --git a/src/main/resources/loginpage/login-image.jpg b/src/main/resources/loginpage/login-image.jpg deleted file mode 100644 index 5b4b3ed..0000000 Binary files a/src/main/resources/loginpage/login-image.jpg and /dev/null differ diff --git a/src/main/resources/loginpage/login.css b/src/main/resources/loginpage/login.css deleted file mode 100644 index f0ffc41..0000000 --- a/src/main/resources/loginpage/login.css +++ /dev/null @@ -1,35 +0,0 @@ -#image-section { - -fx-background-image: url("/loginpage/login-image.jpg"); - -fx-background-size: 200%; - -fx-background-position: left center; - -fx-background-repeat: no-repeat; - -fx-pref-width: 50%; -} - -#login-btn { - -fx-background-color: #000000; - -fx-text-fill: white; - -fx-pref-height: 35px; -} - -#register-btn { - -fx-background-color: #000000; - -fx-text-fill: white; - -fx-pref-height: 35px; -} - -#login-box { - -fx-border-color: #ccc; - -fx-border-radius: 8px; - -fx-border-width: 1px; - -fx-padding: 24px; - -fx-max-width: 340px; -} - -#login-btn:hover { - -fx-cursor: hand; -} - -#register-btn:hover { - -fx-cursor: hand; -} \ No newline at end of file diff --git a/src/main/resources/loginpage/signup-image.png b/src/main/resources/loginpage/signup-image.png deleted file mode 100644 index 7cb78cd..0000000 Binary files a/src/main/resources/loginpage/signup-image.png and /dev/null differ diff --git a/src/main/resources/loginpage/signup.css b/src/main/resources/loginpage/signup.css deleted file mode 100644 index fcd8751..0000000 --- a/src/main/resources/loginpage/signup.css +++ /dev/null @@ -1,35 +0,0 @@ -#image-section { - -fx-background-image: url("/loginpage/signup-image.png"); - -fx-background-size: auto; - -fx-background-position: right center; - -fx-background-repeat: no-repeat; - -fx-pref-width: 50%; -} - -#login-btn { - -fx-background-color: #000000; - -fx-text-fill: white; - -fx-pref-height: 35px; -} - -#register-btn { - -fx-background-color: #000000; - -fx-text-fill: white; - -fx-pref-height: 35px; -} - -#login-box { - -fx-border-color: #ccc; - -fx-border-radius: 8px; - -fx-border-width: 1px; - -fx-padding: 24px; - -fx-max-width: 340px; -} - -#login-btn:hover { - -fx-cursor: hand; -} - -#register-btn:hover { - -fx-cursor: hand; -} \ No newline at end of file diff --git a/src/main/resources/organizationpage/organizationpage.css b/src/main/resources/organizationpage/organizationpage.css deleted file mode 100644 index 20a9ad8..0000000 --- a/src/main/resources/organizationpage/organizationpage.css +++ /dev/null @@ -1,81 +0,0 @@ -#main-container { - -fx-padding: 50px -} - -#logo { - -fx-min-height: 80%; -} - -#orgName { - -fx-font-weight: bold; - -fx-font-size: 28pt; - -fx-padding: 0 0 30 0; -} - -#description-container { - -fx-padding: 30; - -fx-spacing: 22; - -fx-max-width: 750; - -fx-background-color: #f8f9fa; - -fx-border-radius: 8; -} - -#description-paragraph { - -fx-font-size: 18; - -fx-text-fill: #333; - -fx-font-family: "Segoe UI", Arial, sans-serif; - -fx-line-spacing: 13; - -fx-text-alignment: Left; - -fx-wrap-text: true; - -fx-padding: 10 0 10 0; -} - -#donate-button { - -fx-pref-height: 55px; - -fx-background-color: #e03030; - -fx-text-fill: white; - -fx-font-size: 22px; - -fx-font-weight: bold; - -fx-background-radius: 8; - -fx-cursor: hand; - -fx-padding: 0 40 0 40; - -fx-margin-top: 30; -} - -#donate-button:hover { - -fx-background-color: #c02020; -} - -#description-scroll { - -fx-control-inner-background: #f8f9fa; - -fx-padding: 0; -} - -#description-scroll .scroll-bar:vertical { - -fx-pref-width: 8; -} - -#description-scroll .scroll-bar:vertical .thumb { - -fx-background-radius: 4; - -fx-background-color: #cccccc; -} - -#description-scroll .scroll-bar:vertical .thumb:hover { - -fx-background-color: #999999; -} -.back-button { - -fx-background-color: white; - -fx-text-fill: black; - -fx-font-weight: bold; - -fx-font-size: 20px; - -fx-background-radius: 50; - -fx-padding: 4px 10px; - -fx-cursor: hand; - -fx-border-radius: 50; - -fx-border-color: black; - -fx-border-width: 2px; -} -.back-button:hover { - -fx-background-color: #333; - -fx-border-color:#333; -} \ No newline at end of file diff --git a/src/main/resources/test_init.sql b/src/main/resources/test_init.sql deleted file mode 100644 index 00670e8..0000000 --- a/src/main/resources/test_init.sql +++ /dev/null @@ -1,24 +0,0 @@ -DROP TABLE IF EXISTS users, donations; - -CREATE TABLE IF NOT EXISTS -users ( - user_id INT PRIMARY KEY, - role VARCHAR(32) NOT NULL, - first_name VARCHAR(32) NOT NULL, - last_name VARCHAR(32) NOT NULL, - email VARCHAR(32) NOT NULL, - password_hash VARCHAR(72) NOT NULL -); - -CREATE TABLE IF NOT EXISTS -donations ( - donation_id INT PRIMARY KEY, - user_id INT NOT NULL, - organization_id INT NOT NULL, - amount DECIMAL(32, 16) NOT NULL, - dating TIMESTAMP NOT NULL, - payment_method VARCHAR(32) NOT NULL, - CONSTRAINT fk_user - FOREIGN KEY (user_id) - REFERENCES users(user_id) -); diff --git a/src/main/resources/userpage/account_circle.png b/src/main/resources/userpage/account_circle.png deleted file mode 100644 index f53f825..0000000 Binary files a/src/main/resources/userpage/account_circle.png and /dev/null differ diff --git a/src/main/resources/userpage/userpage.css b/src/main/resources/userpage/userpage.css deleted file mode 100644 index 8d68841..0000000 --- a/src/main/resources/userpage/userpage.css +++ /dev/null @@ -1,86 +0,0 @@ -#profile-name { - -fx-font-size: 28px; - -fx-font-weight: bold; -} - -.profile-info { - -fx-font-size: 16px; - -fx-text-fill: #444; -} - -.section-title { - -fx-font-size: 14px; - -fx-font-weight: bold; - -fx-fill: #888; -} - -.section-box { - -fx-background-color: #ddd; - -fx-pref-height: 150px; - -fx-background-radius: 6; -} - -.logout-button { - -fx-background-color: #e03030; - -fx-text-fill: white; - -fx-font-size: 14px; - -fx-font-weight: bold; - -fx-padding: 8 20; - -fx-background-radius: 4; - -fx-cursor: hand; -} - -.logout-button:hover { - -fx-background-color: #c02020; -} - -.cause-chip { - -fx-background-color: #4CAF50; - -fx-text-fill: white; - -fx-padding: 6 12; - -fx-background-radius: 20; - -fx-font-size: 13px; -} - -.cause-chip:hover { - -fx-background-color: #45a049; - -fx-cursor: hand; -} - -.donation-card { - -fx-background-color: #f5f5f5; - -fx-border-color: #ddd; - -fx-border-radius: 6; - -fx-background-radius: 6; -} - -.donation-card:hover { - -fx-background-color: #efefef; - -fx-border-color: #bbb; -} - -.donation-org-name { - -fx-font-size: 15px; - -fx-font-weight: bold; - -fx-fill: #333; -} - -.donation-amount { - -fx-font-size: 14px; - -fx-font-weight: bold; - -fx-text-fill: #2196F3; -} - -.donation-date { - -fx-font-size: 12px; - -fx-text-fill: #999; -} - -.donation-list { - -fx-pref-height: 400px; -} - -/* ScrollPane styling */ -.scroll-pane { - -fx-control-inner-background: #fafafa; -} \ No newline at end of file diff --git a/src/main/resources/verified_check.png b/src/main/resources/verified_check.png deleted file mode 100644 index 520f237..0000000 Binary files a/src/main/resources/verified_check.png and /dev/null differ diff --git a/src/test/java/edu/group5/app/AppTest.java b/src/test/java/edu/group5/app/AppTest.java deleted file mode 100644 index d186aa9..0000000 --- a/src/test/java/edu/group5/app/AppTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package edu.group5.app; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -/** - * Unit test for simple App. - */ -public class AppTest { - - /** - * Rigorous Test :-) - */ - @Test - public void shouldAnswerWithTrue() { - assertTrue(true); - } -} diff --git a/src/test/java/edu/group5/app/model/AppStateTest.java b/src/test/java/edu/group5/app/model/AppStateTest.java deleted file mode 100644 index 1254033..0000000 --- a/src/test/java/edu/group5/app/model/AppStateTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package edu.group5.app.model; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.math.BigDecimal; - -import org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import edu.group5.app.model.organization.Organization; -import edu.group5.app.model.user.Customer; -import edu.group5.app.model.user.User; - -public class AppStateTest { - private User nullUser; - private BigDecimal nullDonationAmount; - private Organization nullOrganization; - private String nullPaymentMethod; - - private User currentTestUser; - private BigDecimal currentTestDonationAmount; - private Organization currentTestOrganization; - private String currentTestPaymentMethod; - - @BeforeEach - void setUp() { - nullUser = null; - nullDonationAmount = null; - nullOrganization = null; - nullPaymentMethod = null; - - currentTestUser = new Customer(1, "Bob", - "Builder", "testuser@example.com", - "password123"); - - currentTestDonationAmount = BigDecimal.ZERO; - - currentTestOrganization = new Organization(1738, "TestOrg", - true, "https://testorg.example.com", true, - "A test organization", "https://testorg.example.com/logo.png"); - - currentTestPaymentMethod = "Credit Card"; - } - - @Test - void gettersAndSetters_WorkCorrectly() { - AppState appState = new AppState(); - - // Test current user - assertEquals(nullUser, appState.getCurrentUser()); - appState.setCurrentUser(currentTestUser); - assertEquals(currentTestUser, appState.getCurrentUser()); - - // Test current donation amount - assertEquals(nullDonationAmount, appState.getCurrentDonationAmount()); - appState.setCurrentDonationAmount(currentTestDonationAmount); - assertEquals(currentTestDonationAmount, appState.getCurrentDonationAmount()); - - // Test current organization - assertEquals(nullOrganization, appState.getCurrentOrganization()); - appState.setCurrentOrganization(currentTestOrganization); - assertEquals(currentTestOrganization, appState.getCurrentOrganization()); - - // Test current payment method - assertEquals(nullPaymentMethod, appState.getCurrentPaymentMethod()); - appState.setCurrentPaymentMethod(currentTestPaymentMethod); - assertEquals(currentTestPaymentMethod, appState.getCurrentPaymentMethod()); - } -} diff --git a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java deleted file mode 100644 index 82140a7..0000000 --- a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java +++ /dev/null @@ -1,275 +0,0 @@ -package edu.group5.app.model.donation; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.sql.Timestamp; -import java.util.*; - -import static org.junit.jupiter.api.Assertions.*; - -public class DonationRepositoryTest { - - private DonationRepository repo; - private Donation donation1, donation2, donation3, donation4; - private Timestamp ts1, ts2, now; - - @BeforeEach - void setUp() { - ts1 = Timestamp.valueOf("2025-01-01 10:00:00"); - ts2 = Timestamp.valueOf("2025-01-01 10:00:01"); - now = new Timestamp(System.currentTimeMillis()); - - donation1 = new Donation(1, 102, 202, new BigDecimal("500.0"), ts1, "Card"); - donation2 = new Donation(2, 102, 202, new BigDecimal("500.0"), ts2, "PayPal"); - donation3 = new Donation(3, 103, 203, new BigDecimal("200.0"), now, "PayPal"); - donation4 = new Donation(1, 101, 201, new BigDecimal("500.0"), now, "Card"); - - repo = new DonationRepository(new ArrayList<>()); - } - - @Test - void constructorThrowsIfNullList() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new DonationRepository(null)); - assertEquals("List of donation rows can't be null", ex.getMessage()); - } - - @Test - void constructorThrowsIfRowInvalidLength() { - List invalidRows = new ArrayList<>(); - invalidRows.add(new Object[]{1, 2}); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new DonationRepository(invalidRows)); - assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); - } - - @Test - void constructorParsesRowSuccessfully() { - List rows = new ArrayList<>(); - Timestamp ts = Timestamp.valueOf("2025-01-01 10:00:00"); - rows.add(new Object[]{1, 101, 201, new BigDecimal("100"), ts, "Card"}); - DonationRepository repoWithRow = new DonationRepository(rows); - Donation d = repoWithRow.getDonationById(1); - assertNotNull(d); - assertEquals(101, d.userId()); - assertEquals(201, d.organizationId()); - assertEquals(new BigDecimal("100"), d.amount()); - assertEquals(ts, d.date()); - assertEquals("Card", d.paymentMethod()); - } - - - @Test - void constructorThrowsIfRowIsNull() { - List rows = new ArrayList<>(); - rows.add(null); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new DonationRepository(rows)); - assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); - } - - @Test - void addContentSuccessfully() { - assertTrue(repo.addContent(donation1)); - assertEquals(1, repo.getAllDonations().size()); - assertTrue(repo.getAllDonations().containsValue(donation1)); - } - - @Test - void addContentDuplicateIdFails() { - repo.addContent(donation1); - assertFalse(repo.addContent(donation4)); - assertEquals(1, repo.getAllDonations().size()); - } - - @Test - void addContentNullThrows() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.addContent(null)); - assertEquals("Donation can't be null", ex.getMessage()); - } - - @Test - void getDonationByIdSuccessfully() { - repo.addContent(donation1); - Donation retrieved = repo.getDonationById(1); - assertEquals(donation1, retrieved); - } - - @Test - void getDonationByIdThrowsIfNegative() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.getDonationById(0)); - assertEquals("Donation ID must be a positive integer", ex.getMessage()); - } - - @Test - void getDonationByIdReturnsNullIfNotFound() { - assertNull(repo.getDonationById(999)); - } - - @Test - void getAllDonationsReturnsCopy() { - repo.addContent(donation1); - Map all = repo.getAllDonations(); - assertEquals(1, all.size()); - all.clear(); - assertEquals(1, repo.getAllDonations().size()); - } - - @Test - void sortByDateAscending() { - repo.addContent(donation3); - repo.addContent(donation1); - repo.addContent(donation2); - - Map sorted = repo.sortByDate(); - Integer[] keys = sorted.keySet().toArray(new Integer[0]); - assertArrayEquals(new Integer[]{1, 2, 3}, keys); - } - - @Test - void sortByDateWithSameDateKeepsAll() { - repo.addContent(donation1); - repo.addContent(donation4); - Map sorted = repo.sortByDate(); - assertEquals(1, sorted.size()); - assertTrue(sorted.containsKey(1)); - } - - @Test - void sortByAmountAscending() { - repo.addContent(donation3); - repo.addContent(donation1); - - Map sorted = repo.sortByAmount(); - Integer[] keys = sorted.keySet().toArray(new Integer[0]); - assertArrayEquals(new Integer[]{3, 1}, keys); - } - - @Test - void sortByAmountWithSameAmount() { - repo.addContent(donation1); - repo.addContent(donation2); - Map sorted = repo.sortByAmount(); - assertEquals(2, sorted.size()); - assertTrue(sorted.containsKey(1)); - assertTrue(sorted.containsKey(2)); - } - - @Test - void filterByOrganizationMatches() { - repo.addContent(donation1); - repo.addContent(donation2); - repo.addContent(donation3); - - Map filtered = repo.filterByOrganization(202); - assertEquals(2, filtered.size()); - assertTrue(filtered.containsKey(1)); - assertTrue(filtered.containsKey(2)); - } - - @Test - void filterByOrganizationNoMatch() { - repo.addContent(donation1); - Map filtered = repo.filterByOrganization(999); - assertTrue(filtered.isEmpty()); - } - - @Test - void filterByOrganizationThrowsIfNegative() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.filterByOrganization(0)); - assertEquals("Organization number must be a positive integer", ex.getMessage()); - } - - @Test - void exportReturnsAllRowsSortedById() { - repo.addContent(donation2); - repo.addContent(donation1); - - List exported = repo.export(); - assertEquals(2, exported.size()); - assertArrayEquals(new Object[]{1, 102, 202, new BigDecimal("500.0"), ts1, "Card"}, exported.get(0)); - assertArrayEquals(new Object[]{2, 102, 202, new BigDecimal("500.0"), ts2, "PayPal"}, exported.get(1)); - } - - @Test - void getNextDonationIdReturnsCorrectNextId() { - assertEquals(1, repo.getNextDonationId()); - repo.addContent(donation1); - repo.addContent(donation4); - assertEquals(2, repo.getNextDonationId()); - } - - @Test - void exportEmptyRepositoryReturnsEmptyList() { - List exported = repo.export(); - assertTrue(exported.isEmpty()); - } - - @Test - void sortByDateHandlesDuplicateKeysWithLambda() { - Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); - Donation d2 = new Donation(1, 102, 202, new BigDecimal("200"), ts2, "PayPal"); - repo.addContent(d1); - repo.getAllDonations().put(d2.donationId(), d2); - Map sorted = repo.sortByDate(); - assertEquals(d1, sorted.get(1)); - } - - @Test - void sortByAmountHandlesDuplicateKeysWithLambda() { - Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); - Donation d2 = new Donation(1, 102, 202, new BigDecimal("100"), ts2, "PayPal"); - repo.addContent(d1); - repo.getAllDonations().put(d2.donationId(), d2); - Map sorted = repo.sortByAmount(); - assertEquals(d1, sorted.get(1)); - } - - @Test - void filterByOrganizationHandlesDuplicateKeysWithLambda() { - Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); - Donation d2 = new Donation(1, 102, 201, new BigDecimal("200"), ts2, "PayPal"); - repo.addContent(d1); - repo.getAllDonations().put(d2.donationId(), d2); - Map filtered = repo.filterByOrganization(201); - assertEquals(d1, filtered.get(1)); - } - - @Test - void filterByUserIdMatches() { - repo.addContent(donation1); - repo.addContent(donation3); - - Map filtered = repo.filterByUser(102); - assertEquals(1, filtered.size()); - assertTrue(filtered.containsKey(1)); - } - - @Test - void filterByUserIdNoMatch() { - repo.addContent(donation1); - Map filtered = repo.filterByUser(999); - assertTrue(filtered.isEmpty()); - } - - @Test - void filterByUserIdThrowsIfNegative() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.filterByUser(0)); - assertEquals("User ID must be a positive integer", ex.getMessage()); - } - - @Test - void exportExportsOnlyNewInformation() { - repo.addContent(donation1); - repo.addContent(donation2); - assertEquals(2, repo.export().size()); - repo.addContent(donation3); - assertEquals(1, repo.export().size()); - } -} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java deleted file mode 100644 index 3b1e5ff..0000000 --- a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java +++ /dev/null @@ -1,145 +0,0 @@ -package edu.group5.app.model.donation; - -import edu.group5.app.model.organization.OrganizationRepository; -import edu.group5.app.model.organization.OrganizationScraper; -import edu.group5.app.model.user.Customer; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.sql.Timestamp; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -class DonationServiceTest { - - private DonationRepository donationRepository; - private OrganizationRepository organizationRepository; - private DonationService donationService; - private Customer customer; - private OrganizationScraper scraper; - - @BeforeEach - void setUp() { - scraper = new OrganizationScraper(); - HashMap orgMap = new HashMap<>(); - orgMap.put("org_number", "101"); - orgMap.put("name", "CharityOrg"); - orgMap.put("status", "approved"); - orgMap.put("url", "https://charity.org"); - orgMap.put("is_pre_approved", true); - - Object[] orgInput = new Object[]{ orgMap }; - organizationRepository = new OrganizationRepository(orgInput, scraper); - - donationRepository = new DonationRepository(new ArrayList<>()); - - - donationService = new DonationService(donationRepository, organizationRepository); - - customer = new Customer(1, "John", "Doe", "john@example.com", "$2a$10$hashed"); - } - - @Test - void testConstructorThrowsIfDonationRepositoryIsNull() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new DonationService(null, organizationRepository); - }); - assertEquals("DonationRepository can't be null", exception.getMessage()); - } - - @Test - void testConstructorThrowsIfOrganizationRepositoryIsNull() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - new DonationService(donationRepository, null); - }); - assertEquals("OrganizationRepository can't be null", exception.getMessage()); - } - - @Test - void getUserDonationsReturnsEmptyMapIfNoDonations() { - assertTrue(donationService.getUserDonations(customer.getUserId()).isEmpty()); - } - - @Test - void getOrganizationDonationsReturnsMapOfDonations() { - Donation donation1 = new Donation(1, customer.getUserId(), - 101, new BigDecimal("20.00"), Timestamp.from(Instant.now()), "Card"); - Donation donation2 = new Donation(2, customer.getUserId(), - 101, new BigDecimal("30.00"), Timestamp.from(Instant.now()), "PayPal"); - donationRepository.addContent(donation1); - donationRepository.addContent(donation2); - - Map donations = donationService.getOrganizationDonations(101); - assertEquals(2, donations.size()); - assertTrue(donations.containsKey(1)); - assertTrue(donations.containsKey(2)); - assertEquals(donation1, donations.get(1)); - assertEquals(donation2, donations.get(2)); - } - - @Test - void getOrganizationDonationsReturnsEmptyMapIfNoDonations() { - assertTrue(donationService.getOrganizationDonations(1).isEmpty()); - } - - @Test - void donateReturnsFalseIfCustomerNull() { - boolean result = donationService.donate(null, - 101, BigDecimal.TEN, "Card"); - assertFalse(result); - } - - @Test - void donateReturnsFalseIfAmountInvalid() { - assertFalse(donationService.donate(customer, - 101, null, "Card")); - assertFalse(donationService.donate(customer, - 101, BigDecimal.ZERO, "Card")); - assertFalse(donationService.donate(customer, - 101, new BigDecimal("-5"), "Card")); - } - - @Test - void donateReturnsFalseIfPaymentMethodBlank() { - assertFalse(donationService.donate(customer, - 101, BigDecimal.TEN, "")); - assertFalse(donationService.donate(customer, - 101, BigDecimal.TEN, " ")); - } - - @Test - void donateReturnsFalseIfOrganizationNotFound() { - boolean result = donationService.donate(customer, 999, BigDecimal.TEN, "Card"); - assertFalse(result); - } - - @Test - void testDonateAddsDonationSuccessfully() { - BigDecimal amount = new BigDecimal("50.00"); - String paymentMethod = "PayPal"; - boolean result = donationService.donate(customer, 101, amount, paymentMethod); - assertTrue(result); - assertEquals(1, donationRepository.getAllDonations().size()); - - Donation donation = donationRepository.getAllDonations().values().iterator().next(); - assertEquals(customer.getUserId(), donation.userId()); - assertEquals(101, donation.organizationId()); - assertEquals(amount, donation.amount()); - assertEquals(paymentMethod, donation.paymentMethod()); - assertTrue(donation.date().before(Timestamp.from(Instant.now())) || - donation.date().equals(Timestamp.from(Instant.now()))); - } - - @Test - void testDonateWithMultipleDonations() { - donationService.donate(customer, 101, new BigDecimal("10.00"), "PayPal"); - donationService.donate(customer, 101, new BigDecimal("25.00"), "Card"); - assertEquals(2, donationRepository.getAllDonations().size()); - } -} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/donation/DonationTest.java b/src/test/java/edu/group5/app/model/donation/DonationTest.java deleted file mode 100644 index 1de92f9..0000000 --- a/src/test/java/edu/group5/app/model/donation/DonationTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package edu.group5.app.model.donation; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.sql.Timestamp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class DonationTest { - - private String expectedMessage; - private int donationId1; - private int userId1; - private int organizationId1; - private BigDecimal amount1; - private Timestamp date1; - private String paymentMethod1; - private Donation donation; - - @BeforeEach - void setUp(){ - donationId1 = 1; - userId1 = 101; - organizationId1 = 202; - amount1 = new BigDecimal("500.0"); - date1 = new Timestamp(System.currentTimeMillis()); - paymentMethod1 = "Card"; - donation = new Donation(donationId1, userId1, - organizationId1, amount1, date1, paymentMethod1); - - } - - private void exceptionTest(int donationId, int userId, - int organizationId, BigDecimal amount, - Timestamp date, String paymentMethod) { - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, () -> new Donation( - donationId, userId, organizationId, amount, date, paymentMethod) - ); - assertEquals(expectedMessage, exception.getMessage()); - } - - @Test - void testIfDonationGetsDonationValues() { - - assertEquals(1, donation.donationId()); - assertEquals(101, donation.userId()); - assertEquals(202, donation.organizationId()); - assertEquals(new BigDecimal("500.0"), donation.amount()); - assertEquals(date1, donation.date()); - assertEquals("Card", donation.paymentMethod()); - } - - @Test - void testIfThrowsExceptionWhenDonationIdIsNotPositive() { - expectedMessage = "Donation ID must be positive"; - exceptionTest(0, userId1, organizationId1, - amount1, date1, paymentMethod1); - } - - @Test - void testIfThrowsExceptionWhenUserIdIsNotPositive() { - expectedMessage = "User ID must be positive"; - exceptionTest(donationId1, -1, organizationId1, - amount1, date1, paymentMethod1); - } - @Test - void testIfThrowsExceptionWhenOrganizationIdIsNotPositive() { - expectedMessage = "Organization ID must be positive"; - exceptionTest(donationId1, userId1, 0, - amount1, date1, paymentMethod1); - } - @Test - void testIfThrowsExceptionWhenAmountIsNegative() { - expectedMessage = "Amount must be positive and not null"; - exceptionTest(donationId1, userId1, organizationId1, - new BigDecimal("-1.00"), date1, paymentMethod1); - } - - @Test - void testIfThrowsExceptionWhenAmountIsNull() { - expectedMessage = "Amount must be positive and not null"; - exceptionTest(donationId1, userId1, organizationId1, - null, date1, paymentMethod1); - } - - @Test - void testIfThrowsExceptionWhenDateIsNull() { - expectedMessage = "Date must not be null"; - exceptionTest(donationId1, userId1, organizationId1, - amount1, null, paymentMethod1); - } - @Test - void testIfThrowsExceptionWhenPaymentMethodIsNullOrEmpty() { - expectedMessage = "Payment method must not be empty"; - exceptionTest(donationId1, userId1, organizationId1, - amount1, date1, null); - exceptionTest(donationId1, userId1, organizationId1, - amount1, date1, " "); - - } - - -} diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java deleted file mode 100644 index e6b0cf9..0000000 --- a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java +++ /dev/null @@ -1,181 +0,0 @@ -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_SkipsOrganizationWithMissingOrgNumber() { - Object[] content = new Object[] { - Map.of( - "name", "Good Org", - "status", "approved", - "url", "org.com", - "is_pre_approved", true - ), - Map.of( - "org_number", "999", - "name", "Bad Org", - "status", "approved", - "url", "org.com", - "is_pre_approved", true - ) - }; - - OrganizationRepository repo = new OrganizationRepository(content, scraper); - - assertEquals(1, repo.findByOrgNumber(999) != null ? 1 : 0); - assertNull(repo.findByOrgNumber(1)); - } - - @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/OrganizationScraperTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java deleted file mode 100644 index 5d48428..0000000 --- a/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package edu.group5.app.model.organization; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; - - -import static org.junit.jupiter.api.Assertions.*; - -class OrganizationScraperTest { - - private OrganizationScraper scraper; - - @BeforeEach - void setUp() { - scraper = new OrganizationScraper(); - } - - @Test - void fetchDescriptionReturnsNullWhenUrlIsNull() { - assertNull(scraper.fetchDescription(null)); - } - - @Test - void fetchDescriptionReturnsNullWhenUrlIsBlank() { - assertNull(scraper.fetchDescription("")); - } - - @Test - void fetchDescriptionReturnsNullWhenUrlIsInvalid() { - String result = scraper.fetchDescription("https://invalid-url-that-does-not-exist-xyz123.com"); - assertNull(result); - } - - @Test - void fetchDescriptionCachesResultOnSecondCall() { - // Mock URLs won't work, but cache still works with null returns - scraper.fetchDescription("https://example.com"); - scraper.fetchDescription("https://example.com"); - // If no exception thrown, cache works - assertTrue(true); - } - - @Test - void fetchLogoUrlReturnsNullWhenUrlIsNull() { - assertNull(scraper.fetchLogoUrl(null)); - } - - @Test - void fetchLogoUrlReturnsNullWhenUrlIsBlank() { - assertNull(scraper.fetchLogoUrl("")); - } - - @Test - void fetchLogoUrlReturnsNullWhenUrlIsInvalid() { - String result = scraper.fetchLogoUrl("https://invalid-url-that-does-not-exist-xyz123.com"); - assertNull(result); - } - - @Test - void fetchLogoUrlCachesResultOnSecondCall() { - // Mock URLs won't work, but cache still works with null returns - scraper.fetchLogoUrl("https://example.com"); - scraper.fetchLogoUrl("https://example.com"); - // If no exception thrown, cache works - assertTrue(true); - } - - @Test - void fetchDescriptionReturnsCachedValue() { - OrganizationScraper scraper = new OrganizationScraper(); - - // First call - caches (but makes real request) - String result1 = scraper.fetchDescription("https://example.com"); - - // Second call - should return cached without new request - String result2 = scraper.fetchDescription("https://example.com"); - - assertEquals(result1, result2); // Same object = cached - } - - @Test - void fetchDescriptionHandlesExceptionGracefully() { - String result = scraper.fetchDescription("https://invalid-domain-xyz.test"); - assertNull(result); - } - - @Test - void fetchLogoUrlHandlesExceptionGracefully() { - String result = scraper.fetchLogoUrl("https://invalid-domain-xyz.test"); - assertNull(result); - } - - @Test - void parseDescriptionExtractsParagraphs() { - String html = "

    First paragraph

    Second paragraph

    "; - Document doc = Jsoup.parse(html); - assertTrue(scraper.parseDescription(doc).contains("First paragraph")); - } - - @Test - void parseDescriptionUsesFallbackWhenNoParagraphs() { - String html = "
    Fallback text without paragraph tags
    "; - Document doc = Jsoup.parse(html); - assertEquals("Fallback text without paragraph tags", scraper.parseDescription(doc)); - } - - @Test - void parseDescriptionRemovesExtraInfoDivs() { - String html = "

    Keep this

    Remove this
    "; - Document doc = Jsoup.parse(html); - String result = scraper.parseDescription(doc); - assertTrue(result.contains("Keep this")); - assertFalse(result.contains("Remove this")); - } - - @Test - void parseDescriptionFiltersOutLesMer() { - String html = "

    Some text Les mer More text

    "; - Document doc = Jsoup.parse(html); - assertFalse(scraper.parseDescription(doc).contains("Les mer")); - } - - @Test - void parseDescriptionReturnsEmptyStringWhenNoSection() { - String html = "
    No section here
    "; - Document doc = Jsoup.parse(html); - assertEquals("", scraper.parseDescription(doc)); - } - - @Test - void parseLogoUrlExtractsImageUrl() { - String html = "
    "; - Document doc = Jsoup.parse(html); - doc.setBaseUri("https://example.com"); - assertTrue(scraper.parseLogoUrl(doc).contains("logo.png")); - } - - @Test - void parseLogoUrlReturnsEmptyWhenNoImage() { - String html = "
    "; - Document doc = Jsoup.parse(html); - assertEquals("", scraper.parseLogoUrl(doc)); - } -} \ 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 deleted file mode 100644 index 3765bf5..0000000 --- a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package edu.group5.app.model.organization; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -public class OrganizationServiceTest { - private OrganizationRepository repo; - private OrganizationService service; - private OrganizationScraper scraper; - private Object[] content; - - @BeforeEach - public void setUp() { - scraper = new OrganizationScraper(); - Map orgMap = new HashMap<>(); - orgMap.put("org_number", "1"); - orgMap.put("name", "Misjonsalliansen"); - orgMap.put("status", "approved"); - orgMap.put("url", "https://www.innsamlingskontrollen.no/organisasjoner/misjonsalliansen/"); - orgMap.put("is_pre_approved", false); - - content = new Object[]{orgMap}; - repo = new OrganizationRepository(content, scraper); - service = new OrganizationService(repo, scraper); - } - - @Test - void constructor_throwsIfRepositoryIsNull() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new OrganizationService(null, scraper)); - assertEquals("OrganizationRepository can't be null", ex.getMessage()); - } - - @Test - void constructor_throwsIfScraperIsNull() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new OrganizationService(repo, null)); - assertEquals("Scraper can't be null", ex.getMessage()); - } - - @Test - void testGetTrustedOrganizationsWithLogos() { - Map orgsWithLogos = service.getTrustedOrganizationsWithLogos(); - assertNotNull(orgsWithLogos); - assertTrue(orgsWithLogos.containsKey(1)); - Organization org = orgsWithLogos.get(1); - assertEquals(1, org.orgNumber()); - assertEquals("Misjonsalliansen", org.name()); - assertNotNull(org); - } - - @Test - void testGetTrustedOrganizationsWithLogosAsync() throws Exception { - CompletableFuture> futureOrgs = - service.getTrustedOrganizationsWithLogosAsync(); - - assertNotNull(futureOrgs); - Map orgsWithLogos = futureOrgs.get(); - assertNotNull(orgsWithLogos); - assertTrue(orgsWithLogos.containsKey(1)); - assertEquals("Misjonsalliansen", orgsWithLogos.get(1).name()); - } - - @Test - void testGetTrustedOrganizations() { - Map trustedOrgs = service.getTrustedOrganizations(); - assertNotNull(trustedOrgs); - assertTrue(trustedOrgs.containsKey(1)); - Organization org = trustedOrgs.get(1); - assertEquals(1, org.orgNumber()); - assertEquals("Misjonsalliansen", org.name()); - assertTrue(org.trusted()); - assertEquals("https://www.innsamlingskontrollen.no/organisasjoner/misjonsalliansen/", org.websiteUrl()); - assertFalse(org.isPreApproved()); - } - - @Test - void testFindByOrgNumber() { - Organization org = service.findByOrgNumber(1); - assertNotNull(org); - assertEquals(1, org.orgNumber()); - assertEquals("Misjonsalliansen", org.name()); - } - - - @Test - void testFindByOrgName() { - Organization org = service.findByOrgName("Misjonsalliansen"); - assertNotNull(org); - assertEquals(1, org.orgNumber()); - assertEquals("Misjonsalliansen", org.name()); - } -} - diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationTest.java deleted file mode 100644 index 0b97840..0000000 --- a/src/test/java/edu/group5/app/model/organization/OrganizationTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package edu.group5.app.model.organization; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class OrganizationTest { - - @Test - void constructor_CreatesAnOrganizationWhenInputIsValid() { - Organization org = new Organization( - 1, - "Org", - true, - "org.com", - true, - "Org description", - null - ); - - assertAll( - () -> assertEquals(1, org.orgNumber()), - () -> assertEquals("Org", org.name()), - () -> assertTrue(org.trusted()), - () -> assertEquals("org.com", org.websiteUrl()), - () -> assertTrue(org.isPreApproved()), - () -> assertEquals("Org description", org.description()) - ); - } - - @Test - void constructor_ThrowsWhenOrgNumberIsNegative() { - assertThrows(IllegalArgumentException.class, () -> new Organization( - -1, - "Org", - true, - "org.com", - true, - "Org description", - null - )); - } - - @Test - void constructor_ThrowsWhenNameIsNull() { - assertThrows(NullPointerException.class, () -> new Organization( - 1, - null, - true, - "org.com", - true, - "Org description", - null - )); - } - - @Test - void constructor_ThrowsWhenNameIsBlank() { - assertThrows(IllegalArgumentException.class, () -> new Organization( - 1, - "", - true, - "org.com", - true, - "Org description", - null - )); - } - - @Test - void constructor_ThrowsWhenWebsiteURLIsNull() { - assertThrows(NullPointerException.class, () -> new Organization( - 1, - "Org", - true, - null, - true, - "Org description", - null - )); - } - - @Test - void constructor_ThrowsWhenWebsiteURLIsBlank() { - assertThrows(IllegalArgumentException.class, () -> new Organization( - 1, - "Org", - true, - "", - true, - "Org description", - null - )); - } - - @Test - void constructor_ThrowsWhenDescriptionIsNull() { - assertThrows(NullPointerException.class, () -> new Organization( - 1, - "Org", - true, - "org.com", - true, - null, - null - )); - } - - @Test - void constructor_AcceptsNullLogoUrl() { - assertDoesNotThrow(() -> new Organization( - 1, - "Org", - true, - "org.com", - true, - "description", - null - )); - } -} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/user/CustomerTest.java b/src/test/java/edu/group5/app/model/user/CustomerTest.java deleted file mode 100644 index 147343f..0000000 --- a/src/test/java/edu/group5/app/model/user/CustomerTest.java +++ /dev/null @@ -1,221 +0,0 @@ -package edu.group5.app.model.user; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -public class CustomerTest { - - private int testUserId; - private String testFirstName; - private String testLastName; - private String testEmail; - private String testPassword; - private String testPasswordHash; - - @BeforeEach - void setUp() { - testUserId = 1; - testFirstName = "John"; - testLastName = "Doe"; - testEmail = "john.doe@example.com"; - testPassword = "password123"; - testPasswordHash = new BCryptPasswordEncoder().encode(testPassword); - } - - private void constructorTest(int userId, String firstName, - String lastName, String email, String passwordHash, - String expectedMessage) { - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new Customer(userId, firstName, lastName, email, passwordHash) - ); - - assertEquals(expectedMessage, exception.getMessage()); - } - - @Test - void constructorCreatesValidUser() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - assertEquals(testUserId, user.getUserId()); - assertEquals(testFirstName, user.getFirstName()); - assertEquals(testLastName, user.getLastName()); - assertEquals(testEmail, user.getEmail()); - assertEquals(testPasswordHash, user.getPasswordHash()); - } - - @Test - void testInstanceOfCustomer() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - assertTrue(user instanceof Customer); - } - - @Test - void constructorWithNegativeUserIdThrowsException() { - constructorTest(-1, testFirstName, testLastName, - testEmail, testPasswordHash, "User ID must be a positive integer"); - } - - @Test - void constructorWithNullFirstNameThrowsException() { - constructorTest(testUserId, null, testLastName, - testEmail, testPasswordHash, "First name can't be null"); - } - - @Test - void constructorWithEmptyFirstNameThrowsException() { - constructorTest(testUserId, "", testLastName, - testEmail, testPasswordHash, "First name can't be blank"); - } - - @Test - void constructorWithNullLastNameThrowsException() { - constructorTest(testUserId, testFirstName, null, - testEmail, testPasswordHash, "Last name can't be null"); - } - - @Test - void constructorWithEmptyLastNameThrowsException() { - constructorTest(testUserId, testFirstName, - "", testEmail, testPasswordHash, "Last name can't be blank"); - } - - @Test - void constructorWithNullEmailThrowsException() { - constructorTest(testUserId, testFirstName, testLastName, - null, testPasswordHash, "Email can't be null"); - } - - @Test - void constructorWithEmptyEmailThrowsException() { - constructorTest(testUserId, testFirstName, testLastName, - "", testPasswordHash, "Email can't be blank"); - } - - @Test - void constructorWithNullPasswordHashThrowsException() { - constructorTest(testUserId, testFirstName, testLastName, - testEmail, null, "Password hash can't be null"); - } - - @Test - void constructorWithEmptyPasswordHashThrowsException() { - constructorTest(testUserId, testFirstName, testLastName, - testEmail, "", "Password hash can't be blank"); - } - - - @Test - void verifyPasswordReturnsTrueForCorrectPassword() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - - assertTrue(user.verifyPassword(testPassword.toCharArray())); - } - - @Test - void verifyPasswordReturnsFalseForIncorrectPassword() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - - assertFalse(user.verifyPassword("wrongPassword".toCharArray())); - } - - @Test - void verifyPasswordReturnsFalseForNullPassword() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - - assertFalse(user.verifyPassword(null)); - } - - @Test - void verifyPasswordReturnsFalseForEmptyPassword() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - - assertFalse(user.verifyPassword("".toCharArray())); - } - - @Test - void verifyPasswordThrowsExceptionForTooLongPassword() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - char[] longPassword = new char[73]; // 73 characters, exceeding BCrypt limit - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> user.verifyPassword(longPassword) - ); - assertEquals("Password cannot be longer than 72 characters", exception.getMessage()); - } - - @Test - void getRoleReturnsCustomer() { - User user = new Customer(testUserId, testFirstName, - testLastName, testEmail, testPasswordHash); - assertEquals(UserRepository.ROLE_CUSTOMER, user.getRole()); - } - - @Test - void addPreferenceAddsOrganizationNumber() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - customer.addPreference(123); - assertTrue(customer.getPreferences().contains(123)); - } - - @Test - void addPreferenceWithNegativeOrgNumberThrowsException() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> customer.addPreference(-1) - ); - assertEquals("Organization number must be a positive integer", exception.getMessage()); - } - - @Test - void addPreferenceWithExistingOrgNumberThrowsException() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - customer.addPreference(123); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> customer.addPreference(123) - ); - assertEquals("Organization number already in preferences", exception.getMessage()); - } - - @Test - void removePreferenceRemovesOrganizationNumber() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - customer.addPreference(123); - customer.removePreference(123); - assertFalse(customer.getPreferences().contains(123)); - } - - @Test - void removePreferenceWithNegativeOrgNumberThrowsException() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> customer.removePreference(-1) - ); - assertEquals("Organization number must be a positive integer", exception.getMessage()); - } - - @Test - void removePreferenceWithNonExistingOrgNumberThrowsException() { - Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); - customer.addPreference(123); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> customer.removePreference(456) - ); - assertEquals("Organization number not found in preferences", exception.getMessage()); - } -} diff --git a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java deleted file mode 100644 index fd13b13..0000000 --- a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java +++ /dev/null @@ -1,148 +0,0 @@ -package edu.group5.app.model.user; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class UserRepositoryTest { - -private UserRepository repo; -private List rows; -private User additionalUser; - - @BeforeEach - void setUp() { - rows = new ArrayList<>(); - rows.add(new Object[]{1, "Customer", "John", "Cena", "john@example.com", "hashedpass"}); - rows.add(new Object[]{2, "Customer", "Jane", "Doe", "jane@example.com", "hashedpass"}); - repo = new UserRepository(rows); - this.additionalUser = new Customer(3, "John", "Doe", "john@example.com", "hashedpass"); - } - - @Test - void constructorThrowsIfNull() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserRepository(null)); - assertEquals("List of User rows can't be null", ex.getMessage()); - } - - @Test - void constructorThrowsIfInvalidRowLength() { - List invalid = new ArrayList<>(); - invalid.add(new Object[]{1, "Customer"}); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserRepository(invalid)); - assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); - } - - @Test - void constructorThrowsIfUnknownRole() { - List badRole = new ArrayList<>(); - badRole.add(new Object[]{3, "Admin", "Bob", "Smith", "bob@example.com", "pass"}); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserRepository(badRole)); - assertEquals("Unknown role: Admin", ex.getMessage()); - } - - @Test - void constructorThrowsIfRowNull() { - List invalid = new ArrayList<>(); - invalid.add(null); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserRepository(invalid)); - assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); - } - - @Test - void constructorThrowsIfRowHasIncorrectLength() { - List invalid = new ArrayList<>(); - invalid.add(new Object[]{1, "Customer", "John"}); - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserRepository(invalid)); - assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); - } - - @Test - void addContentSuccessfully() { - Customer user = new Customer(3, "Bob", "Smith", "bob@example.com", "pass"); - assertTrue(repo.addContent(user)); - assertEquals(3, repo.getUsers().size()); - } - - @Test - void addContentNullThrows() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.addContent(null)); - assertEquals("User can't be null", ex.getMessage()); - } - - @Test - void addContentDuplicateReturnsFalse() { - Customer user = new Customer(1, "John", "Cena", "john@example.com", "pass"); - assertFalse(repo.addContent(user)); - } - - @Test - void findUserByEmailSuccessfully() { - User u = repo.findUserByEmail("jane@example.com"); - assertEquals("Jane", u.getFirstName()); - } - - @Test - void findUserByEmailReturnsNullIfNotFound() { - assertNull(repo.findUserByEmail("notfound@example.com")); - } - - @Test - void findUserByEmailThrowsIfNullOrEmpty() { - IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class, - () -> repo.findUserByEmail(null)); - assertEquals("Email can't be null", ex1.getMessage()); - - IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class, - () -> repo.findUserByEmail(" ")); - assertEquals("Email can't be blank", ex2.getMessage()); - } - -@Test -void getUserByIdSuccessfully() { - User u = repo.getUserById(1); - assertEquals("John", u.getFirstName()); - } - -@Test -void getUserByIdReturnsNullIfNotFound() { - assertNull(repo.getUserById(999)); - } - - @Test - void getUserByIdThrowsIfNonPositive() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> repo.getUserById(0)); - assertEquals("User ID must be a positive integer", ex.getMessage()); - } - - - @Test - void getNextUserIdReturnsNextAvailable() { - int nextId = repo.getNextUserId(); - assertEquals(3, nextId); - } - - @Test - void getNextUserIdReturns1IfEmpty() { - UserRepository emptyRepo = new UserRepository(new ArrayList<>()); - assertEquals(1, emptyRepo.getNextUserId()); - } - - @Test - void exportExportsOnlyNewInformation() { - assertEquals(0, repo.export().size()); - repo.addContent(this.additionalUser); - assertEquals(1, repo.export().size()); - } -} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java deleted file mode 100644 index 459b635..0000000 --- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java +++ /dev/null @@ -1,163 +0,0 @@ -package edu.group5.app.model.user; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class UserServiceTest { - private UserRepository repo; - private UserService service; - - @BeforeEach - void setUp() { - List rows = new ArrayList<>(); - rows.add(new Object[]{1, "Customer", "John", "Cena", "john.cena@example.com", "$2a$10$hashed"}); - rows.add(new Object[]{2, "Customer", "Jane", "Doe", "jane.doe@example.com", "$2a$10$hashed"}); - repo = new UserRepository(rows); - service = new UserService(repo); - } - @Test - void constructorthrowsIfNull() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> new UserService(null)); - assertEquals("UserRepository can't be null", ex.getMessage()); - } - - @Test - void registerUserValid() { - boolean result = service.registerUser("Customer", "Alice", "Smith", - "alice@example.com", "$2a$10$hashed"); - assertTrue(result); - assertEquals(3, repo.getUsers().size()); - } - - @Test - void registerUserRoleCaseInsensitive() { - boolean result = service.registerUser("customer", "Bob", "Smith", - "bob@example.com", "$2a$10$hashed"); - assertTrue(result); - } - - @Test - void registerUserInvalidInputsReturnFalse() { - assertFalse(service.registerUser(null, "A", "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", null, "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", null, "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", "B", null, "pass")); - assertFalse(service.registerUser("Customer", "A", "B", "a@b.com", null)); - - assertFalse(service.registerUser("", "A", "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "", "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", "", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", "B", "", "pass")); - assertFalse(service.registerUser("Customer", "A", "B", "a@b.com", "")); - - assertFalse(service.registerUser(" ", "A", "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", " ", "B", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", " ", "a@b.com", "pass")); - assertFalse(service.registerUser("Customer", "A", "B", " ", "pass")); - assertFalse(service.registerUser("Customer", "A", "B", "a@b.com", " ")); - } - - @Test - void registerUserDuplicateEmailNotAllowedInCurrentCode() { - boolean result = service.registerUser("Customer", "John", "Cena", - "john.cena@example.com", "$2a$10$hashed"); - assertFalse(result); - assertEquals(2, repo.getUsers().size()); - } - - @Test - void registerUserInvalidRoleReturnsFalse() { - boolean result = service.registerUser("Admin", "X", "Y", "x@y.com", "pass"); - assertFalse(result); - } - - @Test - void loginValidPassword() { - String plainPassword = "password123"; - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - String hashedPassword = encoder.encode(plainPassword); - User testUser = new Customer(10, "Test", "User", "test@example.com", hashedPassword); - repo.addContent(testUser); - - User result = service.login("test@example.com", plainPassword.toCharArray()); - assertNotNull(result); - assertEquals("test@example.com", result.getEmail()); - assertTrue(result.verifyPassword(plainPassword.toCharArray())); - } - - @Test - void loginReturnsCorrectUser() { - String password = "1234"; - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - - User user = new Customer( - 99, "Alice", "Smith", "alice@test.com", - encoder.encode(password) - ); - - repo.addContent(user); - - User result = service.login("alice@test.com", password.toCharArray()); - - assertNotNull(result); - assertEquals(99, result.getUserId()); - assertEquals("Alice", result.getFirstName()); - } - - @Test - void loginInvalidPassword() { - String plainPassword = "password123"; - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - String hashedPassword = encoder.encode(plainPassword); - User testUser = new Customer(10, "Test", "User", "test@example.com", hashedPassword); - repo.addContent(testUser); - - User result = service.login("test@example.com", "wrongpass".toCharArray()); - User result2 = service.login("test@example.com", null); - User result3 = service.login("test@example.com", " ".toCharArray()); - User result4 = service.login("test@example.com", new char[0]); - assertNull(result); - assertNull(result2); - assertNull(result3); - assertNull(result4); - } - - @Test - void loginInvalidEmail() { - User result = service.login("nonexist@example.com", "password".toCharArray()); - User result2 = service.login(null, "password".toCharArray()); - User result3 = service.login(" ", "password".toCharArray()); - assertNull(result); - assertNull(result2); - assertNull(result3); - } - - @Test - void getUserByEmailValid() { - User result = service.getUserByEmail("jane.doe@example.com"); - assertEquals("Jane", result.getFirstName()); - } - - @Test - void getUserByEmailNotFound() { - User result = service.getUserByEmail(""); - User result2 = service.getUserByEmail(null); - User result3 = service.getUserByEmail(" "); - assertNull(result); - assertNull(result2); - assertNull(result3); - } -} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java deleted file mode 100644 index 289b803..0000000 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java +++ /dev/null @@ -1,245 +0,0 @@ -package edu.group5.app.model.wrapper; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * A test class for interactions with the donations table using the DbWrapper. - */ -public class DbWrapperDonationsTest { - private Object[] johnDonation; - private List users; - private Object[] janeDonation; - private Object[] cutoffDonation; - private Object[] freakyDonation; - private Object[] repeatingDonation; - private Object[] tooBigDonation; - private List donations; - private List donations2; - private List donations3; - private List repeatedDonations; - private List wrongFormatDonations; - private List wrongDatatypeDonations; - private List tooBigDonations; - private List nullList; - - private static final int PRECISION = 5; - - private DbWrapper db; - - @BeforeEach - void init() { - this.db = new DbWrapper(true); - String[] firstNames = new String[] { "John", "Jane", "Cutoff", "Freaky", "Repeating", "Big" }; - String[] lastNames = new String[] { "Doe", "Doe", "Joh", "Bill", "JoeJoe", "Willy" }; - this.users = new ArrayList(); - for (int i = 0; i < firstNames.length; i++) { - Object[] row = new Object[6]; - row[0] = i + 1; - row[1] = "Customer"; - row[2] = firstNames[i]; - row[3] = lastNames[i]; - row[4] = firstNames[i] + lastNames[i] + "@email.com"; - row[5] = "password"; - users.add(row); - } - - this.johnDonation = new Object[] { - 1, 1, 39, new BigDecimal(20.02), new Timestamp(new Date().getTime()), "Paypal" - }; - this.donations = new ArrayList(); - this.donations.add(this.johnDonation); - - this.janeDonation = new Object[] { - 2, 2, 39, new BigDecimal(20.00), new Timestamp(new Date().getTime()), "Visa debit card" - }; - this.donations2 = new ArrayList(); - this.donations2.add(this.johnDonation); - this.donations2.add(this.janeDonation); - - this.donations3 = new ArrayList(); - this.donations3.add(this.janeDonation); - - this.repeatingDonation = new Object[] { 3, 3, 333, new BigDecimal(18181818.18), - new Timestamp(new Date().getTime()), "Klarna installations payment" }; - this.repeatedDonations = new ArrayList(); - this.repeatedDonations.add(this.repeatingDonation); - this.repeatedDonations.add(this.repeatingDonation); - - this.cutoffDonation = new Object[] { 4, 4, 21 }; - this.wrongFormatDonations = new ArrayList(); - this.wrongFormatDonations.add(cutoffDonation); - - this.freakyDonation = new Object[] { 5, 5, "Freaks4Education", "lots", false, new String[6] }; - this.wrongDatatypeDonations = new ArrayList(); - this.wrongDatatypeDonations.add(freakyDonation); - - this.tooBigDonation = new Object[] { - 6, 6, 999999, new BigDecimal("9999999999999999999999999999999"), - new Timestamp(new Date().getTime()), "Azerbaijani technologies" - }; - this.tooBigDonations = new ArrayList(); - this.tooBigDonations.add(tooBigDonation); - - Object[] nullRow = new Object[] {null, null, null, null, null, null}; - this.nullList = new ArrayList(); - this.nullList.add(nullRow); - - this.db.connect(); - try { - this.db.exportUsers(users); - } catch (Exception e) { - // This exception won't happen - } - } - - private static boolean donationEquals(Object[] array1, Object[] array2) { - Object[] tempArray1 = array1.clone(); - tempArray1[3] = ((BigDecimal) tempArray1[3]).setScale(2, RoundingMode.HALF_UP); - Object[] tempArray2 = array2.clone(); - tempArray2[3] = ((BigDecimal) tempArray2[3]).setScale(2, RoundingMode.HALF_UP); - return Arrays.equals(tempArray1, tempArray2); - } - - @Test - public void importDonationsIsOnlyExportDonationsTest() { - assertDoesNotThrow(() -> { - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertTrue(this.db.exportDonations(this.donations) == 1); - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); - assertTrue( - donationEquals( - this.donations.get(0), - this.db.importDonations((int) this.users.get(0)[0]).get(0) - ) - ); - assertTrue(this.db.disconnect()); - }); - } - - @Test - public void nullDataInExportDonationsThrowsExpectedException() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(null); - }); - assertTrue(this.db.disconnect()); - assertEquals("data can't be null", exception.getMessage()); - } - - @Test - public void wronglyFormattedDonationsThrowsExpectedException() { - assertTrue(this.db.importDonations((int) this.users.get(2)[0]).size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.wrongFormatDonations); - }); - assertTrue(this.db.importDonations((int) this.users.get(2)[0]).size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("data's arrays must have a length of 6", exception.getMessage()); - } - - @Test - public void wronglyDatatypedDonationsThrowsExpectedException() { - assertTrue(this.db.importDonations((int) this.users.get(3)[0]).size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.wrongDatatypeDonations); - }); - assertTrue(this.db.importDonations((int) this.users.get(3)[0]).size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("One or more rows in data contains a wrong datatype", exception.getMessage()); - } - - @Test - public void addingSameDonationTwiceThrowsExpectedException() { - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertDoesNotThrow(() -> assertEquals(1, this.db.exportDonations(this.donations))); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.donations); - }); - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain existing rows", exception.getMessage()); - } - - @Test - public void addingSameDonationTwiceThrowsExpectedException2() { - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 0); - assertDoesNotThrow(() -> assertEquals(2, this.db.exportDonations(this.donations2))); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> { - this.db.exportDonations(this.donations); - } - ); - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); - assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 1); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain existing rows", exception.getMessage()); - } - - @Test - public void addingDifferentDonationsThrowsNoException() { - assertDoesNotThrow(() -> { - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 0); - assertEquals(1, this.db.exportDonations(this.donations)); - assertEquals(1, this.db.exportDonations(this.donations3)); - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); - assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 1); - assertTrue(this.db.disconnect()); - }); - } - - @Test - public void addingDonationListWithDuplicateIdsThrowsExpectedException() { - assertTrue(this.db.importDonations((int) this.users.get(4)[0]).size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.repeatedDonations); - }); - assertTrue(this.db.importDonations((int) this.users.get(4)[0]).size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain duplicate rows", exception.getMessage()); - } - - @Test - public void addingDonationListWithNullInRowThrowsExpectedException() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.nullList); - }); - assertTrue(this.db.disconnect()); - assertEquals("One or more rows in data contains null values", exception.getMessage()); - } - - @Test - public void dataIsEmptyAfterExportingAndImportingEmptyList() { - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertDoesNotThrow(() -> assertEquals(0, this.db.exportDonations(new ArrayList()))); - assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertTrue(this.db.disconnect()); - } - - @Test - public void exportTooBigNumberThrowsSQLException() { - SQLException exception = assertThrows(SQLException.class, () -> { - this.db.exportDonations(this.tooBigDonations); - }); - assertEquals( - "An unexpected SQL exception has occurred. " + - "This might be caused by inserting an item that is too large.", - exception.getMessage() - ); - } -} diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java deleted file mode 100644 index e8621d8..0000000 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java +++ /dev/null @@ -1,205 +0,0 @@ -package edu.group5.app.model.wrapper; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * A class for testing interactions with the users table of the database using DbWrapper. - */ -public class DbWrapperUserTest { - private Object[] johnDoe; - private Object[] janeDoe; - private Object[] cutoffJoh; - private Object[] freakyBill; - private Object[] repeatingJoeJoe; - private Object[] bigWilly; - private List users; - private List users2; - private List users3; - private List repeatedUsers; - private List wrongFormatUsers; - private List wrongDatatypeUsers; - private List tooBigUsers; - private List nullList; - - private DbWrapper db; - - @BeforeEach - void init() { - this.db = new DbWrapper(true); - this.johnDoe = new Object[] { 1, "Customer", "John", "Doe", "johndoe@email.com", "password" }; - this.users = new ArrayList(); - this.users.add(this.johnDoe); - - this.janeDoe = new Object[] { 2, "Customer", "Jane", "Doe", "janedoe@email.com", "qwerty" }; - this.users2 = new ArrayList(); - this.users2.add(this.johnDoe); - this.users2.add(this.janeDoe); - - this.users3 = new ArrayList(); - this.users3.add(this.janeDoe); - - this.repeatingJoeJoe = new Object[] { - 3, "Customer", "Repeating", "JoeJoe", "repeatingjjoe@email.com", "passwordpassword" - }; - this.repeatedUsers = new ArrayList(); - this.repeatedUsers.add(this.repeatingJoeJoe); - this.repeatedUsers.add(this.repeatingJoeJoe); - - this.cutoffJoh = new Object[] { 4, "Customer", "Cutoff", "Joh" }; - this.wrongFormatUsers = new ArrayList(); - this.wrongFormatUsers.add(cutoffJoh); - - this.freakyBill = new Object[] { 5, "Customer", 6.1805011125, "Bill", true, false }; - this.wrongDatatypeUsers = new ArrayList(); - this.wrongDatatypeUsers.add(freakyBill); - - this.bigWilly = new Object[] { - 6, "Customer", "Big", "Willy", "bigdwilly@waaaaaytoolargemail.com", "passssssssssssword" - }; - this.tooBigUsers = new ArrayList(); - this.tooBigUsers.add(this.bigWilly); - - Object[] nullRow = new Object[] {null, null, null, null, null, null}; - this.nullList = new ArrayList(); - this.nullList.add(nullRow); - } - - @Test - public void nonTestDbWrapperThrowsNoException() { - assertDoesNotThrow(() -> new DbWrapper(false)); - } - - @Test - public void importUsersIsOnlyExportUsersTest() { - assertDoesNotThrow(() -> { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.exportUsers(this.users) == 1); - assertTrue(this.db.importUsers().size() == 1); - assertTrue(Arrays.equals(this.db.importUsers().get(0), this.johnDoe)); - assertTrue(this.db.disconnect()); - }); - } - - @Test - public void nullDataInExportUsersThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(null); - }); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("data can't be null", exception.getMessage()); - } - - @Test - public void wronglyFormattedUsersThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.wrongFormatUsers); - }); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("data's arrays must have a length of 6", exception.getMessage()); - } - - @Test - public void wronglyDatatypedUsersThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.wrongDatatypeUsers); - }); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("One or more rows in data contains a wrong datatype", exception.getMessage()); - } - - @Test - public void addingSameUserTwiceThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - assertDoesNotThrow(() -> assertEquals(1, this.db.exportUsers(this.users))); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.users); - }); - assertTrue(this.db.importUsers().size() == 1); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain existing rows", exception.getMessage()); - } - - @Test - public void addingSameUserTwiceThrowsExpectedException2() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - assertDoesNotThrow(() -> assertEquals(2, this.db.exportUsers(this.users2))); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.users); - }); - assertTrue(this.db.importUsers().size() == 2); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain existing rows", exception.getMessage()); - } - - @Test - public void addingDifferentUsersThrowsNoException() { - assertDoesNotThrow(() -> { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - assertEquals(1, this.db.exportUsers(this.users)); - assertEquals(1, this.db.exportUsers(this.users3)); - assertTrue(this.db.importUsers().size() == 2); - assertTrue(this.db.disconnect()); - }); - } - - @Test - public void addingUserListWithDuplicateIdsThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.repeatedUsers); - }); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("data can't contain duplicate rows", exception.getMessage()); - } - - @Test - public void addingUserListWithNullInRowThrowsExpectedException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportUsers(this.nullList); - }); - assertTrue(this.db.importUsers().size() == 0); - assertTrue(this.db.disconnect()); - assertEquals("One or more rows in data contains null values", exception.getMessage()); - } - - @Test - public void exportTooLongNameThrowsSQLException() { - assertTrue(this.db.connect()); - assertTrue(this.db.importUsers().size() == 0); - SQLException exception = assertThrows(SQLException.class, () -> { - this.db.exportUsers(this.tooBigUsers); - }); - assertEquals( - "An unexpected SQL exception has occurred. " + - "This might be caused by inserting an item that is too large.", - exception.getMessage() - ); - } -} diff --git a/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java b/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java deleted file mode 100644 index 0165456..0000000 --- a/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package edu.group5.app.model.wrapper; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.lang.IllegalArgumentException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import tools.jackson.core.exc.StreamReadException; - -/** - * A test class for the OrgApiWrapper class. - */ -public class OrgApiWrapperTest { - String testUrl; - String wrongUrl; - String wrongUrl2; - - /** - * Initiates the urlStrings used for testing. - */ - @BeforeEach - void init() { - this.testUrl = "https://app.innsamlingskontrollen.no/api/public/v1/all"; - this.wrongUrl = "This is not a URL"; - this.wrongUrl2 = "https://www.google.com"; - } - - /** - * Checks if inputting null as the urlString throws the expected exception - * during construction. - */ - @Test - public void nullUrlThrowsException() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> new OrgApiWrapper(null)); - assertEquals("url can't be null", exception.getMessage()); - } - - /** - * Checks if inputting an empty urlString throws the expected exception during - * construction. - */ - @Test - public void emptyUrlThrowsException() { - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, () -> new OrgApiWrapper("")); - assertEquals("url can't be blank", exception.getMessage()); - } - - /** - * Checks if an invalid urlString throws the expected exception during - * construction. - */ - @Test - public void faultyUrlThrowsException() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> new OrgApiWrapper(this.wrongUrl)); - assertEquals("url has to be valid", exception.getMessage()); - } - - // /** - // * Checks if import returns False when there's no internet connection. - // */ - // @Test - // public void noConnectionReturnsFalseImport() { - // assertDoesNotThrow(() -> { - // OrgAPIWrapper api = new OrgAPIWrapper(this.testUrl); - // assertFalse(api.importData()); - // }); - // } - - /** - * Checks if import actually imports something. - */ - @Test - public void importsNonEmptyData() { - assertDoesNotThrow(() -> { - OrgApiWrapper api = new OrgApiWrapper(testUrl); - assertTrue(api.importData()); - assertFalse(api.getData().length == 0); - }); - } - - /** - * Checks if an unparseable website throws the expected exception. - */ - @Test - public void nonParseableSiteThrowsExceptionWhenImporting() { - assertDoesNotThrow(() -> { - OrgApiWrapper api = new OrgApiWrapper(this.wrongUrl2); - StreamReadException exception = assertThrows( - StreamReadException.class, () -> api.importData()); - assertEquals("The URL leads to a website that can't be parsed", exception.getMessage()); - }); - } - -} diff --git a/src/test/java/edu/group5/app/utils/ParameterValidatorTest.java b/src/test/java/edu/group5/app/utils/ParameterValidatorTest.java deleted file mode 100644 index bf8ac0c..0000000 --- a/src/test/java/edu/group5/app/utils/ParameterValidatorTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package edu.group5.app.utils; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class ParameterValidatorTest { - - @Test - void testValidatorDoesNotThrowWithValidParameters() { - assertDoesNotThrow(() -> ParameterValidator.stringChecker("valid", "validString")); - assertDoesNotThrow(() -> ParameterValidator.intChecker(1, "positiveInt")); - assertDoesNotThrow(() -> ParameterValidator.objectChecker(new Object(), "validObject")); - assertDoesNotThrow(() -> ParameterValidator.bigDecimalChecker(java.math.BigDecimal.valueOf(1), "positiveBigDecimal")); - } - - @Test - void testValidatorThrowsWithStringChecker() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.stringChecker(null, "nullString"); - }); - IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.stringChecker("", "emptyString"); - }); - IllegalArgumentException exception3 = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.stringChecker(" ", "blankString"); - }); - assertEquals("nullString can't be null", exception.getMessage()); - assertEquals("emptyString can't be blank", exception2.getMessage()); - assertEquals("blankString can't be blank", exception3.getMessage()); - } - - @Test - void testValidatorThrowsWithIntChecker() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.intChecker(-1, "negativeInt"); - }); - assertEquals("negativeInt must be a positive integer", exception.getMessage()); - } - - @Test - void testValidatorThrowsWithObjectChecker() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.objectChecker(null, "nullObject"); - }); - assertEquals("nullObject can't be null", exception.getMessage()); - } - - @Test - void testValidatorThrowsWithBigDecimalChecker() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.bigDecimalChecker(null, "nullBigDecimal"); - }); - IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.bigDecimalChecker(java.math.BigDecimal.valueOf(-1), "negativeBigDecimal"); - }); - IllegalArgumentException exception3 = assertThrows(IllegalArgumentException.class, () -> { - ParameterValidator.bigDecimalChecker(java.math.BigDecimal.ZERO, "zeroBigDecimal"); - }); - assertEquals("nullBigDecimal can't be null", exception.getMessage()); - assertEquals("negativeBigDecimal must be larger than 0", exception2.getMessage()); - assertEquals("zeroBigDecimal must be larger than 0", exception3.getMessage()); - } -} diff --git a/src/test/java/edu/group5/app/view/ViewTest.java b/src/test/java/edu/group5/app/view/ViewTest.java deleted file mode 100644 index bd0cd83..0000000 --- a/src/test/java/edu/group5/app/view/ViewTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package edu.group5.app.view; - -public class ViewTest { - -}