Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/main/java/application/user/UserEditProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package application.user;

import application.security.PasswordHasher;
import domain.user.User;
import persistence.dao.UserDao;
import persistence.dao.UserRepository;

public class UserEditProfile {

private final PasswordHasher hasher;
private final UserRepository repo;


public UserEditProfile(PasswordHasher hasher, UserRepository repo) {
this.hasher = hasher;
this.repo = repo;
}


public void execute(User user, String username, String email, String phoneNumber, String password) {
user.setUsername(username);
user.setEmail(email);
user.setPhoneNumber(phoneNumber);
if (password != null && !password.isBlank()) {
user.setPassword(hasher.hash(password));
}
repo.updateUser(user);
}
}
61 changes: 61 additions & 0 deletions src/main/java/application/user/UserFavorite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package application.user;

import domain.organization.Organization;
import persistence.dao.FavoriteDao;


import java.util.List;

public class UserFavorite {

private final FavoriteDao favoriteDao;

/**
* Use case class for managing a user's favourite organizations.
* Delegates all persistence operations to {@link FavoriteDao}.
*/
public UserFavorite(FavoriteDao favouriteDao) {
this.favoriteDao = favouriteDao;
}

/**
* Adds an organization to a user's favorites.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number to add as favourite
*/
public void addFavorite(long userId, String orgNumber) {
favoriteDao.addFavorite(userId, orgNumber);
}

/**
* Removes an organization from a user's favorites.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number to remove
*/
public void removeFavorite(long userId, String orgNumber) {
favoriteDao.removeFavorite(userId, orgNumber);
}

/**
* Retrieves all favorite organizations for a given user.
*
* @param userId the unique identifier of the user
* @return a list of {@link Organization} objects the user has saved as favourites
*/
public List<Organization> getFavorites(long userId) {
return favoriteDao.getFavorites(userId);
}

/**
* Checks whether an organization is saved as a favourite by a specific user.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number to check
* @return {@code true} if the organization is a favourite, {@code false} otherwise
*/
public boolean isFavorite(long userId, String orgNumber) {
return favoriteDao.isFavorite(userId, orgNumber);
}
}
114 changes: 114 additions & 0 deletions src/main/java/persistence/dao/FavoriteDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package persistence.dao;

import domain.organization.Organization;
import persistence.db.Database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class FavoriteDao {


/**
* Inserts a new favorite into the database.
* Links a user to an organization they have saved as a favourite.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number of the favourite organization
* @throws RuntimeException if a database error occurs during the insert
*/
public void addFavorite(long userId, String orgNumber) {
String sql = "INSERT INTO favourite(user_id, organization_id) VALUES(?, ?)";

try (Connection conn = Database.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setLong(1, userId);
stmt.setString(2, orgNumber);
stmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

/**
* Removes a favorite from the database.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number of the favourite organization
* @throws RuntimeException if a database error occurs during the insert
*/
public void removeFavorite(long userId, String orgNumber) {
String sql = "DELETE FROM favourite WHERE user_id = ? AND organization_id = ?";

try (Connection conn = Database.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setLong(1, userId);
stmt.setString(2, orgNumber);
stmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

/**
* Retrieves all favorite organizations for a specific user.
*
* @param userId the unique identifier of the user
* @return a list of {@link Organization} objects the user has saved as favourites;
* empty list if none found
* @throws RuntimeException if a database error occurs during the query
*/
public List<Organization> getFavorites(long userId) {
String sql = """
SELECT o.* FROM favourite f
JOIN organization o ON f.organization_id = o.org_number
WHERE f.user_id = ?""";

try (Connection conn = Database.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setLong(1, userId);
ResultSet rs = stmt.executeQuery();
List<Organization> list = new ArrayList<>();

while (rs.next()) {
Organization org = new Organization();
org.setOrgNumber(rs.getString("org_number"));
org.setName(rs.getString("name"));
org.setStatus(rs.getString("status"));
org.setUrl(rs.getString("url"));
org.setPreApproved(rs.getInt("is_pre_approved") == 1);
list.add(org);
}
return list;

} catch (SQLException e) {
throw new RuntimeException(e);
}
}

/**
* Checks whether an organization is saved as a favorite by a specific user.
*
* @param userId the unique identifier of the user
* @param orgNumber the organization number to check
* @return {@code true} if the organization is a favourite, {@code false} otherwise
* @throws RuntimeException if a database error occurs during the query
*/
public boolean isFavorite(long userId, String orgNumber) {
String sql = "SELECT COUNT(*) FROM favourite WHERE user_id = ? AND organization_id = ?";

try (Connection conn = Database.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setLong(1, userId);
stmt.setString(2, orgNumber);
ResultSet rs = stmt.executeQuery();
return rs.next() && rs.getInt(1) > 0;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/persistence/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,19 @@ private User mapUser(ResultSet rs) throws SQLException {
return user;
}

public void updateUser(User user) {
String sql = "UPDATE user SET user_name = ?, phone_number = ?, e_mail = ?, password_hash = ? WHERE id = ?";

try (Connection conn = Database.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getUsername());
stmt.setString(2, user.getPhoneNumber());
stmt.setString(3, user.getEmail());
stmt.setString(4, user.getPassword());
stmt.setLong(5, user.getID());
stmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
1 change: 1 addition & 0 deletions src/main/java/persistence/dao/UserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public interface UserRepository {
boolean existsByEmail(String email);
boolean existsByUsername(String username);
void insert(User user);
void updateUser(User user);
}
61 changes: 59 additions & 2 deletions src/main/java/ui/controller/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import persistence.dao.UserDao;
Expand Down Expand Up @@ -92,7 +93,14 @@ public void loadPage(Page page) throws IOException {
try { loadDonationPage(org); }
catch (IOException e) { throw new RuntimeException(e); }
});
c.setOnShowDetail(org -> showOrganizationDetail(org));
c.setOnShowDetail((org, heartBtn) -> showOrganizationDetail(org, heartBtn));
c.setOnSignInRequired(() -> showSignInRequired());
}
if (controller instanceof MyProfileController c) {
c.setOnDonate(org -> {
try { loadDonationPage(org); }
catch (IOException e) { throw new RuntimeException(e); }
});
}

mainPane.setCenter(root);
Expand All @@ -114,8 +122,9 @@ public void loadDonationPage(Organization org) throws IOException {
* Shows the organization detail popup for the given organization.
* Fetches description and logo from Innsamlingskontrollen and displays them.
* @param org the organization to show details for
* @param btn the heart button on the explore card, updated when favourite status changes
*/
public void showOrganizationDetail(Organization org) {
public void showOrganizationDetail(Organization org, Button btn) {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/view/OrganizationDetail.fxml"));
Expand All @@ -141,6 +150,54 @@ public void showOrganizationDetail(Organization org) {
throw new RuntimeException(e);
}
});
controller.setOnSignIn(() -> {
overlay.setVisible(false);
overlay.setManaged(false);
overlay.getChildren().clear();
try { loadPage(Page.SIGN_IN); }
catch (IOException e) { throw new RuntimeException(e); }
});
controller.setOnFavoriteChanged(isFav -> {
btn.setText(isFav ? "♥" : "♡");
});

overlay.getChildren().clear();
overlay.getChildren().add(root);
overlay.setVisible(true);
overlay.setManaged(true);
overlay.setStyle("-fx-background-color: rgba(0,0,0,0.5);");
overlay.setMaxWidth(Double.MAX_VALUE);
overlay.setMaxHeight(Double.MAX_VALUE);
overlay.setAlignment(javafx.geometry.Pos.CENTER);

} catch (IOException e) {
throw new RuntimeException(e);
}
}

/**
* Shows the sign in required popup.
* Displayed when a non-authenticated user tries to save an organization.
*/
public void showSignInRequired() {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/view/SignInRequired.fxml"));
Parent root = loader.load();

SignInRequiredController controller = loader.getController();
controller.setOnClose(() -> {
overlay.setVisible(false);
overlay.setManaged(false);
overlay.getChildren().clear();
});
controller.setOnSignIn(() -> {
overlay.setVisible(false);
overlay.setManaged(false);
overlay.getChildren().clear();
try { loadPage(Page.SIGN_IN); }
catch (IOException e) { throw new RuntimeException(e); }
});

overlay.getChildren().clear();
overlay.getChildren().add(root);
Expand Down
Loading