diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java index b9f0db4..782922c 100644 --- a/src/main/java/edu/group5/app/App.java +++ b/src/main/java/edu/group5/app/App.java @@ -3,30 +3,71 @@ import edu.group5.app.control.MainController; import edu.group5.app.control.wrapper.DbWrapper; import edu.group5.app.control.wrapper.OrgApiWrapper; +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.OrganizationService; +import edu.group5.app.model.user.UserRepository; +import edu.group5.app.model.user.UserService; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; +import java.util.List; + /** - * Hello world! + * Main entry point for the Help-Me-Help charity donation application. + * Handles database connection, data loading, and application setup. */ public class App extends Application { @Override - public void start(Stage stage) throws InterruptedException { + public void start(Stage stage) { + DbWrapper dbWrapper = new DbWrapper(true); OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all"); - orgApiWrapper.importData(); - OrganizationRepository organizationRepository = new OrganizationRepository(orgApiWrapper.getData()); - MainController controller = new MainController(); + if (!dbWrapper.connect()) { + System.err.println("Failed to connect to database"); + return; + } + + // 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 repositories with fetched data + UserRepository userRepository = new UserRepository(userData); + DonationRepository donationRepository = new DonationRepository(donationData); + OrganizationRepository organizationRepository = new OrganizationRepository(organizationData); + + // Create services (backend wiring) + UserService userService = new UserService(userRepository); + DonationService donationService = new DonationService(donationRepository, organizationRepository); + OrganizationService organizationService = new OrganizationService(organizationRepository); + + MainController controller = new MainController(userService, donationService, organizationService); Scene scene = controller.getMainView().getScene(); - controller.showHomePage(); + controller.showLoginPage(); stage.getIcons().add(new Image(getClass().getResource("/header/images/hmh-logo.png").toExternalForm())); stage.setTitle("Help-Me-Help"); stage.setScene(scene); stage.show(); } + + public static void main(String[] args) { + launch(args); + } } diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java new file mode 100644 index 0000000..89ac400 --- /dev/null +++ b/src/main/java/edu/group5/app/control/DonationController.java @@ -0,0 +1,4 @@ +package edu.group5.app.control; + +public class DonationController { +} diff --git a/src/main/java/edu/group5/app/control/MainController.java b/src/main/java/edu/group5/app/control/MainController.java deleted file mode 100644 index 7f48f19..0000000 --- a/src/main/java/edu/group5/app/control/MainController.java +++ /dev/null @@ -1,67 +0,0 @@ -package edu.group5.app.control; - -import edu.group5.app.model.user.User; -import edu.group5.app.view.MainView; - -public class MainController { - private final MainView view; - private final PageController pageController; - private User currentUser; - - public MainController() { - this.view = new MainView(this); - this.pageController = new PageController(this); - } - - public PageController getPageController() { - return this.pageController; - } - -// public void setCurrentUser(User user) { -// this.currentUser = user; -// } -// -// public User getCurrentUser() { -// return this.currentUser; -// } -// -// public void logout() { -// currentUser = null; -// showLoginPage(); -// } - - public MainView getMainView() { - return view; - } - - public void showHomePage() { - view.showHomePage(); - } - - public void showLoginPage() { - view.showLoginPage(); - } - public void showSignInPage() { - view.showSignInPage(); - } - public void showPaymentCompletePage() { - view.showPaymentCompletePage(); - } - public void showBrowsePage() { - view.showBrowsePage(); - } - - public void showOrganizationPage() { - view.showOrganizationPage(); - } - - public void showDonationPage() { - view.showDonationPage(); - } - - public void showAboutUsPage() {} - - public void showUserPage() { - view.showUserPage(); - } -} diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java new file mode 100644 index 0000000..6a7b3c3 --- /dev/null +++ b/src/main/java/edu/group5/app/control/NavigationController.java @@ -0,0 +1,99 @@ +package edu.group5.app.control; + +import edu.group5.app.model.donation.DonationService; +import edu.group5.app.model.organization.Organization; +import edu.group5.app.model.organization.OrganizationService; +import edu.group5.app.model.user.User; +import edu.group5.app.model.user.UserService; +import edu.group5.app.view.ViewManager; + +import java.math.BigDecimal; + +public class MainController { + private final ViewManager view; + + private final UserService userService; + private final DonationService donationService; + private final OrganizationService organizationService; + + private final UserController userController; + + private User currentUser; + private Organization currentOrganization; + private BigDecimal currentDonationAmount; + + public MainController(UserService userService, DonationService donationService, + OrganizationService organizationService) { + this.userService = userService; + this.donationService = donationService; + this.organizationService = organizationService; + + this.userController = new UserController(this, userService, donationService, organizationService); + + this.view = new ViewManager(this); + } + + public ViewManager getMainView() { + return this.view; + } + + public User getCurrentUser() { + return this.currentUser; + } + + public void setCurrentUser(User user) { + this.currentUser = user; + } + + public Organization getCurrentOrganization() { + return this.currentOrganization; + } + + public void setCurrentOrganization(Organization organization) { + this.currentOrganization = organization; + } + + public BigDecimal getCurrentDonationAmount() { + return this.currentDonationAmount; + } + + public void setCurrentDonationAmount(BigDecimal amount) { + this.currentDonationAmount = amount; + } + + public void showHomePage() { + view.showHomePage(); + } + + public void showLoginPage() { + view.showLoginPage(); + } + + public void showSignInPage() { + view.showSignInPage(); + } + + public void showPaymentCompletePage() { + view.showPaymentCompletePage(); + } + + public void showCausesPage() { + view.showCausesPage(); + } + + public void showOrganizationPage() { + view.showOrganizationPage(); + } + + public void showDonationPage() { + view.showDonationPage(); + } + + public void showAboutUsPage() { + view.showAboutUsPage(); + } + + public void showUserPage() { + view.showUserPage(); + } +} diff --git a/src/main/java/edu/group5/app/control/OrganizationController.java b/src/main/java/edu/group5/app/control/OrganizationController.java new file mode 100644 index 0000000..00d08b6 --- /dev/null +++ b/src/main/java/edu/group5/app/control/OrganizationController.java @@ -0,0 +1,4 @@ +package edu.group5.app.control; + +public class OrganizationController { +} diff --git a/src/main/java/edu/group5/app/control/PageController.java b/src/main/java/edu/group5/app/control/PageController.java deleted file mode 100644 index 0f52eb7..0000000 --- a/src/main/java/edu/group5/app/control/PageController.java +++ /dev/null @@ -1,66 +0,0 @@ -package edu.group5.app.control; - -public class PageController { - private final MainController controller; - - public PageController(MainController controller) { - this.controller = controller; - } - - public void handleHomeBtn() { - System.out.println("Home button pressed"); - controller.showHomePage(); - } - - public void handleCausesBtn() { - System.out.println("Causes button pressed"); - controller.showBrowsePage(); - } - - public void handleAboutBtn() { - System.out.println("About button pressed"); - } - - public void handleProfileBtn() { - System.out.println("profileSection"); - controller.showUserPage(); - } - - public void handleDonateToACauseBtn() { - System.out.println("Donate to a cause button pressed"); - controller.showBrowsePage(); - } - - public void handleRegisterBtn() { - System.out.println("Sign in button pressed"); - controller.showSignInPage(); - } - - public void handleDonateClick() { - controller.showDonationPage(); - } - - - public void handleAboutUsBtn() { - System.out.println("About us button pressed"); - controller.showAboutUsPage(); - } - - public void handleBrowseCardClick() { - controller.showDonationPage(); - } - - public void handleSignInBtn() { - System.out.println("Sign in button pressed"); - controller.showHomePage(); - } - public void handleLoginBtn() { - System.out.println("Back to login button pressed"); - controller.showLoginPage(); - } - - public void handleDonationBtn() { - System.out.println("Donating"); - controller.showPaymentCompletePage(); - } -} diff --git a/src/main/java/edu/group5/app/control/UserController.java b/src/main/java/edu/group5/app/control/UserController.java new file mode 100644 index 0000000..2ee376c --- /dev/null +++ b/src/main/java/edu/group5/app/control/UserController.java @@ -0,0 +1,4 @@ +package edu.group5.app.control; + +public class UserController { +} diff --git a/src/main/java/edu/group5/app/control/wrapper/DbWrapper.java b/src/main/java/edu/group5/app/control/wrapper/DbWrapper.java index 65196dc..babb929 100644 --- a/src/main/java/edu/group5/app/control/wrapper/DbWrapper.java +++ b/src/main/java/edu/group5/app/control/wrapper/DbWrapper.java @@ -141,7 +141,7 @@ public int exportUsers(List data) { return rowsAffected; } - private List importDonations() { + public List fetchAllDonations() { return this.importDonations(0, true); } @@ -182,8 +182,8 @@ private List importDonations(int user_id, boolean all) { return this.donations; } - public int exportDonations(List data) { - this.importDonations(); + public int exportDonations(List data) { + this.fetchAllDonations(); if (data == null) { throw new IllegalArgumentException("data can't be null"); diff --git a/src/main/java/edu/group5/app/model/AppState.java b/src/main/java/edu/group5/app/model/AppState.java new file mode 100644 index 0000000..96901b3 --- /dev/null +++ b/src/main/java/edu/group5/app/model/AppState.java @@ -0,0 +1,4 @@ +package edu.group5.app.model; + +public class AppState { +} diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java new file mode 100644 index 0000000..c5979f5 --- /dev/null +++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java @@ -0,0 +1,58 @@ +package edu.group5.app.model.organization; + +import java.util.Map; + +/** + * Service class for managing organization-related operations. + * It interacts with the OrganizationRepository to retrieve organization information + * and contains business logic associated with organization management. + */ +public class OrganizationService { + private OrganizationRepository organizationRepository; + + /** + * Constructs an OrganizationService with the given OrganizationRepository. + * @param organizationRepository the OrganizationRepository to use for managing organization data; must not be null + * @throws IllegalArgumentException if organizationRepository is null + */ + public OrganizationService(OrganizationRepository organizationRepository) { + if (organizationRepository == null) { + throw new IllegalArgumentException("OrganizationRepository cannot be null"); + } + this.organizationRepository = organizationRepository; + } + + /** + * Getter for the OrganizationRepository used by this service. + * @return the OrganizationRepository instance used by this service + */ + public OrganizationRepository getOrganizationRepository() { + return this.organizationRepository; + } + + /** + * Retrieves all trusted organizations. + * @return a map of trusted organizations by organization number + */ + public Map getTrustedOrganizations() { + return organizationRepository.getTrustedOrganizations(); + } + + /** + * 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) { + return organizationRepository.findByOrgName(name); + } +} diff --git a/src/main/java/edu/group5/app/model/user/UserService.java b/src/main/java/edu/group5/app/model/user/UserService.java index 44e092b..628c785 100644 --- a/src/main/java/edu/group5/app/model/user/UserService.java +++ b/src/main/java/edu/group5/app/model/user/UserService.java @@ -21,7 +21,7 @@ public UserService(UserRepository userRepository) { } /** - * Getter for the UserRepository used by this service. + * Getter for the UserRepository used by this service. * This method allows access to the user repository for managing user data and performing operations such as registration and login. * @return the UserRepository instance used by this service */ @@ -65,7 +65,7 @@ public boolean registerUser(String role, String firstName, String lastName, * 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 + * @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 */ @@ -79,4 +79,16 @@ public User login(String email, char[] password) { } 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/view/MainView.java b/src/main/java/edu/group5/app/view/MainView.java deleted file mode 100644 index 3ebf457..0000000 --- a/src/main/java/edu/group5/app/view/MainView.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.group5.app.view; - -import edu.group5.app.control.*; -import edu.group5.app.view.browsepage.BrowsePageView; -import edu.group5.app.control.*; -import edu.group5.app.model.user.Customer; -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.LoginPageView; -import edu.group5.app.view.loginpage.SignInPageView; -import edu.group5.app.view.userpage.UserPageView; -import edu.group5.app.view.organizationpage.OrganizationPageView; -import javafx.scene.Scene; -import javafx.scene.layout.BorderPane; - -public class MainView extends BorderPane { - private final MainController controller; - private final HomePageView homePageView; - private final LoginPageView loginPageView; - private final SignInPageView signInPageView; - private final BrowsePageView browsePageView; - private final OrganizationPageView organizationPageView; - private final DonationPageView donationPageView; - private final PaymentCompletePageView paymentCompletePageView; - private final UserPageView userPageView; - - public MainView(MainController controller) { - this.controller = controller; - Header header = new Header(controller.getPageController()); - this.homePageView = new HomePageView(controller.getPageController()); - this.loginPageView = new LoginPageView(controller.getPageController()); - this.signInPageView = new SignInPageView(controller.getPageController()); - this.browsePageView = new BrowsePageView(controller.getPageController()); - this.organizationPageView = new OrganizationPageView(controller.getPageController()); - this.donationPageView = new DonationPageView(controller.getPageController()); - this.paymentCompletePageView = new PaymentCompletePageView(controller.getPageController()); - this.userPageView = new UserPageView(controller.getPageController(), new Customer(1, "John", "Pedersen", "jh@gmail.com", "1234")); - - Scene root = new Scene(this, 1280, 720); - - setTop(header); - -// this.headerController = new HeaderController(mainController); -// this.homePageController = new HomePageController(mainController); -// this.loginPageController = new LoginPageController(mainController); -// this.signInPageController = new SignInPageController(mainController); -// this.donationPageController = new DonationPageController(mainController); -// this.paymentCompleteController = new PaymentCompleteController(mainController); - } - - public void showHomePage() { - setCenter(this.homePageView); - } - - public void showLoginPage() { - setCenter(this.loginPageView); - } - - public void showBrowsePage() { - setCenter(this.browsePageView); - } - - public void showOrganizationPage() { - setCenter(this.organizationPageView); - } - - public void showSignInPage() { - setCenter(this.signInPageView); - } - public void showDonationPage() { - setCenter(this.donationPageView); - } - public void showPaymentCompletePage() { - setCenter(this.paymentCompletePageView); - } - - public void showAboutUsPage() {} - - public void showUserPage() { - Customer testCustomer = new Customer( - 1, - "Jinwoo", - "Son", - "aurafarmer@gmail.com", - "hashedpassword" - ); - setCenter(this.userPageView); - } -} diff --git a/src/main/java/edu/group5/app/view/browsepage/BrowsePageView.java b/src/main/java/edu/group5/app/view/browsepage/BrowsePageView.java deleted file mode 100644 index b529765..0000000 --- a/src/main/java/edu/group5/app/view/browsepage/BrowsePageView.java +++ /dev/null @@ -1,71 +0,0 @@ -package edu.group5.app.view.browsepage; - -import edu.group5.app.control.PageController; -import javafx.geometry.Pos; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.layout.*; - -public class BrowsePageView extends BorderPane { - private final PageController controller; - - public BrowsePageView(PageController controller) { - this.controller = controller; - getStylesheets().add(getClass().getResource("/browsepage/browsepage.css").toExternalForm()); - setCenter(createBody()); - } - - private ScrollPane createBody() { - ScrollPane body = new ScrollPane(); - body.setId("body"); - body.setFitToWidth(true); - VBox vBox = new VBox(); - vBox.setSpacing(10); - vBox.getChildren().addAll( - createSearchSection(), - createOrganizationSection() - ); - body.setContent(vBox); - return body; - } - - private HBox createSearchSection() { - HBox searchSection = new HBox(); - TextField searchField = new TextField(); - searchField.setPromptText("Search.."); - searchSection.getChildren().add(searchField); - return searchSection; - } - - private GridPane createOrganizationSection() { - GridPane grid = new GridPane(); - grid.setId("card-grid"); - grid.setHgap(10); - grid.setVgap(10); - grid.setMaxWidth(Double.MAX_VALUE - 50); - - int column = 0; - int row = 0; - for (int i = 0; i < 16; i++) { - BrowseCard card = new BrowseCard(controller, "/browsepage/images/children_of_shambala.png", "Shambala Foundation"); - - GridPane.setFillWidth(card, true); - grid.setAlignment(Pos.CENTER); - 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); - } - return grid; - } -} diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java new file mode 100644 index 0000000..d19b735 --- /dev/null +++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java @@ -0,0 +1,132 @@ +package edu.group5.app.view.browsepage; + +import edu.group5.app.control.PageController; +import edu.group5.app.model.organization.Organization; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.*; + +import java.util.Map; +import java.util.stream.Collectors; + +public class BrowsePageView extends BorderPane { + private final PageController controller; + private GridPane organizationGrid; + private Map allOrganizations; + + public BrowsePageView(PageController controller) { + this.controller = controller; + getStylesheets().add(getClass().getResource("/browsepage/browsepage.css").toExternalForm()); + setCenter(createBody()); + } + + private ScrollPane createBody() { + 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); + vBox.getChildren().addAll( + createSearchSection(), + createOrganizationSection(null) + ); + body.setContent(vBox); + return body; + } + + 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); + + if (allOrganizations == null) { + System.out.println("Controller: " + controller.getMainController().getOrganizationService()); + allOrganizations = controller.getMainController().getOrganizationService().getTrustedOrganizations(); + } + + // Filter organizations by search term + Map organizations = filterOrganizations(searchTerm); + + int column = 0; + int row = 0; + for (int i = 0; i < 16; i++) { + for (Organization org : organizations.values()) { + String defaultImg = "/browsepage/images/children_of_shambala.png"; + BrowseCard card = new BrowseCard(controller, org, defaultImg); + + 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)) + .collect(Collectors.toMap( + Organization::orgNumber, + org -> org + )); + } + + private void updateOrganizationGrid(String searchTerm) { + if (organizationGrid == null) { + return; + } + + organizationGrid.getChildren().clear(); + organizationGrid.getColumnConstraints().clear(); + + // Rebuild grid with filtered organizations + GridPane updated = createOrganizationSection(searchTerm); + + organizationGrid.getChildren().addAll(updated.getChildren()); + organizationGrid.getColumnConstraints().addAll(updated.getColumnConstraints()); + } +} diff --git a/src/main/java/edu/group5/app/view/browsepage/BrowseCard.java b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java similarity index 76% rename from src/main/java/edu/group5/app/view/browsepage/BrowseCard.java rename to src/main/java/edu/group5/app/view/causespage/OrganizationCard.java index 3a46244..ccdb76a 100644 --- a/src/main/java/edu/group5/app/view/browsepage/BrowseCard.java +++ b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java @@ -1,6 +1,6 @@ -package edu.group5.app.view.browsepage; +package edu.group5.app.view.causespage; -import edu.group5.app.control.PageController; +import edu.group5.app.model.organization.Organization; import javafx.geometry.Pos; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -8,25 +8,27 @@ import javafx.scene.layout.VBox; import javafx.scene.text.Text; -public class BrowseCard extends VBox { +public class OrgCard extends VBox { + private final Organization organization; private final PageController controller; - public BrowseCard(PageController controller, String img, String name) { + public OrgCard(PageController controller, Organization org, String img) { this.controller = controller; + this.organization = org; setId("mainContainer"); getStylesheets().add(getClass().getResource("/browsepage/browse_org.css").toExternalForm()); getChildren().addAll( imageContainer(img), - orgName(name), + orgName(org.name()), checkMarkContainer() ); setOnMouseClicked(e -> { - controller.handleBrowseCardClick(); + controller.handleBrowseCardClick(organization); }); - setSpacing(20); + setSpacing(10); setFillWidth(true); setAlignment(Pos.CENTER); } @@ -34,8 +36,8 @@ public BrowseCard(PageController controller, String img, String name) { private StackPane imageContainer(String img) { StackPane imageContainer = new StackPane(); imageContainer.setId("imageContainer"); - imageContainer.setPrefHeight(120); - imageContainer.setPrefWidth(120); + imageContainer.setPrefHeight(80); + imageContainer.setPrefWidth(80); imageContainer.setMaxWidth(Double.MAX_VALUE); ImageView logo = new ImageView( @@ -45,7 +47,7 @@ private StackPane imageContainer(String img) { logo.setId("logo"); logo.setSmooth(true); logo.setPreserveRatio(true); - logo.setFitHeight(150); + logo.setFitHeight(80); imageContainer.getChildren().add(logo); return imageContainer; @@ -54,6 +56,7 @@ private StackPane imageContainer(String img) { private Text orgName(String text) { Text orgName = new Text(text); orgName.setId("orgName"); + orgName.setWrappingWidth(150); return orgName; } diff --git a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java index f521657..31919f4 100644 --- a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java +++ b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java @@ -11,9 +11,18 @@ 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class DonationPageView extends BorderPane { private final PageController controller; + private final List allDonationElements = new ArrayList<>(); + private final Map elementAmounts = new HashMap<>(); public DonationPageView(PageController controller) { this.controller = controller; @@ -56,13 +65,14 @@ public Button createDonationButton(String title, String amount) { button.setTextAlignment(TextAlignment.CENTER); button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); button.getStyleClass().add("donation-button"); + + BigDecimal parsedAmount = parseAmount(amount); + elementAmounts.put(button, parsedAmount); + button.setOnAction(e -> { - if (button.getStyleClass().contains("donation-button-selected")) { - button.getStyleClass().remove("donation-button-selected"); - } else { - button.getStyleClass().add("donation-button-selected"); - } + selectDonationElement(button); }); + allDonationElements.add(button); return button; } private VBox createCustomButton() { @@ -84,6 +94,19 @@ private VBox createCustomButton() { box.setAlignment(Pos.CENTER); box.getStyleClass().add("donation-button"); + + box.setOnMouseClicked(e -> { + try { + BigDecimal amount = new BigDecimal(amountField.getText().trim()); + elementAmounts.put(box, amount); + selectDonationElement(box); + } catch (NumberFormatException exception) { + System.err.println("Invalid custom donation amount: " + amountField.getText()); + } + + }); + + allDonationElements.add(box); return box; } private HBox createDonateSection() { @@ -95,7 +118,35 @@ private HBox createDonateSection() { section.setAlignment(Pos.CENTER); section.setPadding(new Insets(20, 0, 30, 0)); return section; + } + private void selectDonationElement(Node element) { + // Remove selected class from all elements + for (Node node : allDonationElements) { + node.getStyleClass().remove("donation-button-selected"); + } + + element.getStyleClass().add("donation-button-selected"); + + // Extract and store the amount + extractAndStoreAmount(element); + } + + private void extractAndStoreAmount(Node element) { + BigDecimal amount = elementAmounts.get(element); + if (amount != null) { + controller.setCurrentDonationAmount(amount); + } else { + System.err.println("Error: No amount found for selected element"); + } + } + + private BigDecimal parseAmount(String amountStr) { + try { + return new BigDecimal(amountStr.replace("kr", "").trim()); + } catch (NumberFormatException e) { + return BigDecimal.ZERO; + } } -} +} \ No newline at end of file diff --git a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java index ba306ee..0e5f0a6 100644 --- a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java +++ b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java @@ -13,6 +13,9 @@ public class LoginPageView extends BorderPane { private final PageController controller; + private TextField emailField; + private PasswordField passwordField; + private Label errorLabel; public LoginPageView(PageController controller) { this.controller = controller; @@ -31,6 +34,20 @@ public LoginPageView(PageController controller) { 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); @@ -42,13 +59,20 @@ private VBox getLoginBox() { VBox loginSection = new VBox(12); loginSection.setAlignment(Pos.CENTER); loginSection.setId("login-box"); - loginSection.getChildren().addAll(getEmailBox(), getPasswordBox(), getLoginBtn()); + 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); - TextField emailField = new TextField(); + emailField = new TextField(); emailField.setPromptText("aurafarmer@gmail.com"); emailField.setMaxWidth(300); emailBox.getChildren().addAll(new Label("Email"), emailField); @@ -57,7 +81,7 @@ private VBox getEmailBox() { private VBox getPasswordBox() { VBox passwordBox = new VBox(); passwordBox.setMaxWidth(300); - PasswordField passwordField = new PasswordField(); + passwordField = new PasswordField(); passwordField.setMaxWidth(300); passwordBox.getChildren().addAll(new Label("Password"), passwordField); return passwordBox; @@ -66,6 +90,7 @@ private Button getLoginBtn() { Button loginBtn = new Button("Log In"); loginBtn.setMaxWidth(300); loginBtn.setId("login-btn"); + loginBtn.setOnMouseClicked(e -> controller.handleLoginBtn()); return loginBtn; } public Button getRegisterBtn() { diff --git a/src/main/java/edu/group5/app/view/loginpage/SignInPageView.java b/src/main/java/edu/group5/app/view/loginpage/SignInPageView.java index 01b0a44..7f147dd 100644 --- a/src/main/java/edu/group5/app/view/loginpage/SignInPageView.java +++ b/src/main/java/edu/group5/app/view/loginpage/SignInPageView.java @@ -13,6 +13,11 @@ public class SignInPageView extends BorderPane { private final PageController controller; + private TextField nameField; + private TextField surnameField; + private TextField emailField; + private PasswordField passwordField; + private Label errorLabel; public SignInPageView(PageController controller) { this.controller = controller; @@ -31,6 +36,29 @@ public SignInPageView(PageController controller) { 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.setStyle("-fx-text-fill: red;"); + } + private VBox getOuterSection() { VBox outerSection = new VBox(12); outerSection.setAlignment(Pos.CENTER); @@ -42,21 +70,28 @@ private VBox getSignInBox() { VBox signInSection = new VBox(12); signInSection.setAlignment(Pos.CENTER); signInSection.setId("login-box"); - signInSection.getChildren().addAll(getNameRow(), getEmailBox(), getPasswordBox(), getSignInBtn()); + signInSection.getChildren().addAll(getErrorLabel(), getNameRow(), getEmailBox(), getPasswordBox(), getSignInBtn()); return signInSection; } + + private Label getErrorLabel() { + errorLabel = new Label(); + errorLabel.setPrefHeight(20); + return errorLabel; + } + private HBox getNameRow() { HBox nameRow = new HBox(12); nameRow.setMaxWidth(300); VBox nameBox = new VBox(); - TextField nameField = new TextField(); + nameField = new TextField(); nameField.setPromptText("Jinwoo"); HBox.setHgrow(nameBox, Priority.ALWAYS); nameBox.getChildren().addAll(new Label("First name"), nameField); VBox surnameBox = new VBox(); - TextField surnameField = new TextField(); + surnameField = new TextField(); surnameField.setPromptText("Son"); HBox.setHgrow(surnameBox, Priority.ALWAYS); surnameBox.getChildren().addAll(new Label("Last Name"), surnameField); @@ -68,7 +103,7 @@ private HBox getNameRow() { private VBox getEmailBox() { VBox emailBox = new VBox(); emailBox.setMaxWidth(300); - TextField emailField = new TextField(); + emailField = new TextField(); emailField.setPromptText("aurafarmer@gmail.com"); emailField.setMaxWidth(300); emailBox.getChildren().addAll(new Label("Email"), emailField); @@ -77,7 +112,7 @@ private VBox getEmailBox() { private VBox getPasswordBox() { VBox passwordBox = new VBox(); passwordBox.setMaxWidth(300); - PasswordField passwordField = new PasswordField(); + passwordField = new PasswordField(); passwordField.setMaxWidth(300); passwordBox.getChildren().addAll(new Label("Password"), passwordField); return passwordBox; diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java index 24ee8f7..2d2aee6 100644 --- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java +++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java @@ -1,7 +1,7 @@ package edu.group5.app.view.organizationpage; import edu.group5.app.control.PageController; -import edu.group5.app.view.Header; +import edu.group5.app.model.organization.Organization; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -55,8 +55,11 @@ private StackPane createImageContainer() { imageContainer.setPrefWidth(120); imageContainer.setMaxWidth(Double.MAX_VALUE); + Organization org = controller.getCurrentOrganization(); + String imagePath = "/browsepage/images/children_of_shambala.png"; + ImageView logo = new ImageView( - new Image(getClass().getResource("/browsepage/images/children_of_shambala.png").toExternalForm()) + new Image(getClass().getResource(imagePath).toExternalForm()) ); logo.setId("logo"); @@ -68,15 +71,17 @@ private StackPane createImageContainer() { } private VBox createOrgInfoSection() { + Organization org = controller.getCurrentOrganization(); + VBox orgInfoSection = new VBox(); orgInfoSection.setSpacing(50); VBox orgNameAndDescription = new VBox(); - Label orgName = new Label("Shambala Foundation"); + Label orgName = new Label(org != null ? org.name() : "Unknown Organization"); orgName.setId("orgName"); - Text description = new Text("Descriptive text"); + Text description = new Text(org != null ? org.description() : "No description available"); description.setId("description"); orgNameAndDescription.getChildren().addAll(orgName, description); diff --git a/src/main/java/edu/group5/app/view/userpage/UserPageView.java b/src/main/java/edu/group5/app/view/userpage/UserPageView.java index 2678ae8..a538f4d 100644 --- a/src/main/java/edu/group5/app/view/userpage/UserPageView.java +++ b/src/main/java/edu/group5/app/view/userpage/UserPageView.java @@ -2,26 +2,32 @@ import edu.group5.app.control.PageController; import edu.group5.app.model.user.Customer; -import edu.group5.app.view.Header; +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.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + public class UserPageView extends BorderPane { private final PageController controller; - private final Customer customer; + private final User currentUser; public UserPageView(PageController controller, Customer customer) { this.controller = controller; - this.customer = customer; + this.currentUser = controller.getCurrentUser(); getStylesheets().add(getClass().getResource("/userpage/userpage.css").toExternalForm()); VBox content = new VBox(30); @@ -29,6 +35,7 @@ public UserPageView(PageController controller, Customer customer) { content.getChildren().addAll(createProfileSection(), createCausesSection(), createDonationsSection()); setCenter(content); } + private HBox createProfileSection() { ImageView avatar = new ImageView(new Image(getClass().getResourceAsStream("/userpage/account_circle.png"))); avatar.setFitWidth(150); @@ -36,39 +43,89 @@ private HBox createProfileSection() { avatar.setPreserveRatio(true); avatar.setId("avatar"); - Text name = new Text(customer.getFirstName() + " " + customer.getLastName()); + Text name = new Text(currentUser.getFirstName() + " " + currentUser.getLastName()); name.setId("profile-name"); - Label email = new Label(customer.getEmail()); + Label email = new Label(currentUser.getEmail()); email.getStyleClass().add("profile-info"); Label location = new Label("Trondheim, Norway"); location.getStyleClass().add("profile-info"); - VBox info = new VBox(10, name, email, location); + Button logoutBtn = new Button("Logout"); + logoutBtn.getStyleClass().add("logout-button"); + logoutBtn.setOnAction(e -> controller.logout()); + + 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"); - Pane causesPlaceholder = new Pane(); - causesPlaceholder.getStyleClass().add("section-box"); + VBox causesBox = new VBox(10); + causesBox.getStyleClass().add("section-box"); + causesBox.setPadding(new Insets(10)); + + HashMap userDonations = controller.getDonationService() + .getDonationRepository().filterByUser(currentUser.getUserId()); + + Set uniqueOrgs = new HashSet<>(); + for (Donation donation : userDonations.values()) { + uniqueOrgs.add(donation.organizationId()); + } - return new VBox(10, title, causesPlaceholder); + if (uniqueOrgs.isEmpty()) { + Label noCauses = new Label("No causes supported yet"); + noCauses.setStyle("-fx-text-fill: #999;"); + causesBox.getChildren().add(noCauses); + } else { + for (int orgId : uniqueOrgs) { + Organization org = controller.getOrganizationService().findByOrgNumber(orgId); + if (org != null) { + Label causeLabel = new Label("• " + org.name()); + causesBox.getChildren().add(causeLabel); + } + } + } + + return new VBox(10, title, causesBox); } + private VBox createDonationsSection() { Text title = new Text("PREVIOUS DONATIONS"); title.getStyleClass().add("section-title"); - Pane donationsPlaceholder = new Pane(); - donationsPlaceholder.getStyleClass().add("section-box"); + VBox donationsBox = new VBox(10); + donationsBox.getStyleClass().add("section-box"); + donationsBox.setPadding(new Insets(10)); + + HashMap userDonations = controller.getDonationService() + .getDonationRepository().filterByUser(currentUser.getUserId()); + + 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 = controller.getOrganizationService() + .findByOrgNumber(donation.organizationId()); + String orgName = (org != null) ? org.name() : "Unknown Organization"; + + Label donationLabel = new Label( + orgName + " • " + donation.amount() + " kr" + " • " + donation.date() + ); + donationsBox.getChildren().add(donationLabel); + } + } - return new VBox(10, title, donationsPlaceholder); + return new VBox(10, title, donationsBox); } } diff --git a/src/main/resources/browsepage/browse_org.css b/src/main/resources/browsepage/browse_org.css index 96ce14e..9a50c71 100644 --- a/src/main/resources/browsepage/browse_org.css +++ b/src/main/resources/browsepage/browse_org.css @@ -3,7 +3,6 @@ -fx-border-width: 1px; -fx-border-radius: 1em; -fx-padding: 5px; - -fx-pref-width: 10px; -fx-background-color: white; -fx-background-radius: 1em; } @@ -20,6 +19,7 @@ #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/userpage/userpage.css b/src/main/resources/userpage/userpage.css index 6253079..8401a77 100644 --- a/src/main/resources/userpage/userpage.css +++ b/src/main/resources/userpage/userpage.css @@ -16,4 +16,16 @@ -fx-pref-height: 120px; -fx-pref-width: 700px; -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; } \ 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 new file mode 100644 index 0000000..e34aba7 --- /dev/null +++ b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java @@ -0,0 +1,70 @@ +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; + +public class OrganizationServiceTest { + private OrganizationRepository repo; + private OrganizationService service; + private Object[] content; + + @BeforeEach + public void setUp() { + 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); + service = new OrganizationService(repo); + } + + @Test + void constructor_throwsIfNull() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new OrganizationService(null)); + assertEquals("OrganizationRepository cannot be null", ex.getMessage()); + } + + @Test + void testGetOrganizationRepository() { + assertEquals(repo, service.getOrganizationRepository()); + } + + @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/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java index c6713d7..8f56957 100644 --- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java +++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java @@ -17,7 +17,7 @@ public class UserServiceTest { private UserRepository repo; - private UserService service; + private UserService service; @BeforeEach void setUp() {