From 31e578cd50e7261c0ba8e05175d8b7415019460a Mon Sep 17 00:00:00 2001 From: Marius Klepp Date: Tue, 21 Apr 2026 13:14:47 +0200 Subject: [PATCH 01/12] feat: add favorite organizations functionality --- .../java/application/user/UserFavorite.java | 61 +++++++++ .../java/persistence/dao/FavoriteDao.java | 114 ++++++++++++++++ .../java/ui/controller/MainController.java | 61 ++++++++- .../ui/controller/MyProfileController.java | 129 +++++++++++++++++- .../ui/controller/OrganizationController.java | 58 +++++++- .../OrganizationDetailController.java | 41 +++++- src/main/resources/css/MyProfile.css | 28 ++++ src/main/resources/css/OrganizationView.css | 35 ++++- src/main/resources/view/MyProfile.fxml | 114 ++++++++++++++-- .../resources/view/OrganizationDetail.fxml | 1 + 10 files changed, 612 insertions(+), 30 deletions(-) create mode 100644 src/main/java/application/user/UserFavorite.java create mode 100644 src/main/java/persistence/dao/FavoriteDao.java diff --git a/src/main/java/application/user/UserFavorite.java b/src/main/java/application/user/UserFavorite.java new file mode 100644 index 0000000..23f141f --- /dev/null +++ b/src/main/java/application/user/UserFavorite.java @@ -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 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); + } +} diff --git a/src/main/java/persistence/dao/FavoriteDao.java b/src/main/java/persistence/dao/FavoriteDao.java new file mode 100644 index 0000000..2e262e2 --- /dev/null +++ b/src/main/java/persistence/dao/FavoriteDao.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/ui/controller/MainController.java b/src/main/java/ui/controller/MainController.java index c586dbf..6e7295b 100644 --- a/src/main/java/ui/controller/MainController.java +++ b/src/main/java/ui/controller/MainController.java @@ -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; @@ -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); @@ -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")); @@ -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); diff --git a/src/main/java/ui/controller/MyProfileController.java b/src/main/java/ui/controller/MyProfileController.java index b8dd62c..1915ab9 100644 --- a/src/main/java/ui/controller/MyProfileController.java +++ b/src/main/java/ui/controller/MyProfileController.java @@ -1,17 +1,33 @@ package ui.controller; +import application.user.UserEditProfile; +import application.user.UserFavorite; import application.user.UserStatistics; import domain.donation.Donation; +import domain.organization.Organization; import domain.user.User; import java.util.List; + +import integration.security.Sha256PasswordHasher; +import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.ScrollPane; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; import persistence.dao.DonationDao; +import persistence.dao.FavoriteDao; +import persistence.dao.UserDao; import ui.Page; import util.SessionManager; @@ -22,18 +38,31 @@ * Displays user information, donation statistics and donation history. */ public class MyProfileController implements NavigationAware { - + private final UserFavorite userFavorite = new UserFavorite(new FavoriteDao()); private Consumer onNavigate; + private Consumer onDonate; @FXML private Label totalDonatedLabel; @FXML private Label numberOfDonationsLabel; @FXML private Label favoriteOrgLabel; @FXML private Label nameLabel; @FXML private Label emailLabel; @FXML private Label phoneLabel; + @FXML private VBox myProfile; @FXML private TableView donationTable; @FXML private TableColumn orgColumn; @FXML private TableColumn amountColumn; @FXML private TableColumn dateColumn; + @FXML private FlowPane savedOrgsPane; + @FXML private VBox editProfilePane; + @FXML private TextField editNameField; + @FXML private TextField editEmailField; + @FXML private TextField editPhoneField; + @FXML private PasswordField newPasswordField; + @FXML private PasswordField confirmPasswordField; + private final UserEditProfile userEditProfile = new UserEditProfile(new Sha256PasswordHasher(), new UserDao()); + @FXML private Label errorLabel; + @FXML private ScrollPane scrollPane; + /** * Sets the navigation callback. @@ -43,21 +72,25 @@ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } + public void setOnDonate(Consumer onDonate) { + this.onDonate = onDonate; + } + @FXML public void initialize() { User user = SessionManager.getSignedInUser(); if (user == null) return; - nameLabel.setText("Name: " + user.getUsername()); - emailLabel.setText("E-mail: " + user.getEmail()); - phoneLabel.setText("Phonenumber: " + user.getPhoneNumber()); + nameLabel.setText(user.getUsername()); + emailLabel.setText(user.getEmail()); + phoneLabel.setText(user.getPhoneNumber()); UserStatistics stats = new UserStatistics(); totalDonatedLabel.setText(stats.userTotalDonationAmount(user) + " kr"); numberOfDonationsLabel.setText(stats.getTotalDonationsMade(user)); List favOrg = stats.userFavoriteOrganization(user); - favoriteOrgLabel.setText(favOrg.isEmpty() ? "None" : favOrg.get(0)); + favoriteOrgLabel.setText(favOrg.isEmpty() ? "None" : favOrg.getFirst()); orgColumn.setCellValueFactory(cell -> new SimpleStringProperty(cell.getValue().getOrganization().getName())); @@ -71,6 +104,15 @@ public void initialize() { donationDao.findByUser(user.getID()) ); donationTable.setItems(data); + + List favorites = userFavorite.getFavorites(user.getID()); + if (favorites.isEmpty()) { + savedOrgsPane.getChildren().add(new Label("No saved organizations yet")); + } else { + for (Organization org : favorites) { + savedOrgsPane.getChildren().add(createFavoriteCard(org)); + } + } } @FXML @@ -78,4 +120,81 @@ private void handleSignOut() { SessionManager.signOut(); onNavigate.accept(Page.HOME); } + + private VBox createFavoriteCard(Organization org) { + User user = SessionManager.getSignedInUser(); + + VBox card = new VBox(8); + card.getStyleClass().add("org-card"); + + Label name = new Label(org.getName()); + name.getStyleClass().add("org-name"); + name.setMaxWidth(Double.MAX_VALUE); + + Button removeBtn = new Button("♥"); + removeBtn.getStyleClass().add("remove-button"); + removeBtn.setOnAction(e -> { + userFavorite.removeFavorite(user.getID(), org.getOrgNumber()); + savedOrgsPane.getChildren().remove(card); + }); + + HBox topRow = new HBox(15); + topRow.getChildren().addAll(name, removeBtn); + HBox.setHgrow(name, Priority.ALWAYS); + + Button donateBtn = new Button("Donate"); + donateBtn.getStyleClass().add("donate-button"); + donateBtn.setMaxWidth(Double.MAX_VALUE); + donateBtn.setOnAction(e -> onDonate.accept(org)); + + card.getChildren().addAll(topRow, donateBtn); + return card; + } + + @FXML + public void handleEditProfile() { + User user = SessionManager.getSignedInUser(); + editNameField.setText(user.getUsername()); + editEmailField.setText(user.getEmail()); + editPhoneField.setText(user.getPhoneNumber()); + + myProfile.setVisible(false); + myProfile.setManaged(false); + editProfilePane.setVisible(true); + editProfilePane.setManaged(true); + } + + @FXML + public void handleSaveProfile() { + String newPassword = newPasswordField.getText(); + String confirmPassword = confirmPasswordField.getText(); + + if (!newPassword.isBlank() && !newPassword.equals(confirmPassword)) { + errorLabel.setText("Passwords do not match"); + errorLabel.setVisible(true); + errorLabel.setManaged(true); + return; + } + User user = SessionManager.getSignedInUser(); + userEditProfile.execute( + user, + editNameField.getText(), + editEmailField.getText(), + editPhoneField.getText(), + newPassword + ); + nameLabel.setText(user.getUsername()); + emailLabel.setText(user.getEmail()); + phoneLabel.setText(user.getPhoneNumber()); + handleCancelEdit(); + } + + @FXML + public void handleCancelEdit() { + myProfile.setVisible(true); + myProfile.setManaged(true); + editProfilePane.setVisible(false); + editProfilePane.setManaged(false); + myProfile.requestFocus(); + } } diff --git a/src/main/java/ui/controller/OrganizationController.java b/src/main/java/ui/controller/OrganizationController.java index 00ad56c..c868f90 100644 --- a/src/main/java/ui/controller/OrganizationController.java +++ b/src/main/java/ui/controller/OrganizationController.java @@ -1,19 +1,26 @@ package ui.controller; +import application.user.UserFavorite; import domain.organization.Organization; +import domain.user.User; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import persistence.dao.FavoriteDao; import persistence.dao.OrganizationDao; import ui.Page; +import util.SessionManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -41,7 +48,10 @@ public class OrganizationController implements NavigationAware{ private Consumer onNavigate; private Consumer onDonate; - private Consumer onShowDetail; + private BiConsumer onShowDetail; + private Runnable onSignInRequired; + + private final UserFavorite userFavorite = new UserFavorite(new FavoriteDao()); /** * Sets the navigation callback. @@ -63,10 +73,14 @@ public void setOnDonate(Consumer onDonate) { * Sets the callback to invoke when the user clicks on an organization card. * @param onShowDetail the callback with the selected organization */ - public void setOnShowDetail(Consumer onShowDetail) { + public void setOnShowDetail(BiConsumer onShowDetail) { this.onShowDetail = onShowDetail; } + public void setOnSignInRequired(Runnable onSignInRequired) { + this.onSignInRequired = onSignInRequired; + } + public void initialize() { try { OrganizationDao dao = new OrganizationDao(); @@ -130,22 +144,56 @@ private VBox createCard(Organization org) { name.getStyleClass().add("org-name"); name.setWrapText(true); + Button heartBtn = new Button("♥"); + heartBtn.setVisible(false); + heartBtn.getStyleClass().add("heart-button"); + User user = SessionManager.getSignedInUser(); + boolean isFav = user != null && userFavorite.isFavorite(user.getID(), org.getOrgNumber()); + heartBtn.setOnAction(e -> { + if (user == null) { + if (onSignInRequired != null) onSignInRequired.run(); + return; + } + if (heartBtn.getText().equals("♡")) { + userFavorite.addFavorite(user.getID(), org.getOrgNumber()); + heartBtn.setText("♥"); + } else { + userFavorite.removeFavorite(user.getID(), org.getOrgNumber()); + heartBtn.setText("♡"); + } + }); + heartBtn.setText(isFav ? "♥" : "♡"); + + HBox topRow = new HBox(); + topRow.getChildren().addAll(name, heartBtn); + name.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(name, Priority.ALWAYS); + heartBtn.setMinWidth(30); + + card.setOnMouseEntered(e -> heartBtn.setVisible(true)); + card.setOnMouseExited(e -> heartBtn.setVisible(false)); + Label orgnr = new Label("Org.nr: " + org.getOrgNumber()); orgnr.getStyleClass().add("org-detail"); Label status = new Label(org.isPreApproved() ? "✓ Forhåndsgodkjent" : "✓ Verified"); status.getStyleClass().add("org-status"); + Button detailBtn = new Button("Details"); + detailBtn.setMaxWidth(Double.MAX_VALUE); + detailBtn.setOnAction(e -> onShowDetail.accept(org, heartBtn)); + detailBtn.getStyleClass().add("details-button"); + Button donateBtn = new Button("Donate"); donateBtn.getStyleClass().add("donate-button"); donateBtn.setMaxWidth(Double.MAX_VALUE); donateBtn.setOnAction(e -> onDonate.accept(org)); - - card.setOnMouseClicked(e -> onShowDetail.accept(org)); - card.getChildren().addAll(name, orgnr, status, donateBtn); + card.setOnMouseClicked(e -> onShowDetail.accept(org, heartBtn)); + card.getChildren().addAll(topRow, orgnr, status, detailBtn, donateBtn); return card; } + @FXML private void previousPage() { page--; diff --git a/src/main/java/ui/controller/OrganizationDetailController.java b/src/main/java/ui/controller/OrganizationDetailController.java index b741862..0ab1896 100644 --- a/src/main/java/ui/controller/OrganizationDetailController.java +++ b/src/main/java/ui/controller/OrganizationDetailController.java @@ -1,12 +1,18 @@ package ui.controller; +import application.user.UserFavorite; import domain.organization.Organization; +import domain.user.User; import integration.OrganizationDetails; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import persistence.dao.FavoriteDao; +import util.SessionManager; + import java.awt.Desktop; import java.net.URI; import java.util.function.Consumer; @@ -23,11 +29,15 @@ public class OrganizationDetailController { @FXML private Label descriptionLabel; @FXML private ImageView logoImageView; @FXML private Hyperlink readMoreLink; - + @FXML private Button saveBtn; private Organization organization; + private UserFavorite userFavorite = new UserFavorite(new FavoriteDao()); private Consumer onDonate; private Runnable onClose; + private Runnable onSignIn; + private Consumer onFavoriteChanged; + /** * Sets the organization and its details, and updates all UI elements. @@ -73,6 +83,10 @@ public void setOrganization(Organization org, OrganizationDetails details) { } else { logoImageView.setVisible(false); } + + User user = SessionManager.getSignedInUser(); + boolean isFav = user != null && userFavorite.isFavorite(user.getID(), org.getOrgNumber()); + saveBtn.setText(isFav ? " ♥\nSaved" : " ♡\nSave"); } /** @@ -114,4 +128,29 @@ public void handleVisitWebsite() { public void handleReadMore() { handleVisitWebsite(); } + + @FXML + public void handleSave() { + User user = SessionManager.getSignedInUser(); + if (user == null) { + if (onSignIn != null) onSignIn.run(); + return; + } + if (userFavorite.isFavorite(user.getID(), organization.getOrgNumber())) { + userFavorite.removeFavorite(user.getID(), organization.getOrgNumber()); + saveBtn.setText("♡\nSave"); + if (onFavoriteChanged != null) onFavoriteChanged.accept(false); + } else { + userFavorite.addFavorite(user.getID(), organization.getOrgNumber()); + saveBtn.setText("♥\nSaved"); + if (onFavoriteChanged != null) onFavoriteChanged.accept(true); + } + } + + public void setOnFavoriteChanged(Consumer onFavoriteChanged) { + this.onFavoriteChanged = onFavoriteChanged; + } + public void setOnSignIn(Runnable onSignIn) { + this.onSignIn = onSignIn; + } } diff --git a/src/main/resources/css/MyProfile.css b/src/main/resources/css/MyProfile.css index a9b04ec..fbed4fc 100644 --- a/src/main/resources/css/MyProfile.css +++ b/src/main/resources/css/MyProfile.css @@ -1,4 +1,32 @@ .profile-label{ -fx-font-size: 16px; +} + +.remove-button { + -fx-background-color: transparent; + -fx-font-size: 16px; + -fx-cursor: hand; + -fx-padding: 0; +} + +.table-view .column-header { + -fx-background-color: #1a2e6b; +} + +.table-view .column-header .label { + -fx-text-fill: white; + -fx-font-weight: bold; +} + +.table-view .table-row-cell:odd { + -fx-background-color: #f5f5f5; +} + +.table-view .table-row-cell:even { + -fx-background-color: white; +} + +.table-view .table-cell { + -fx-padding: 8 16 8 16; } \ No newline at end of file diff --git a/src/main/resources/css/OrganizationView.css b/src/main/resources/css/OrganizationView.css index 18c0a90..24500ca 100644 --- a/src/main/resources/css/OrganizationView.css +++ b/src/main/resources/css/OrganizationView.css @@ -10,12 +10,6 @@ -fx-cursor: hand; } -.org-card:pressed { - -fx-scale-x: 0.98; - -fx-scale-y: 0.98; - -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.05), 4, 0, 0, 2); -} - .org-name { -fx-font-size: 20px; } @@ -114,4 +108,33 @@ -fx-font-size: 13px; -fx-text-fill: #333333; -fx-wrap-text: true; +} + +.details-button { + -fx-background-color: transparent; + -fx-border-color: #1a2e6b; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-border-width: 1; + -fx-text-fill: #1a2e6b; + -fx-cursor: hand; +} + +.details-button:hover { + -fx-background-color: #1a2e6b; + -fx-text-fill: white; + -fx-scale-x: 1.02; + -fx-scale-y: 1.02; +} + +.details-button:pressed { + -fx-scale-x: 0.98; + -fx-scale-y: 0.98; +} + +.heart-button { + -fx-font-size: 24px; + -fx-background-color: transparent; + -fx-cursor: hand; + -fx-padding: 0; } \ No newline at end of file diff --git a/src/main/resources/view/MyProfile.fxml b/src/main/resources/view/MyProfile.fxml index 40ec2de..55f54dc 100644 --- a/src/main/resources/view/MyProfile.fxml +++ b/src/main/resources/view/MyProfile.fxml @@ -4,34 +4,123 @@ + + +
- +