From 3265666720163adc43434da8c489353c0580e28b Mon Sep 17 00:00:00 2001 From: = Date: Tue, 19 May 2026 11:24:47 +0200 Subject: [PATCH 1/4] Feat: Integrated view with top bar Made it possible to access transactions view using the top bar navigation. Also changes "Quit" to "Back" (like the market view) --- .../ntnu/idi/idatt2003/g40/mappe/Main.java | 8 ++++- .../view/widgets/topbar/TopBarActions.java | 3 +- .../view/widgets/topbar/TopBarController.java | 32 +++++++++++++++---- .../mappe/view/widgets/topbar/TopBarView.java | 11 +++++-- .../transactions/TransactionsActions.java | 4 +++ .../transactions/TransactionsController.java | 20 ++++++++++++ .../transactions/TransactionsView.java | 21 ++++++++++++ 7 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java create mode 100644 src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java create mode 100644 src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java index 7b1c456..3c02553 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java @@ -27,6 +27,8 @@ import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.market.MarketView; import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.topbar.TopBarController; import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.topbar.TopBarView; +import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions.TransactionsController; +import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions.TransactionsView; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.Pane; @@ -116,6 +118,9 @@ public void start(final Stage stage) throws Exception { exchange, stocksInFile); + TransactionsView transactionsView = new TransactionsView(); + new TransactionsController(transactionsView, eventManager); + // In-game (Change "topBarView" to "topBarView2" if no summary section). // Dashboard er default center-view. InGameView inGameView = new InGameView(topBarView, dashBoardView.getRootPane()); @@ -124,7 +129,8 @@ public void start(final Stage stage) throws Exception { topBarController.setMarketIntegration( inGameView::changeCenterView, dashBoardView.getRootPane(), - marketView.getRootPane() + marketView.getRootPane(), + transactionsView.getRootPane() ); // Register all views diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarActions.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarActions.java index 08f84fb..9b71268 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarActions.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarActions.java @@ -4,5 +4,6 @@ public enum TopBarActions { EXIT, STATS, MARKET, - SETTINGS; + SETTINGS, + TRANSACTIONS; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java index c8772f0..e19e0ab 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java @@ -17,6 +17,14 @@ public class TopBarController extends ViewController { * */ private boolean inMarketView = false; + /** + * Whether the transactions screen is currently the active center-view. + * + *

When true, the quit/back button returns to the dashboard instead + * of exiting to the main menu.

+ * */ + private boolean inTransactionsView = false; + /** * {@inheritDoc}. */ @@ -50,19 +58,22 @@ protected void initInteractions() { * otherwise to the main menu. * * - * @param centerSwitcher callback that swaps the center-view (typically - * {@code inGameView::changeCenterView}). - * @param dashboardCenter root pane of the dashboard widget. - * @param marketCenter root pane of the market widget. + * @param centerSwitcher callback that swaps the center-view (typically + * {@code inGameView::changeCenterView}). + * @param dashboardCenter root pane of the dashboard widget. + * @param marketCenter root pane of the market widget. + * @param transactionsCenter root pane of the transactions widget. * */ public void setMarketIntegration(final Consumer centerSwitcher, final Node dashboardCenter, - final Node marketCenter) { + final Node marketCenter, + final Node transactionsCenter) { getViewElement().setOnAction(TopBarActions.EXIT, () -> { - if (inMarketView) { + if (inMarketView || inTransactionsView) { centerSwitcher.accept(dashboardCenter); getViewElement().setQuitText("Quit"); inMarketView = false; + inTransactionsView = false; } else { changeScene(ViewEnum.MAIN_MENU); } @@ -72,12 +83,21 @@ public void setMarketIntegration(final Consumer centerSwitcher, centerSwitcher.accept(dashboardCenter); getViewElement().setQuitText("Quit"); inMarketView = false; + inTransactionsView = false; }); getViewElement().setOnAction(TopBarActions.MARKET, () -> { centerSwitcher.accept(marketCenter); getViewElement().setQuitText("Back"); inMarketView = true; + inTransactionsView = false; + }); + + getViewElement().setOnAction(TopBarActions.TRANSACTIONS, () -> { + centerSwitcher.accept(transactionsCenter); + getViewElement().setQuitText("Back"); + inMarketView = false; + inTransactionsView = true; }); } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarView.java index cdb8727..30f0259 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarView.java @@ -16,8 +16,10 @@ public class TopBarView extends ViewElement { private Button statsBtn; private Button marketBtn; private Button settingsBtn; + private Button transactionsBtn; private SummaryView summaryView; + public TopBarView(final SummaryView summaryView) { this.summaryView = summaryView; super(new VBox(10), TopBarActions.class); @@ -45,8 +47,9 @@ protected void initLayout() { statsBtn = new Button("Stats"); marketBtn = new Button("Market"); settingsBtn = new Button("Settings"); + transactionsBtn = new Button("Transactions"); - Stream.of(quitBtn, statsBtn, marketBtn, settingsBtn).forEach(b -> { + Stream.of(quitBtn, statsBtn, marketBtn, settingsBtn, transactionsBtn).forEach(b -> { HBox.setHgrow(b, Priority.ALWAYS); }); @@ -54,7 +57,8 @@ protected void initLayout() { quitBtn, statsBtn, marketBtn, - settingsBtn + settingsBtn, + transactionsBtn ); if (summaryView != null) { @@ -66,12 +70,13 @@ protected void initLayout() { registerButton(TopBarActions.STATS, statsBtn); registerButton(TopBarActions.MARKET, marketBtn); registerButton(TopBarActions.SETTINGS, settingsBtn); + registerButton(TopBarActions.TRANSACTIONS, transactionsBtn); } @Override protected void initStyling() { getRootPane().getStyleClass().add("top-bar"); - Stream.of(quitBtn, statsBtn, marketBtn, settingsBtn) + Stream.of(quitBtn, statsBtn, marketBtn, settingsBtn, transactionsBtn) .forEach(b -> b.getStyleClass().add("menu-button")); } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java new file mode 100644 index 0000000..bbaad49 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java @@ -0,0 +1,4 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; + +public enum TransactionsActions { +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java new file mode 100644 index 0000000..63bc088 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java @@ -0,0 +1,20 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; + +import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; +import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController; + +public class TransactionsController extends ViewController { + + /** + * {@inheritDoc} + */ + public TransactionsController(final TransactionsView viewElement, final EventManager eventManager) + throws IllegalArgumentException { + super(viewElement, eventManager); + } + + @Override + protected void initInteractions() { + + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java new file mode 100644 index 0000000..5f60974 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java @@ -0,0 +1,21 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; + +import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewElement; +import javafx.scene.layout.VBox; + +public class TransactionsView extends ViewElement { + + public TransactionsView() { + super(new VBox(), TransactionsActions.class); + } + + @Override + protected void initLayout() { + + } + + @Override + protected void initStyling() { + + } +} From a860e36f6f373752dde8624ec82f7bfd4b133e19 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 19 May 2026 14:10:52 +0200 Subject: [PATCH 2/4] Feat: Added transaction view with basic search and filter --- .../ntnu/idi/idatt2003/g40/mappe/Main.java | 5 +- .../transactions/TransactionsController.java | 59 +++++- .../transactions/TransactionsView.java | 173 +++++++++++++++++- src/main/resources/styles.css | 77 ++++++++ 4 files changed, 304 insertions(+), 10 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java index 3c02553..4ecf25b 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java @@ -119,7 +119,10 @@ public void start(final Stage stage) throws Exception { stocksInFile); TransactionsView transactionsView = new TransactionsView(); - new TransactionsController(transactionsView, eventManager); + new TransactionsController( + transactionsView, + eventManager, + player.getTransactionArchive()); // In-game (Change "topBarView" to "topBarView2" if no summary section). // Dashboard er default center-view. diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java index 63bc088..89c30fc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java @@ -1,20 +1,67 @@ package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; +import edu.ntnu.idi.idatt2003.g40.mappe.engine.TransactionArchive; +import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale; import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController; +import javafx.scene.control.TextFormatter; public class TransactionsController extends ViewController { - /** - * {@inheritDoc} - */ - public TransactionsController(final TransactionsView viewElement, final EventManager eventManager) - throws IllegalArgumentException { + private final TransactionArchive transactionArchive; + public TransactionsController(final TransactionsView viewElement, + final EventManager eventManager, + final TransactionArchive transactionArchive) { + this.transactionArchive = transactionArchive; super(viewElement, eventManager); + getViewElement().clearCards(); } @Override protected void initInteractions() { + getViewElement().getWeekField().setTextFormatter(new TextFormatter<>(change -> { + if (change.getControlNewText().matches("([0-9]*(\\.[0-9]*)?)?")) { + return change; + } + return null; + })); + + getViewElement().getWeekField().focusedProperty().addListener((observable, wasFocused, isNowFocused) -> { + if (!isNowFocused && getViewElement().getWeekField().getText().trim().isEmpty()) { + getViewElement().getWeekField().setText("1"); + } + filterData(getViewElement().getSearchField().getText(), Integer.parseInt(getViewElement().getWeekField().getText())); + }); + + getViewElement().getSearchField().textProperty().addListener((observable, oldValue, newValue) -> { + filterData(newValue, Integer.parseInt(getViewElement().getWeekField().getText())); + }); + } + + private void filterData(final String searchKeyword, final Integer weekTarget) { + getViewElement().clearCards(); + transactionArchive.getTransactions(weekTarget).forEach(t -> { + if (t.getShare().getStock().getCompany().toLowerCase().contains(searchKeyword.toLowerCase()) + || t.getShare().getStock().getSymbol().toLowerCase().contains(searchKeyword.toLowerCase())) { + String transactionType = "Purchase"; + String sharePrefix = "+ "; + String moneyPrefix = "- "; + + if(t.getClass().equals(Sale.class)){ + transactionType = "Sale"; + sharePrefix = "- "; + moneyPrefix = "+ "; + } + + getViewElement().addTransactionCard( + transactionType, + t.getShare().getStock().getSymbol(), + t.getShare().getStock().getCompany(), + sharePrefix + t.getShare().getQuantity().floatValue() + " shares", + moneyPrefix + t.getCalculator().calculateTotal().toString() + " NOK", + weekTarget.toString()); + } + }); } -} +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java index 5f60974..6d9f77a 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java @@ -1,21 +1,188 @@ package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewElement; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -public class TransactionsView extends ViewElement { +/** + * View for displaying a history of all transactions for the active player. + * + *

Extends {@link ViewElement}

+ * */ +public final class TransactionsView + extends ViewElement { + /** + * Search field. + * */ + private TextField searchField; + + /** + * Week input field. + * */ + private TextField weekField; + + /** + * Container element for cards. + * */ + private HBox cardsContainer; + + /** + * Entire search bar element. + * */ + private HBox searchBar; + + /** + * Search label text. + * */ + private Label searchLabel; + + /** + * Week label text. + * */ + private Label weekLabel; + + /** + * Scroll pane for cards. + * */ + private ScrollPane scrollPane; + + /** + * Constructor. + * + * @see ViewElement + * */ public TransactionsView() { super(new VBox(), TransactionsActions.class); } @Override protected void initLayout() { + searchBar = new HBox(); + searchLabel = new Label("Search:"); + searchField = new TextField(); + + HBox.setHgrow(searchField, Priority.ALWAYS); + weekLabel = new Label("Week:"); + weekField = new TextField("2"); + + searchBar.getChildren().addAll( + searchLabel, + searchField, + weekLabel, + weekField + ); + + // Horizontal scroll pane: + cardsContainer = new HBox(); + + scrollPane = new ScrollPane(cardsContainer); + scrollPane.setPannable(true); + + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + + getRootPane().getChildren().addAll(searchBar, scrollPane); + } + + /** + * Method to add cards dynamically based on model data. + * + * @param type the type of transaction (PURCHASE/SALE). + * @param symbol the symbol of the stock this transaction was for. + * @param companyName name of the company behind the stock of this + * transaction. + * @param shares the amount of shares gained or lost during this + * transaction. + * @param nok the amount of money in NOK gained or lost during + * the transactions. + * @param week the week this transaction took place. + */ + public void addTransactionCard(final String type, + final String symbol, + final String companyName, + final String shares, + final String nok, + final String week) { + VBox card = new VBox(); + + // Header (SALE / PURCHASE) + Label typeLabel = new Label(type.toUpperCase()); + + // Company Details + Label companyLabel = new Label(symbol + ", " + companyName); + + // Info Rows (+- shares, +- NOK) + VBox infoBox = new VBox(); + + Label textRow1 = new Label(shares); + Label textRow2 = new Label(nok); + + infoBox.getChildren().addAll(textRow1, textRow2); + + Region spacer = new Region(); + VBox.setVgrow(spacer, Priority.ALWAYS); + + // Week Label + Label cardWeekLabel = new Label("Week " + week); + + card.getChildren().addAll( + typeLabel, + companyLabel, + infoBox, + spacer, + cardWeekLabel + ); + cardsContainer.getChildren().add(card); + + card.getStyleClass().add("transactions-transactionCard"); + typeLabel.getStyleClass().add("transactions-typeLabel"); + infoBox.getStyleClass().add("transactions-infoBox"); + companyLabel.getStyleClass().add("transactions-cardText"); + textRow1.getStyleClass().add("transactions-cardText"); + textRow2.getStyleClass().add("transactions-cardText"); + cardWeekLabel.getStyleClass().add("transactions-cardText"); + } + + /** + * Clears the displayed list of cards. + * */ + public void clearCards() { + cardsContainer.getChildren().clear(); + } + + /** + * Getter method for the search field. + * + * @return Search field. + * */ + public TextField getSearchField() { + return searchField; + } + + /** + * Getter method for the week filter field. + * + * @return week filter field. + * */ + public TextField getWeekField() { + return weekField; } @Override protected void initStyling() { - + getRootPane().getStyleClass().add("transactions-root"); + searchBar.getStyleClass().add("transactions-searchBar"); + searchLabel.getStyleClass().add("transactions-searchLabel"); + searchField.getStyleClass().add("transactions-searchField"); + weekLabel.getStyleClass().add("transactions-searchLabel"); + weekField.getStyleClass().add("transactions-weekField"); + scrollPane.getStyleClass().add("transactions-scrollPane"); + cardsContainer.getStyleClass().add("transactions-cardsContainer"); } -} +} \ No newline at end of file diff --git a/src/main/resources/styles.css b/src/main/resources/styles.css index a72733f..a6c00d5 100644 --- a/src/main/resources/styles.css +++ b/src/main/resources/styles.css @@ -1,5 +1,6 @@ .root { -fx-font-family: "Aptos"; + -fx-text-fill: black; } /* Container for the buttons */ @@ -563,3 +564,79 @@ -fx-text-fill: #555555; -fx-padding: 40; } + +/* ---------------- TRANSACTIONS VIEW ---------------- */ +.transactions-root { + -fx-spacing: 20; + -fx-padding: 15; + -fx-alignment: TOP_CENTER; +} + +.transactions-root .label { + -fx-text-fill: black; +} + +.transactions-searchBar { + -fx-spacing: 10; + -fx-alignment: CENTER_LEFT; + -fx-border-color: #000000; + -fx-border-width: 1.5; + -fx-background-color: #E0E0E0; + -fx-padding: 10; +} + +.transactions-searchLabel { + -fx-font-size: 14; +} + +.transactions-searchField { + -fx-background-color: white; + -fx-border-color: #CCCCCC; +} + +.transactions-weekField { + -fx-pref-width: 50; + -fx-alignment: CENTER; + -fx-background-color: white; + -fx-border-color: #000000; + -fx-border-width: 1.5; +} + +.transactions-transactionCard { + -fx-spacing: 15; + -fx-padding: 25; + -fx-alignment: TOP_CENTER; + -fx-pref-width: 260; + -fx-pref-height: 320; + -fx-background-color: #D6D6D6; + -fx-background-radius: 35; + -fx-border-color: #000000; + -fx-border-radius: 35; + -fx-border-width: 1.5; +} + +.transactions-typeLabel { + -fx-font-size: 28; + -fx-font-weight: bold; +} + +.transactions-infoBox { + -fx-spacing: 8; + -fx-alignment: CENTER; +} + +.transactions-cardText { + -fx-font-size: 14; +} + +.transactions-cardsContainer { + -fx-spacing: 25; + -fx-alignment: CENTER_LEFT; + -fx-padding: 10; +} + +.transactions-scrollPane { + -fx-fit-to-height: true; + -fx-background: transparent; + -fx-background-color: transparent; +} \ No newline at end of file From 41f1125a3f68d66518d4a7a8f1985227c831c393 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 19 May 2026 14:11:24 +0200 Subject: [PATCH 3/4] Fix: Fixed visual bug regarding precision error when calculating owned shares --- .../widgets/dashboard/DashBoardController.java | 12 ++++++++++-- .../view/widgets/dashboard/DashBoardView.java | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java index d6334f0..64280dc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java @@ -129,7 +129,8 @@ protected void initInteractions() { getViewElement().setCurrentStock(stockList.getFirst(), 0); getViewElement().updateGraph(selectedTimeRange); getViewElement().setOnAction(DashBoardActions.BUY_SHARES, () -> { - if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText())) { + if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText()) + && Float.parseFloat(getViewElement().getQuantityInputField().getText()) > 0) { BigDecimal amountToBuy = new BigDecimal(getViewElement().getQuantityInputField().getText()); Transaction purchase = exchange.buy( getViewElement().getCurrentStock().getSymbol(), @@ -143,7 +144,8 @@ protected void initInteractions() { }); getViewElement().setOnAction(DashBoardActions.SELL_SHARES, () -> { - if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText())) { + if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText()) + && Float.parseFloat(getViewElement().getQuantityInputField().getText()) > 0) { List transactions = exchange.sell( new BigDecimal(getViewElement().getQuantityInputField().getText()), getViewElement().getCurrentStock().getSymbol(), @@ -190,6 +192,12 @@ protected void initInteractions() { return null; })); + getViewElement().getQuantityInputField().focusedProperty().addListener((observable, wasFocused, isNowFocused) -> { + if (!isNowFocused && getViewElement().getQuantityInputField().getText().trim().isEmpty()) { + getViewElement().getQuantityInputField().setText("1.0"); + } + }); + getViewElement().getSideBarSearchField().textProperty().addListener((observable, o, n) -> { selectedFilter = n; populateStockList(selectedFilter); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardView.java index c1d53a6..5c2a711 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardView.java @@ -382,7 +382,7 @@ public void setCurrentStock(final Stock stock, final float amountOwned) { selectedStockLabel.setText(stock.getSymbol()); stockFullNameLabel.setText(stock.getCompany()); ownedStocks = amountOwned; - ownedQuantityLabel.setText("Owned: " + ownedStocks); + ownedQuantityLabel.setText("Owned: " + formatShares(ownedStocks)); } /** @@ -392,7 +392,7 @@ public void setCurrentStock(final Stock stock, final float amountOwned) { * */ public void addOwnedShares(final float val) { ownedStocks += val; - ownedQuantityLabel.setText("Owned: " + ownedStocks); + ownedQuantityLabel.setText("Owned: " + formatShares(ownedStocks)); } /** @@ -553,4 +553,14 @@ public TextField getQuantityInputField() { public ComboBox getTimeRangeSelector() { return timeRangeSelector; } + + /** + * Helper method for formatting owned shares (avoid precision showing in UI). + * + * @param amount the amount of shares to show. + * */ + private String formatShares(final float amount) { + String formatted = String.format(java.util.Locale.US, "%.3f", amount); + return formatted.replaceAll("0+$", "").replaceAll("\\.$", ""); + } } From 2700002df0930bffed8b6a8091ea5ddf118f15df Mon Sep 17 00:00:00 2001 From: = Date: Tue, 19 May 2026 15:09:48 +0200 Subject: [PATCH 4/4] Feat: Refreshing, combobox Implemented a refresh method that is called when changing to the transactions view. This method ensures that the view is updated automatically. Also changed the week selection text field to a combobox that only gives a selection range of weeks where transactions have taken place. --- .../ntnu/idi/idatt2003/g40/mappe/Main.java | 5 +- .../g40/mappe/engine/TransactionArchive.java | 9 +++ .../idatt2003/g40/mappe/view/ViewManager.java | 2 - .../view/widgets/topbar/TopBarController.java | 5 +- .../transactions/TransactionsActions.java | 4 ++ .../transactions/TransactionsController.java | 56 +++++++++++++++---- .../transactions/TransactionsView.java | 36 +++++++++--- src/main/resources/styles.css | 13 ++++- 8 files changed, 101 insertions(+), 29 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java index 4ecf25b..d86e8d9 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java @@ -119,7 +119,7 @@ public void start(final Stage stage) throws Exception { stocksInFile); TransactionsView transactionsView = new TransactionsView(); - new TransactionsController( + TransactionsController transactionsController = new TransactionsController( transactionsView, eventManager, player.getTransactionArchive()); @@ -133,7 +133,8 @@ public void start(final Stage stage) throws Exception { inGameView::changeCenterView, dashBoardView.getRootPane(), marketView.getRootPane(), - transactionsView.getRootPane() + transactionsView.getRootPane(), + transactionsController::refresh ); // Register all views diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java index 20ca02b..dadc496 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java @@ -59,6 +59,15 @@ public List getTransactions(final int week) { return result; } + /** + * Returns all transactions. + * + * @return list of transactions from the given week + */ + public List getTransactions() { + return transactions; + } + /** * Returns all purchase transactions from a given week. * diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java index 51eb63f..b3ce064 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java @@ -4,9 +4,7 @@ import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventSubscriber; import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType; - import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; import javafx.scene.Scene; import javafx.stage.Stage; diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java index e19e0ab..9909b1d 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/topbar/TopBarController.java @@ -62,12 +62,12 @@ protected void initInteractions() { * {@code inGameView::changeCenterView}). * @param dashboardCenter root pane of the dashboard widget. * @param marketCenter root pane of the market widget. - * @param transactionsCenter root pane of the transactions widget. + * @param transactionsCenter root pane of the transactions' widget. * */ public void setMarketIntegration(final Consumer centerSwitcher, final Node dashboardCenter, final Node marketCenter, - final Node transactionsCenter) { + final Node transactionsCenter, final Runnable onTransactionUpdate) { getViewElement().setOnAction(TopBarActions.EXIT, () -> { if (inMarketView || inTransactionsView) { centerSwitcher.accept(dashboardCenter); @@ -95,6 +95,7 @@ public void setMarketIntegration(final Consumer centerSwitcher, getViewElement().setOnAction(TopBarActions.TRANSACTIONS, () -> { centerSwitcher.accept(transactionsCenter); + onTransactionUpdate.run(); getViewElement().setQuitText("Back"); inMarketView = false; inTransactionsView = true; diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java index bbaad49..d278745 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsActions.java @@ -1,4 +1,8 @@ package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; +/** + * Enum representing actions to be done in a {@link TransactionsView} element. + * */ public enum TransactionsActions { + // Empty, no interactable buttons in transactions page. } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java index 89c30fc..3069e8c 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsController.java @@ -2,10 +2,16 @@ import edu.ntnu.idi.idatt2003.g40.mappe.engine.TransactionArchive; import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale; +import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction; import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController; -import javafx.scene.control.TextFormatter; +import java.util.List; +/** + * Controller for a {@link TransactionsView} element. + * + *

extends {@link ViewController}

+ * */ public class TransactionsController extends ViewController { private final TransactionArchive transactionArchive; @@ -20,25 +26,27 @@ public TransactionsController(final TransactionsView viewElement, @Override protected void initInteractions() { - getViewElement().getWeekField().setTextFormatter(new TextFormatter<>(change -> { - if (change.getControlNewText().matches("([0-9]*(\\.[0-9]*)?)?")) { - return change; - } - return null; - })); - getViewElement().getWeekField().focusedProperty().addListener((observable, wasFocused, isNowFocused) -> { - if (!isNowFocused && getViewElement().getWeekField().getText().trim().isEmpty()) { - getViewElement().getWeekField().setText("1"); + getViewElement().getWeekSelectBox().setOnAction(event -> { + try { + Integer selectedWeek = Integer.parseInt(getViewElement().getWeekSelectBox().getValue()); + filterData(getViewElement().getSearchField().getText(), selectedWeek); + } catch (NumberFormatException _) { + filterData(getViewElement().getSearchField().getText(), 1); } - filterData(getViewElement().getSearchField().getText(), Integer.parseInt(getViewElement().getWeekField().getText())); }); getViewElement().getSearchField().textProperty().addListener((observable, oldValue, newValue) -> { - filterData(newValue, Integer.parseInt(getViewElement().getWeekField().getText())); + filterData(newValue, Integer.parseInt(getViewElement().getWeekSelectBox().getValue())); }); } + /** + * Method used for updating the list of transactions based on a search and target week. + * + * @param searchKeyword the keyword to search for stocks with. + * @param weekTarget the week used when searching for transactions. + * */ private void filterData(final String searchKeyword, final Integer weekTarget) { getViewElement().clearCards(); transactionArchive.getTransactions(weekTarget).forEach(t -> { @@ -64,4 +72,28 @@ private void filterData(final String searchKeyword, final Integer weekTarget) { } }); } + + private List getUniqueTransactionWeeks(final List transactionList) { + return transactionList.stream() + .map(Transaction::getWeek) + .distinct() + .sorted() + .toList(); + } + + public void refresh() { + List activeWeeks = getUniqueTransactionWeeks(transactionArchive.getTransactions()); + getViewElement().setWeekSelectBoxOptions(activeWeeks); + + if (activeWeeks.contains(Integer.parseInt(getViewElement().getWeekSelectBox().getValue()))) { + getViewElement().getWeekSelectBox().setValue(getViewElement().getWeekSelectBox().getValue()); + } else if (!activeWeeks.isEmpty()) { + getViewElement().getWeekSelectBox().setValue(activeWeeks.getFirst().toString()); + } + + filterData( + getViewElement().getSearchField().getText(), + Integer.parseInt(getViewElement().getWeekSelectBox().getValue()) + ); + } } \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java index 6d9f77a..632b8b0 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/transactions/TransactionsView.java @@ -1,6 +1,7 @@ package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewElement; +import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; @@ -8,6 +9,7 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; +import java.util.List; /** * View for displaying a history of all transactions for the active player. @@ -25,7 +27,7 @@ public final class TransactionsView /** * Week input field. * */ - private TextField weekField; + private ComboBox weekSelectBox; /** * Container element for cards. @@ -70,13 +72,14 @@ protected void initLayout() { HBox.setHgrow(searchField, Priority.ALWAYS); weekLabel = new Label("Week:"); - weekField = new TextField("2"); + weekSelectBox = new ComboBox<>(); + weekSelectBox.setPromptText("All"); searchBar.getChildren().addAll( searchLabel, searchField, weekLabel, - weekField + weekSelectBox ); // Horizontal scroll pane: @@ -149,6 +152,23 @@ public void addTransactionCard(final String type, cardWeekLabel.getStyleClass().add("transactions-cardText"); } + /** + * Helper method for setting the options for the week selection combo box. + * + * @param options a list of integers representing the different options. + * */ + public void setWeekSelectBoxOptions(final List options) { + weekSelectBox.getItems().clear(); + options.forEach(i -> weekSelectBox.getItems().add(i.toString())); + + if (!options.isEmpty()) { + weekSelectBox.setValue(options.getFirst().toString()); + } else { + weekSelectBox.getItems().add("1"); + weekSelectBox.setValue("1"); + } + } + /** * Clears the displayed list of cards. * */ @@ -166,12 +186,12 @@ public TextField getSearchField() { } /** - * Getter method for the week filter field. + * Getter method for the week selection box. * - * @return week filter field. + * @return week selection box. * */ - public TextField getWeekField() { - return weekField; + public ComboBox getWeekSelectBox() { + return weekSelectBox; } @Override @@ -181,7 +201,7 @@ protected void initStyling() { searchLabel.getStyleClass().add("transactions-searchLabel"); searchField.getStyleClass().add("transactions-searchField"); weekLabel.getStyleClass().add("transactions-searchLabel"); - weekField.getStyleClass().add("transactions-weekField"); + weekSelectBox.getStyleClass().add("transactions-weekField"); scrollPane.getStyleClass().add("transactions-scrollPane"); cardsContainer.getStyleClass().add("transactions-cardsContainer"); } diff --git a/src/main/resources/styles.css b/src/main/resources/styles.css index a6c00d5..05ec5ed 100644 --- a/src/main/resources/styles.css +++ b/src/main/resources/styles.css @@ -595,11 +595,18 @@ } .transactions-weekField { - -fx-pref-width: 50; - -fx-alignment: CENTER; + -fx-pref-width: 75px; + -fx-min-width: 75px; -fx-background-color: white; -fx-border-color: #000000; - -fx-border-width: 1.5; + -fx-border-width: 1.5px; + -fx-alignment: CENTER; +} + +.transactions-weekField .list-cell { + -fx-text-fill: black; + -fx-font-weight: bold; + -fx-alignment: center; } .transactions-transactionCard {