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 fef87da..186a096 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
@@ -178,6 +178,29 @@ public void start(final Stage stage) throws Exception {
gameEngineController, clickerGame, findStockGame, timeInputsGame
);
+ // In-game quit confirmation dialog: replaces the previous immediate
+ // "auto-save and return to main menu" behaviour with a popup that
+ // lets the player choose Continue / Save / Save and quit to main
+ // menu / Save and quit game / Sell portfolio, save and quit.
+ //
+ // Constructed up here (before the STATE_RESET subscriber below) so
+ // the subscriber can rebind it to the freshly loaded player/exchange
+ // alongside the other controllers.
+ QuitDialogView quitDialogView = new QuitDialogView();
+ QuitDialogController quitDialogController =
+ new QuitDialogController(quitDialogView, eventManager, inGameView,
+ gameStateLoader, saveGameService, player, exchange);
+ quitDialogController.setOnExitApplication(() -> {
+ try {
+ javafx.application.Platform.exit();
+ } finally {
+ // Fallback in case there are non-daemon background threads
+ // keeping the JVM alive after JavaFX shuts down.
+ System.exit(0);
+ }
+ });
+ topBarController.setOnQuitRequested(quitDialogController::show);
+
// Adds a generic event subscriber to the event manager
// that handles STATE_RESET events. These events are
// triggered when a new save file is loaded.
@@ -209,6 +232,7 @@ public void handleEvent(final EventData eventData) {
dashBoardController.handleStockPoolUpdate(dynamicStocks);
statsController.handleContextUpdate(Main.this.exchange, Main.this.player);
marketController.handleStockPoolUpdate(Main.this.exchange, Main.this.player, dynamicStocks);
+ quitDialogController.handleContextUpdate(Main.this.exchange, Main.this.player);
if (!dynamicStocks.isEmpty()) {
miniGamesController.setActiveStock(dynamicStocks.getFirst());
@@ -222,25 +246,6 @@ public void handleEvent(final EventData eventData) {
new InGameSettingsController(inGameSettingsView, eventManager, inGameView);
topBarController.setSettingsAction(inGameSettingsController::show);
- // In-game quit confirmation dialog: replaces the previous immediate
- // "auto-save and return to main menu" behaviour with a popup that
- // lets the player choose Continue / Save / Save and quit to main
- // menu / Save and quit game.
- QuitDialogView quitDialogView = new QuitDialogView();
- QuitDialogController quitDialogController =
- new QuitDialogController(quitDialogView, eventManager, inGameView,
- gameStateLoader, saveGameService);
- quitDialogController.setOnExitApplication(() -> {
- try {
- javafx.application.Platform.exit();
- } finally {
- // Fallback in case there are non-daemon background threads
- // keeping the JVM alive after JavaFX shuts down.
- System.exit(0);
- }
- });
- topBarController.setOnQuitRequested(quitDialogController::show);
-
topBarController.setOnQuitToMainMenu(() -> {
System.out.println("[auto-save] Quit triggered, attempting snapshot...");
SaveGame snapshot = gameStateLoader.snapshotActiveSave();
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/ConfigValues.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/ConfigValues.java
index 504e90e..27432c2 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/ConfigValues.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/ConfigValues.java
@@ -7,7 +7,7 @@ public enum ConfigValues {
/**
* Standard viewport width.
*/
- VIEWPORT_WIDTH(800),
+ VIEWPORT_WIDTH(1200),
/**
* Standard viewport height.
@@ -17,7 +17,7 @@ public enum ConfigValues {
/**
* Minimum viewport width. The window cannot be resized smaller than this.
*/
- MIN_VIEWPORT_WIDTH(800),
+ MIN_VIEWPORT_WIDTH(1200),
/**
* Minimum viewport height. The window cannot be resized smaller than this.
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogActions.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogActions.java
index 7de92ca..cc9c7af 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogActions.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogActions.java
@@ -5,8 +5,9 @@
*
* The quit dialog is shown when the player clicks the "Quit" button
* from the in-game dashboard, and lets them choose between continuing,
- * saving in place, saving and returning to the main menu, or saving
- * and exiting the application.
+ * saving in place, saving and returning to the main menu, saving and
+ * exiting the application, or selling the entire portfolio before
+ * saving and returning to the main menu.
* */
public enum QuitDialogActions {
/** Closes the dialog and returns the player to the game. */
@@ -19,5 +20,11 @@ public enum QuitDialogActions {
SAVE_AND_QUIT_TO_MAIN_MENU,
/** Saves the current game state and exits the application. */
- SAVE_AND_QUIT_GAME;
+ SAVE_AND_QUIT_GAME,
+
+ /**
+ * Sells every share the player owns, then saves the game and returns
+ * to the main menu.
+ * */
+ SELL_ALL_AND_QUIT;
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogController.java
index 0d12399..051481d 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogController.java
@@ -1,6 +1,9 @@
package edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.quit;
+import edu.ntnu.idi.idatt2003.g40.mappe.engine.Exchange;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Player;
import edu.ntnu.idi.idatt2003.g40.mappe.model.SaveGame;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
import edu.ntnu.idi.idatt2003.g40.mappe.service.GameStateLoader;
import edu.ntnu.idi.idatt2003.g40.mappe.service.SaveGameService;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
@@ -11,10 +14,14 @@
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewEnum;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.InGameView;
+import java.math.BigDecimal;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
/**
* Controller for {@link QuitDialogView}.
*
- * Wires the four dialog buttons to the appropriate save / scene-change
+ *
Wires the five dialog buttons to the appropriate save / scene-change
* / application-exit actions:
*
* - {@code CONTINUE} - hides the overlay; the player stays in the
@@ -26,6 +33,10 @@
* hides the overlay, and changes the scene back to the main menu.
* - {@code SAVE_AND_QUIT_GAME} - snapshots, writes to disk, then
* invokes the configured exit-application runnable to close the JVM.
+ * - {@code SELL_ALL_AND_QUIT} - liquidates every position in the
+ * player's portfolio (via the same exchange API the dashboard uses),
+ * then snapshots, writes to disk, hides the overlay, and changes the
+ * scene back to the main menu.
*
*
* The save logic mirrors the auto-save hook in {@code Main}: a missing
@@ -45,6 +56,24 @@ public final class QuitDialogController
/** Persists save games to disk. */
private final SaveGameService saveGameService;
+ /**
+ * The {@link Player} whose portfolio is liquidated by the
+ * "Sell portfolio, save and quit" button.
+ *
+ *
Not final because the active player can be swapped out when a
+ * save is loaded; {@link #handleContextUpdate} is the entry point
+ * the host application uses to rebind it.
+ * */
+ private Player player;
+
+ /**
+ * The {@link Exchange} used to execute the sell-all transactions.
+ *
+ * Like {@link #player}, this is reassigned when a save is loaded
+ * via {@link #handleContextUpdate}.
+ * */
+ private Exchange exchange;
+
/** Runnable invoked to close the application. */
private Runnable onExitApplication = () -> { };
@@ -58,6 +87,10 @@ public final class QuitDialogController
* @param inGameView the in-game view that hosts this overlay.
* @param gameStateLoader the loader used to snapshot the active save.
* @param saveGameService the service used to persist saves to disk.
+ * @param player the active {@link Player}, whose portfolio
+ * is liquidated by the sell-all-and-quit flow.
+ * @param exchange the active {@link Exchange}, used to execute
+ * the sell transactions.
*
* @throws IllegalArgumentException if any constructor argument is null.
* */
@@ -65,16 +98,22 @@ public QuitDialogController(final QuitDialogView view,
final EventManager eventManager,
final InGameView inGameView,
final GameStateLoader gameStateLoader,
- final SaveGameService saveGameService)
+ final SaveGameService saveGameService,
+ final Player player,
+ final Exchange exchange)
throws IllegalArgumentException {
this.inGameView = inGameView;
this.gameStateLoader = gameStateLoader;
this.saveGameService = saveGameService;
+ this.player = player;
+ this.exchange = exchange;
super(view, eventManager);
eventManager.addSubscriber(this, EventType.SHOW_QUIT_OPTIONS);
if (inGameView == null
|| gameStateLoader == null
- || saveGameService == null) {
+ || saveGameService == null
+ || player == null
+ || exchange == null) {
throw new IllegalArgumentException(
"Invalid QuitDialogController arguments!");
}
@@ -124,6 +163,68 @@ protected void initInteractions() {
onExitApplication.run();
}
});
+
+ getViewElement().setOnAction(QuitDialogActions.SELL_ALL_AND_QUIT, () -> {
+ sellEntirePortfolio();
+ if (performSave()) {
+ close();
+ changeScene(ViewEnum.MAIN_MENU);
+ }
+ });
+ }
+
+ /**
+ * Sells every share the player currently owns, one ticker at a time.
+ *
+ * Uses the same {@link Exchange#sell(BigDecimal, String, Player)}
+ * entry point as the dashboard's sell button and the stats widget's
+ * sell-all button, so commissions, taxes and net-worth listeners all
+ * fire exactly as if the player had sold each ticker manually.
+ *
+ * If the portfolio is already empty this is a no-op.
+ * */
+ private void sellEntirePortfolio() {
+ // Snapshot first so we don't iterate over a list that mutates
+ // while we sell.
+ Map totalsBySymbol = new LinkedHashMap<>();
+ for (Share s : player.getPortfolio().getShares()) {
+ String symbol = s.getStock().getSymbol();
+ totalsBySymbol.merge(symbol, s.getQuantity(), BigDecimal::add);
+ }
+
+ for (Map.Entry entry : totalsBySymbol.entrySet()) {
+ if (entry.getValue().signum() <= 0) {
+ continue;
+ }
+ try {
+ exchange.sell(entry.getValue(), entry.getKey(), player);
+ } catch (IllegalArgumentException e) {
+ System.err.println("[quit-dialog] Sell-all skipped " + entry.getKey()
+ + ": " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Rebinds this controller to a new {@link Player} / {@link Exchange}
+ * pair when a save is loaded.
+ *
+ * Mirrors the {@code handleContextUpdate} hook on
+ * {@code StatsController} - the host application reassigns its
+ * own player/exchange fields when a {@code STATE_RESET} event fires,
+ * and must call this so the sell-all-and-quit flow operates on the
+ * newly active save instead of the original starting state.
+ *
+ * @param updatedExchange the current active exchange engine.
+ * @param updatedPlayer the current active player context profile.
+ * */
+ public void handleContextUpdate(final Exchange updatedExchange,
+ final Player updatedPlayer) {
+ if (updatedExchange == null || updatedPlayer == null) {
+ return;
+ }
+ this.exchange = updatedExchange;
+ this.player = updatedPlayer;
}
/**
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogView.java
index ba9dce1..e2c759a 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogView.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogView.java
@@ -16,12 +16,13 @@
* top bar.
*
* The root of this view is a dimmer {@link StackPane} that covers the
- * current center widget. Inside, a centered panel hosts four action buttons
+ * current center widget. Inside, a centered panel hosts five action buttons
* that let the player choose how to leave (or stay in) the game.
*
* Layout: a header row with the title and a close (X) button, a short
- * description, and a vertical stack of four buttons: Continue, Save,
- * Save and quit to main menu, Save and quit game.
+ * description, and a vertical stack of five buttons: Continue, Save,
+ * Save and quit to main menu, Save and quit game, Sell portfolio save
+ * and quit.
*
* This view is purely presentational - the {@link QuitDialogController}
* wires the buttons to the actual save/quit logic.
@@ -44,6 +45,11 @@ public final class QuitDialogView
/** Saves and exits the application. */
private Button saveAndQuitGameButton;
+ /**
+ * Sells the entire portfolio, then saves and returns to the main menu.
+ * */
+ private Button sellAllAndQuitButton;
+
/** Label used to communicate save status to the player. */
private Label statusLabel;
@@ -107,18 +113,21 @@ protected void initLayout() {
description.getStyleClass().add("quit-dialog-description");
description.setWrapText(true);
- // The four action buttons. Each is full-width inside the panel so the
+ // The five action buttons. Each is full-width inside the panel so the
// longer labels do not overflow.
continueButton = new Button("Continue");
saveButton = new Button("Save");
saveAndQuitMainMenuButton =
new Button("Save and quit to main menu");
saveAndQuitGameButton = new Button("Save and quit game");
+ sellAllAndQuitButton =
+ new Button("Sell portfolio, save and quit");
continueButton.setFocusTraversable(false);
saveButton.setFocusTraversable(false);
saveAndQuitMainMenuButton.setFocusTraversable(false);
saveAndQuitGameButton.setFocusTraversable(false);
+ sellAllAndQuitButton.setFocusTraversable(false);
continueButton.getStyleClass().addAll(
"quit-dialog-button", "quit-dialog-button-primary");
@@ -126,17 +135,21 @@ protected void initLayout() {
saveAndQuitMainMenuButton.getStyleClass().add("quit-dialog-button");
saveAndQuitGameButton.getStyleClass().addAll(
"quit-dialog-button", "quit-dialog-button-danger");
+ sellAllAndQuitButton.getStyleClass().addAll(
+ "quit-dialog-button", "quit-dialog-button-danger");
continueButton.setMaxWidth(Double.MAX_VALUE);
saveButton.setMaxWidth(Double.MAX_VALUE);
saveAndQuitMainMenuButton.setMaxWidth(Double.MAX_VALUE);
saveAndQuitGameButton.setMaxWidth(Double.MAX_VALUE);
+ sellAllAndQuitButton.setMaxWidth(Double.MAX_VALUE);
VBox buttonColumn = new VBox(10,
continueButton,
saveButton,
saveAndQuitMainMenuButton,
- saveAndQuitGameButton);
+ saveAndQuitGameButton,
+ sellAllAndQuitButton);
buttonColumn.setAlignment(Pos.CENTER);
// Status label - hidden until the controller writes something into it.
@@ -156,9 +169,9 @@ protected void initLayout() {
// shrink when the status label appears/disappears, and so it stays
// visually aligned with the settings overlay (which also has a
// fixed height).
- panel.setMinHeight(420);
- panel.setPrefHeight(420);
- panel.setMaxHeight(420);
+ panel.setMinHeight(480);
+ panel.setPrefHeight(480);
+ panel.setMaxHeight(480);
panel.getStyleClass().add("quit-dialog-panel");
// Dimmer wrapper.
@@ -172,6 +185,8 @@ protected void initLayout() {
saveAndQuitMainMenuButton);
registerButton(QuitDialogActions.SAVE_AND_QUIT_GAME,
saveAndQuitGameButton);
+ registerButton(QuitDialogActions.SELL_ALL_AND_QUIT,
+ sellAllAndQuitButton);
// The X button reuses the CONTINUE action so we don't need a
// dedicated enum entry just for "close".
closeButton.setOnAction(e -> continueButton.fire());
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsActions.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsActions.java
index 761e670..23f8afc 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsActions.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsActions.java
@@ -3,11 +3,14 @@
/**
* Enum representing all interactable actions in {@link StatsView}.
*
- * The stats widget is mostly an informational dashboard, so it currently
- * has no top-level buttons of its own. {@code SELECT_HOLDING} is reserved
- * for future use when individual holdings rows become clickable.
+ * {@code SELECT_HOLDING} is reserved for future use when individual
+ * holdings rows become clickable. {@code SELL_ENTIRE_PORTFOLIO} is the
+ * "sell everything" button shown at the bottom of the allocation panel.
* */
public enum StatsActions {
/** Reserved for future holding-row click handling. */
- SELECT_HOLDING;
+ SELECT_HOLDING,
+
+ /** Sells every share the player owns, across all tickers. */
+ SELL_ENTIRE_PORTFOLIO;
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsController.java
index 52e23bc..b934d50 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsController.java
@@ -110,6 +110,45 @@ protected void initInteractions() {
player.getNetWorthAsFloatProperty().addListener((observable, o, n) -> {
pushSnapshot();
});
+
+ getViewElement().setOnAction(
+ StatsActions.SELL_ENTIRE_PORTFOLIO, this::sellEntirePortfolio);
+ }
+
+ /**
+ * Sells every share the player currently owns, one ticker at a time.
+ *
+ * Uses the same {@link Exchange#sell(BigDecimal, String, Player)}
+ * entry point as the dashboard's individual sell button, so the
+ * commission, tax and {@code networthAsFloatProperty} updates all
+ * fire exactly as if the player had sold each ticker manually. The
+ * existing player-property listeners then refresh the stats widget
+ * (and every other widget that listens on net-worth) automatically.
+ *
+ * If the portfolio is already empty this is a no-op.
+ * */
+ private void sellEntirePortfolio() {
+ // Snapshot the symbols and their totals first so we don't iterate
+ // over a list that mutates while we sell.
+ Map totalsBySymbol = new LinkedHashMap<>();
+ for (Share s : player.getPortfolio().getShares()) {
+ String symbol = s.getStock().getSymbol();
+ totalsBySymbol.merge(symbol, s.getQuantity(), BigDecimal::add);
+ }
+
+ for (Map.Entry entry : totalsBySymbol.entrySet()) {
+ if (entry.getValue().signum() <= 0) {
+ continue;
+ }
+ try {
+ exchange.sell(entry.getValue(), entry.getKey(), player);
+ } catch (IllegalArgumentException e) {
+ // Skip symbols the player no longer owns (defensive - the
+ // snapshot above should already exclude these).
+ System.err.println("[stats] Sell-all skipped " + entry.getKey()
+ + ": " + e.getMessage());
+ }
+ }
}
/**
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsView.java
index 8f35e97..a7a1c55 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsView.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/stats/StatsView.java
@@ -4,6 +4,7 @@
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
+import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.ColumnConstraints;
@@ -69,6 +70,9 @@ public class StatsView extends ViewElement {
/** Allocation legend - one row per slice. */
private VBox allocLegend;
+ /** "Sell entire portfolio" button at the bottom of the allocation panel. */
+ private Button sellAllButton;
+
/** Holdings list rows. */
private VBox holdingsList;
@@ -253,7 +257,13 @@ private VBox buildAllocPanel() {
wrap.setAlignment(Pos.CENTER_LEFT);
VBox.setVgrow(wrap, Priority.ALWAYS);
- VBox panel = new VBox(6, title, wrap);
+ sellAllButton = new Button("Sell entire portfolio");
+ sellAllButton.setFocusTraversable(false);
+ sellAllButton.setMaxWidth(Double.MAX_VALUE);
+ sellAllButton.getStyleClass().add("stats-sell-all-button");
+ registerButton(StatsActions.SELL_ENTIRE_PORTFOLIO, sellAllButton);
+
+ VBox panel = new VBox(6, title, wrap, sellAllButton);
panel.getStyleClass().add("stats-panel");
panel.setMaxHeight(Double.MAX_VALUE);
return panel;
@@ -454,6 +464,12 @@ private void renderAllocation() {
allocPie.getChildren().clear();
allocLegend.getChildren().clear();
+ // The sell-all button only makes sense when there are holdings to
+ // sell, so disable it whenever the portfolio is empty.
+ if (sellAllButton != null) {
+ sellAllButton.setDisable(sumValue(holdings).signum() <= 0);
+ }
+
BigDecimal invested = sumValue(holdings);
BigDecimal total = invested.add(cash);
if (total.signum() <= 0) {
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 fc4143e..dabc63b 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
@@ -58,22 +58,21 @@ private void filterData(final String searchKeyword, final Integer weekTarget) {
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().floatValue() + " NOK",
- weekTarget.toString());
+ t.getShare().getQuantity(),
+ t.getCalculator().calculateGross(),
+ t.getCalculator().calculateCommission(),
+ t.getCalculator().calculateTax(),
+ t.getCalculator().calculateTotal(),
+ weekTarget);
}
});
}
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 b55efd3..7edaae8 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,8 @@
package edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewElement;
+
+import java.math.BigDecimal;
import java.util.List;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
@@ -102,16 +104,23 @@ protected void initLayout() {
* 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 gross the gross amount of NOK gained or lost upon
+ * transaction.
+ * @param commission the calculated commission fee for the transaction.
+ * @param tax the calculated tax for the transaction.
+ * @param totalChange the total amount of NOK gained or lost upon
+ * transaction.
* @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) {
+ final BigDecimal shares,
+ final BigDecimal gross,
+ final BigDecimal commission,
+ final BigDecimal tax,
+ final BigDecimal totalChange,
+ final int week) {
VBox card = new VBox();
// Header (SALE / PURCHASE)
@@ -120,13 +129,49 @@ public void addTransactionCard(final String type,
// Company Details
Label companyLabel = new Label(symbol + ", " + companyName);
- // Info Rows (+- shares, +- NOK)
+ // Card rows
VBox infoBox = new VBox();
- Label textRow1 = new Label(shares);
- Label textRow2 = new Label(nok);
+ Region spacer1 = new Region();
+ HBox.setHgrow(spacer1, Priority.ALWAYS);
+ HBox row1 = new HBox(
+ new Label("Quantity: "),
+ spacer1,
+ new Label(String.format("%.3f", shares.floatValue()))
+ );
+ Region spacer2 = new Region();
+ HBox.setHgrow(spacer2, Priority.ALWAYS);
+ HBox row2 = new HBox(
+ new Label("Gross: "),
+ spacer2,
+ new Label(String.format("%.3f", gross.floatValue()) + "NOK")
+ );
+
+ Region spacer3 = new Region();
+ HBox.setHgrow(spacer3, Priority.ALWAYS);
+ HBox row3 = new HBox(
+ new Label("Tax: "),
+ spacer3,
+ new Label(String.format("%.3f", tax.floatValue()) + "NOK")
+ );
+
+ Region spacer4 = new Region();
+ HBox.setHgrow(spacer4, Priority.ALWAYS);
+ HBox row4 = new HBox(
+ new Label("Commission fee: "),
+ spacer4,
+ new Label(String.format("%.3f", commission.floatValue()) + "NOK")
+ );
+
+ Region spacer5 = new Region();
+ HBox.setHgrow(spacer5, Priority.ALWAYS);
+ HBox row5 = new HBox(
+ new Label("Total: "),
+ spacer5,
+ new Label(String.format("%.3f", totalChange.floatValue()) + "NOK")
+ );
- infoBox.getChildren().addAll(textRow1, textRow2);
+ infoBox.getChildren().addAll(row1, row2, row3, row4, row5);
Region spacer = new Region();
VBox.setVgrow(spacer, Priority.ALWAYS);
@@ -147,8 +192,7 @@ public void addTransactionCard(final String type,
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");
+ infoBox.getChildren().forEach(n -> n.getStyleClass().add("transactions-cardText"));
cardWeekLabel.getStyleClass().add("transactions-cardText");
}
diff --git a/src/main/resources/styles.css b/src/main/resources/styles.css
index 60c21b6..8864002 100644
--- a/src/main/resources/styles.css
+++ b/src/main/resources/styles.css
@@ -1115,6 +1115,31 @@
-fx-padding: 20;
}
+.stats-sell-all-button {
+ -fx-background-color: rgba(20, 28, 46, 0.85);
+ -fx-background-radius: 8;
+ -fx-border-color: rgba(255, 99, 132, 0.55);
+ -fx-border-radius: 8;
+ -fx-border-width: 1.2;
+ -fx-padding: 9 16;
+ -fx-font-family: "Aptos";
+ -fx-font-weight: bold;
+ -fx-font-size: 13px;
+ -fx-text-fill: #ffb3c1;
+ -fx-cursor: hand;
+}
+
+.stats-sell-all-button:hover {
+ -fx-background-color: rgba(255, 99, 132, 0.15);
+ -fx-border-color: rgba(255, 99, 132, 0.85);
+ -fx-text-fill: #ffd9e1;
+}
+
+.stats-sell-all-button:disabled {
+ -fx-opacity: 0.45;
+ -fx-cursor: default;
+}
+
/* ========================================================
IN-GAME : TRANSACTIONS VIEW
======================================================== */
@@ -1207,7 +1232,6 @@
}
.transactions-cardText {
- -fx-font-family: "Consolas, Menlo, monospace";
-fx-font-size: 15px;
-fx-text-fill: #f0f4ff;
}
@@ -2177,6 +2201,18 @@
-fx-text-fill: #2a3654;
}
+.light-mode .stats-sell-all-button {
+ -fx-background-color: #fdeaee;
+ -fx-border-color: rgba(204, 36, 76, 0.55);
+ -fx-text-fill: #b1284c;
+}
+
+.light-mode .stats-sell-all-button:hover {
+ -fx-background-color: #f9d6dd;
+ -fx-border-color: #cc244c;
+ -fx-text-fill: #8a1e3b;
+}
+
/* ---------- IN-GAME : TRANSACTIONS ---------- */
.light-mode .transactions-root {
-fx-background-color: rgba(244, 247, 252, 0.65);