From 8766b03b9d081b6a8f8126eadfa903196ae7fd4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20Sk=C3=B8ien?= Date: Mon, 18 May 2026 22:00:16 +0200 Subject: [PATCH] fix checkstyle and add javadoc to tests --- .../idi/idatt2003/gruppe42/view/Popup.java | 1 - .../gruppe42/view/apps/AppStoreApp.java | 15 +- .../idatt2003/gruppe42/view/apps/BankApp.java | 43 ++++- .../gruppe42/view/apps/FilePickerApp.java | 92 ++++++++--- .../gruppe42/view/apps/GameOverApp.java | 23 ++- .../gruppe42/view/apps/HustlersApp.java | 131 +++++++++++---- .../idatt2003/gruppe42/view/apps/NewsApp.java | 14 +- .../gruppe42/view/apps/StockApp.java | 44 +++-- .../gruppe42/view/apps/WarningApp.java | 35 +++- .../gruppe42/view/apps/WeekendReportApp.java | 44 +++-- .../gruppe42/view/views/DesktopView.java | 152 ++++++++++++++---- .../gruppe42/view/views/StartView.java | 52 ++++-- .../views/components/DesktopBottomBar.java | 34 +++- .../view/views/components/Receipt.java | 76 ++++++--- .../view/views/components/StockGraph.java | 24 +-- .../gruppe42/CriticalFeaturesTest.java | 11 +- .../gruppe42/model/ExchangeTest.java | 9 +- .../idatt2003/gruppe42/model/PlayerTest.java | 12 +- .../gruppe42/model/PortfolioTest.java | 5 + .../idatt2003/gruppe42/model/ShareTest.java | 6 + .../gruppe42/model/StockFileHandlerTest.java | 7 +- .../idatt2003/gruppe42/model/StockTest.java | 5 + 22 files changed, 645 insertions(+), 190 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/Popup.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/Popup.java index e0d27dd..e4be32b 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/Popup.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/Popup.java @@ -156,7 +156,6 @@ private void loadStylesheets() { } } - private Button buildCloseButton() { Button button = new Button(); button.setShape(new Circle(6)); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/AppStoreApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/AppStoreApp.java index fed24d1..45148dc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/AppStoreApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/AppStoreApp.java @@ -4,18 +4,29 @@ import edu.ntnu.idi.idatt2003.gruppe42.view.Popup; /** - * A popup for the app store app. + * A popup window for the App Store application. + * + *

This popup is responsible for displaying available applications and related + * store functionality. */ public class AppStoreApp extends Popup { /** - * Constructs a new app store popup. + * Constructs a new App Store popup. + * + *

Initializes the popup with default size and position, and builds its UI content. */ public AppStoreApp() { super(500, 450, 100, 100, App.APPSTORE); buildContent(); } + /** + * Builds the UI content for the App Store popup. + * + *

This method is responsible for constructing and populating all visual + * components inside the popup. + */ private void buildContent() { } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/BankApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/BankApp.java index 9d34d70..733ae01 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/BankApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/BankApp.java @@ -19,7 +19,12 @@ import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -/** Bank app popup showing portfolio summary. */ +/** + * Bank application popup showing the player's portfolio and financial summary. + * + *

Displays total net worth, cash, invested value, return percentage, and a detailed + * list of owned shares with live market values and gains. + */ public class BankApp extends Popup { private static final String POSITIVE_STYLE = "stock-price-positive"; @@ -33,7 +38,11 @@ public class BankApp extends Popup { private Consumer onShareSelected; - /** Constructs the Bank app. */ + /** + * Constructs the Bank app popup. + * + *

Initializes the portfolio list view and builds the UI layout. + */ public BankApp() { super(600, 550, 220, 100, App.BANK); @@ -123,7 +132,14 @@ private VBox summaryItem(String title, Label valueLabel) { return box; } - /** Refreshes summary with latest figures. */ + /** + * Updates the financial summary values displayed in the UI. + * + * @param netWorth the player's total net worth + * @param cash the available cash balance + * @param invested the total invested amount + * @param startingMoney the initial starting capital used to calculate return percentage + */ public void updateStatus(double netWorth, double cash, double invested, double startingMoney) { Platform.runLater(() -> { netWorthLabel.setText(formatUsd(netWorth)); @@ -137,12 +153,15 @@ public void updateStatus(double netWorth, double cash, double invested, double s }); } - /** @return the portfolio list view. */ public ListView getPortfolioList() { return portfolioList; } - /** Sets the share selection callback. */ + /** + * Sets a callback triggered when a share is selected in the portfolio list. + * + * @param onShareSelected consumer handling the selected share + */ public void setOnShareSelected(Consumer onShareSelected) { this.onShareSelected = onShareSelected; } @@ -161,7 +180,11 @@ private static String formatUsd(double amount) { return "$" + String.format("%,.2f", amount); } - /** Renders a single share holding row. */ + /** + * List cell representing a single share holding in the portfolio. + * + *

Displays symbol, company name, quantity, prices, market value, and gain/loss. + */ private class ShareCell extends ListCell { private static final double COL_SYMBOL_MIN = 120; @@ -251,6 +274,12 @@ private void addCellContent() { grid.add(gainBox, 3, 0); } + /** + * Updates the cell content for a given share. + * + * @param share the share to display + * @param empty whether the cell is empty + */ @Override protected void updateItem(Share share, boolean empty) { super.updateItem(share, empty); @@ -271,7 +300,7 @@ private void populateLabels(Share share) { BigDecimal marketValue = currentPrice.multiply(quantity); BigDecimal totalCost = purchasePrice.multiply(quantity); BigDecimal gain = marketValue.subtract(totalCost); - BigDecimal gainPercent = computeGainPercent(gain, totalCost); + final BigDecimal gainPercent = computeGainPercent(gain, totalCost); symbolLabel.setText(share.getStock().getSymbol()); companyLabel.setText(share.getStock().getCompany()); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java index 7ba8668..26d9b3c 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java @@ -18,11 +18,14 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; -/** File picker app. */ +/** + * File picker popup used to browse the local file system and select CSV files. + * + *

The picker provides navigation (back, forward, up), a quick access sidebar, + * and a selectable file list filtered to CSV files.

+ */ public class FilePickerApp extends Popup { - private static final String FILTER = ".csv"; - private final Button backButton = new Button("←"); private final Button forwardButton = new Button("→"); private final Button upButton = new Button("↑"); @@ -47,6 +50,10 @@ public class FilePickerApp extends Popup { private File selectedFile; private Consumer onFileConfirmed; + /** + * Constructs a new file picker popup. + * Initializes UI and loads the initial directory view. + */ public FilePickerApp() { super(580, 420, 80, 60, App.FILEPICKER); buildLayout(); @@ -54,7 +61,10 @@ public FilePickerApp() { populateDir(currentDir); } - + /** + * Builds the full layout of the file picker including sidebar, + * toolbar, file list, and bottom action bar. + */ private void buildLayout() { backButton.getStyleClass().add("fp-toolbar-button"); forwardButton.getStyleClass().add("fp-toolbar-button"); @@ -84,7 +94,11 @@ private void buildLayout() { String[] names = { "Home", "Desktop", "Documents", "Downloads" }; for (int i = 0; i < QUICK_ACCESS.length; i++) { File dir = QUICK_ACCESS[i]; - if (!dir.exists()) continue; + + if (!dir.exists()) { + continue; + } + Button btn = new Button(icons[i] + " " + names[i]); btn.getStyleClass().add("fp-sidebar-item"); btn.setMaxWidth(Double.MAX_VALUE); @@ -130,7 +144,7 @@ private void buildLayout() { HBox nameRow = new HBox(8, new Label("File name:"), selectedField, filterLabel); nameRow.setAlignment(Pos.CENTER_LEFT); - ((Label) nameRow.getChildren().get(0)).getStyleClass().add("fp-bottom-label"); + (nameRow.getChildren().get(0)).getStyleClass().add("fp-bottom-label"); HBox buttonRow = new HBox(6, openButton, cancelButton); buttonRow.setAlignment(Pos.CENTER_RIGHT); @@ -145,6 +159,11 @@ private void buildLayout() { content.setPrefSize(580, 420); } + /** + * Builds the column header row for the file list. + * + * @return header HBox containing column labels + */ private HBox buildColumnHeader() { Label nameCol = new Label("Name"); Label typeCol = new Label("Type"); @@ -162,7 +181,9 @@ private HBox buildColumnHeader() { return header; } - + /** + * Wires toolbar navigation actions (back, up, address bar input). + */ private void wireToolbar() { backButton.setOnAction(e -> { if (prevDir != null) { @@ -180,11 +201,17 @@ private void wireToolbar() { }); addressBar.setOnAction(e -> { File typed = new File(addressBar.getText()); - if (typed.isDirectory()) populateDir(typed); + if (typed.isDirectory()) { + populateDir(typed); + } }); } - + /** + * Populates the file list with entries from the given directory. + * + * @param dir directory to display + */ private void populateDir(final File dir) { currentDir = dir; addressBar.setText(dir.getAbsolutePath()); @@ -195,7 +222,9 @@ private void populateDir(final File dir) { openButton.setDisable(true); File[] entries = dir.listFiles(); - if (entries == null) return; + if (entries == null) { + return; + } Arrays.stream(entries) .filter(f -> f.isDirectory() || f.getName().endsWith(FILTER)) @@ -205,10 +234,16 @@ private void populateDir(final File dir) { .forEach(f -> fileList.getChildren().add(buildRow(f))); } + /** + * Builds a UI row representing a file or directory. + * + * @param file file or directory to render + * @return HBox representing the row + */ private HBox buildRow(final File file) { String icon = file.isDirectory() ? "📁" : "📄"; - String type = file.isDirectory() ? "Folder" : "CSV File"; - String size = file.isDirectory() ? "" + final String type = file.isDirectory() ? "Folder" : "CSV File"; + final String size = file.isDirectory() ? "" : formatSize(file.length()); Label iconLabel = new Label(icon); @@ -256,6 +291,9 @@ private HBox buildRow(final File file) { return row; } + /** + * Confirms the currently selected file and triggers callback. + */ private void confirm() { if (selectedFile != null && onFileConfirmed != null) { onFileConfirmed.accept(selectedFile.toPath()); @@ -263,24 +301,42 @@ private void confirm() { } } + /** + * Clears the current selection highlight from the file list. + */ private void clearSelection() { fileList.getChildren() .forEach(n -> n.getStyleClass().remove("fp-row-selected")); } + /** + * Formats file size into human-readable units. + * + * @param bytes file size in bytes + * @return formatted string (B, KB, MB) + */ private static String formatSize(final long bytes) { - if (bytes < 1024) return bytes + " B"; - if (bytes < 1024 * 1024) return (bytes / 1024) + " KB"; + if (bytes < 1024) { + return bytes + " B"; + } + if (bytes < 1024 * 1024) { + return (bytes / 1024) + " KB"; + } return (bytes / (1024 * 1024)) + " MB"; } - - /** Sets onFileConfirmed callback. */ + /** + * Sets callback executed when a file is confirmed. + * + * @param callback consumer receiving selected file path + */ public void setOnFileConfirmed(final Consumer callback) { this.onFileConfirmed = callback; } - /** Shows the picker. */ + /** + * Shows the file picker and refreshes the directory view. + */ @Override public void show() { selectedFile = null; @@ -290,7 +346,7 @@ public void show() { super.show(); } - /** Hides the picker. */ + /** Hides the file picker. */ @Override public void hide() { super.hide(); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/GameOverApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/GameOverApp.java index aea7d44..b924a4b 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/GameOverApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/GameOverApp.java @@ -8,11 +8,21 @@ import javafx.scene.control.Label; import javafx.scene.layout.VBox; -/** Modal shown on game over. */ +/** + * Game over modal popup shown when the player has lost all lives. + * + *

Displays a final message and provides a restart option.

+ */ public class GameOverApp extends Popup { private final Button startOverButton = new Button("Start Over"); + /** + * Constructs the Game Over popup. + * + *

Initializes UI components and disables the close button to prevent + * accidental dismissal.

+ */ public GameOverApp() { super(500, 360, 0, 0, App.GAMEOVER); @@ -27,6 +37,10 @@ public GameOverApp() { buildLayout(); } + /** + * Builds the internal layout of the game over screen, + * including title, message, and restart button. + */ private void buildLayout() { Label title = new Label("Game Over"); title.getStyleClass().add("game-over-title"); @@ -45,14 +59,15 @@ private void buildLayout() { content.getChildren().setAll(inner); } - /** @return start over button. */ public Button getStartOverButton() { return startOverButton; } - /** Shows the app. */ + /** + * Shows the Game Over popup. + */ @Override - public void show(){ + public void show() { super.show(); } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java index a7bdc69..a436384 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java @@ -16,9 +16,14 @@ import javafx.scene.text.Text; import javafx.scene.text.TextFlow; -/** Tutorial app popup. */ +/** + * Tutorial-style popup application that provides in-game guidance + * for core mechanics such as trading, banking, messaging, and rent. + * + *

The content is structured into chapters displayed in a sidebar, + * with dynamic rendering of formatted instructional text in the main panel.

+ */ public class HustlersApp extends Popup { - private static final String BG_ROOT = "#ffffff"; private static final String BG_SIDEBAR = "#f4f4f4"; private static final String BG_CONTENT = "#ffffff"; @@ -29,19 +34,28 @@ public class HustlersApp extends Popup { private static final String DIVIDER = "#e0e0e0"; private static final String HOVER_BG = "#e8e8e8"; private static final String SELECTED_BG = "#fef9ec"; - private Label activeNavItem = null; private VBox contentBody; private Label chapterTitleLabel; private final Map chapters = new LinkedHashMap<>(); - /** Constructs the Hustlers app. */ + /** + * Constructs the Hustler's University tutorial popup. + * + *

Initializes all tutorial chapters and builds the UI layout, + * including sidebar navigation and content rendering system.

+ */ public HustlersApp() { super(720, 480, 80, 60, App.HUSTLERS); buildChapters(); - buildUI(); + buildUi(); } + /** + * Builds all tutorial chapters used in the sidebar navigation. + * + *

Each chapter consists of a title and formatted instructional content.

+ */ private void buildChapters() { chapters.put("Welcome", new String[]{ "HUSTLER'S UNIVERSITY", @@ -61,7 +75,8 @@ private void buildChapters() { + "Green means the price is climbing; red means it's falling.\n\n" + "HOW TO BUY\n" + "Select s stock, enter how many units you want, and tap TRADE. " - + "The cost is deducted from your wallet immediately, unless you don't have enough...\n\n" + + "The cost is deducted from your wallet immediately, " + + "unless you don't have enough...\n\n" + "HOW TO SELL\n" + "Select a held stock, enter how many units you want to sell, and tap TRADE. " + "Profit = (sell price minus buy price) times units.\n\n" @@ -78,7 +93,8 @@ private void buildChapters() { "HOW THE BANK WORKS", "The Bank app is your financial safety net — and leverage tool.\n\n" + "SAVINGS ACCOUNT\n" - + "Your account displays your total balance, cash available, cash invested and total return. " + + "Your account displays your total balance, cash available, " + + "cash invested and total return. " + "Remember, green numbers mean you're flying, red means you gotta lock in chief.\n\n" + "PORTFOLIO\n" + "If you like to brag about your investments, this is where it's at. " @@ -153,9 +169,10 @@ private void buildChapters() { chapters.put("Rent", new String[]{ "HOW RENT WORKS", - "Rent is collected automatically at the end of every in-game week.\n\n" + "Rent is collected automatically at the end of every in-game week.\n" + "WHEN IT'S DUE\n" - + "Rent is charged on Saturday 00:00, right before the new week begins, so you have until Friday to make sure you're set.\n\n" + + "Rent is charged on Saturday 00:00, right before the new week begins, " + + "so you have until Friday to make sure you're set.\n\n" + "HOW IT'S PAID\n" + "The rent amount is deducted directly from your wallet — automatically, " + "no action needed on your part. Make sure the money is in your wallet, " @@ -165,7 +182,8 @@ private void buildChapters() { + " - You lose ONE heart.\n" + " - Losing all hearts ends the game.\n\n" + "HOW MUCH IS RENT?\n" - + "Your rent amount is shown on the Rent Day popup at Saturday 00:00. You can count on it being $50 the first couple weeks.\n" + + "Your rent amount is shown on the Rent Day popup at Saturday 00:00. " + + "You can count on it being $50 the first couple weeks.\n" + "It may increase as you progress — so keep earning.\n\n" + "PRO TIPS\n" + "Check your wallet balance on Friday.\n" @@ -190,7 +208,10 @@ private void buildChapters() { }); } - private void buildUI() { + /** + * Builds the full UI layout including sidebar navigation and content area. + */ + private void buildUi() { chapterTitleLabel = new Label(); chapterTitleLabel.setStyle( "-fx-text-fill: " + ACCENT + ";" @@ -223,6 +244,11 @@ private void buildUI() { applyChapter(firstKey); } + /** + * Constructs the sidebar containing chapter navigation items. + * + * @return configured sidebar VBox + */ private VBox buildSidebar() { VBox sidebar = new VBox(); sidebar.setStyle( @@ -249,29 +275,7 @@ private VBox buildSidebar() { nav.setPadding(new Insets(8, 6, 8, 6)); for (String chapter : chapters.keySet()) { - Label item = new Label(chapter); - item.setPadding(new Insets(8, 10, 8, 10)); - item.setMaxWidth(Double.MAX_VALUE); - item.setStyle(navStyle(false)); - - item.setOnMouseEntered(e -> { - if (item != activeNavItem) { - item.setStyle(navHoverStyle()); - } - }); - item.setOnMouseExited(e -> { - if (item != activeNavItem) { - item.setStyle(navStyle(false)); - } - }); - item.setOnMouseClicked(e -> { - if (activeNavItem != null) { - activeNavItem.setStyle(navStyle(false)); - } - activeNavItem = item; - item.setStyle(navStyle(true)); - applyChapter(chapter); - }); + Label item = getLabel(chapter); nav.getChildren().add(item); } @@ -286,6 +290,38 @@ private VBox buildSidebar() { return sidebar; } + private Label getLabel(String chapter) { + Label item = new Label(chapter); + item.setPadding(new Insets(8, 10, 8, 10)); + item.setMaxWidth(Double.MAX_VALUE); + item.setStyle(navStyle(false)); + + item.setOnMouseEntered(e -> { + if (item != activeNavItem) { + item.setStyle(navHoverStyle()); + } + }); + item.setOnMouseExited(e -> { + if (item != activeNavItem) { + item.setStyle(navStyle(false)); + } + }); + item.setOnMouseClicked(e -> { + if (activeNavItem != null) { + activeNavItem.setStyle(navStyle(false)); + } + activeNavItem = item; + item.setStyle(navStyle(true)); + applyChapter(chapter); + }); + return item; + } + + /** + * Builds the main content wrapper containing the chapter title and scrollable content area. + * + * @return configured content wrapper VBox + */ private VBox buildContentWrapper() { HBox titleBar = new HBox(); titleBar.setPadding(new Insets(13, 20, 13, 20)); @@ -311,6 +347,11 @@ private VBox buildContentWrapper() { return wrapper; } + /** + * Applies the selected chapter and renders its content. + * + * @param key the chapter key to display + */ private void applyChapter(String key) { String[] content = chapters.get(key); if (content == null) { @@ -321,6 +362,11 @@ private void applyChapter(String key) { renderContent(content[1]); } + /** + * Renders formatted instructional text into the content body. + * + * @param rawText raw chapter text to render + */ private void renderContent(String rawText) { for (String para : rawText.split("\n\n")) { para = para.trim(); @@ -357,6 +403,12 @@ private void renderContent(String rawText) { } } + /** + * Determines whether a paragraph should be rendered as a section header. + * + * @param text input text + * @return true if text is considered a header, false otherwise + */ private boolean isSectionHeader(String text) { String stripped = text.replaceAll("[^a-zA-Z]", ""); if (stripped.isEmpty()) { @@ -365,6 +417,12 @@ private boolean isSectionHeader(String text) { return stripped.equals(stripped.toUpperCase()) && text.length() < 60; } + /** + * Generates CSS style string for navigation items. + * + * @param selected whether the item is currently selected + * @return CSS style string + */ private String navStyle(boolean selected) { return "-fx-text-fill: " + (selected ? ACCENT : TEXT_SEC) + ";" + "-fx-font-size: 11px;" @@ -377,6 +435,11 @@ private String navStyle(boolean selected) { : ""); } + /** + * Generates CSS style string for hovered navigation items. + * + * @return CSS style string for hover state + */ private String navHoverStyle() { return "-fx-text-fill: " + TEXT_PRIMARY + ";" + "-fx-font-size: 11px;" diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/NewsApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/NewsApp.java index d4f3019..ec58d77 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/NewsApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/NewsApp.java @@ -3,10 +3,20 @@ import edu.ntnu.idi.idatt2003.gruppe42.model.App; import edu.ntnu.idi.idatt2003.gruppe42.view.Popup; -/** News app popup. */ +/** + * News application popup. + * + *

This popup represents a simple news app window. Content is intended + * to be added in future implementations.

+ */ public class NewsApp extends Popup { - /** Constructs the News app. */ + /** + * Constructs the NewsApp popup. + * + *

Initializes the popup window with default size and position, + * and assigns it to the NEWS application type.

+ */ public NewsApp() { super(400, 300, 180, 180, App.NEWS); // Add news-specific content here diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java index c9d30d8..313b822 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java @@ -11,6 +11,7 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.Spinner; import javafx.scene.control.TextField; @@ -18,9 +19,21 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import javafx.scene.control.Label; -/** Popup for the Stock app. */ +/** + * Popup window for the Stock trading application. + * + *

Provides a searchable and sortable list of available stocks, and a detailed + * trading view where the user can buy or sell shares. The view includes a + * price display, interactive trade controls, a live price graph, and a receipt + * preview of the current transaction. + * + *

The popup supports two main states: + *

    + *
  • Search view – browse and filter available stocks
  • + *
  • Stock detail view – view charts and execute trades
  • + *
+ */ public class StockApp extends Popup { private final ListView stockList; @@ -34,7 +47,11 @@ public class StockApp extends Popup { private final Button backButton; private final Receipt receipt; - /** Constructs the Stock app. */ + /** + * Constructs the Stock trading popup. + * + * @param player the current player, used for portfolio context and receipt calculations + */ public StockApp(final Player player) { super(500, 450, 140, 140, App.STOCK); @@ -76,7 +93,9 @@ public StockApp(final Player player) { showSearchPage(); } - /** Shows stock search page. */ + /** + * Shows the stock search page where the user can browse and filter stocks. + */ public void showSearchPage() { content.setPadding(new Insets(20)); content.setSpacing(20); @@ -85,7 +104,11 @@ public void showSearchPage() { content.getChildren().setAll(searchBox, stockList); } - /** Shows stock detail page. */ + /** + * Shows the detailed stock view including graph, price, and trading controls. + * + * @param stock the selected stock to display + */ public void showStockPage(final Stock stock) { content.getChildren().clear(); @@ -98,7 +121,7 @@ public void showStockPage(final Stock stock) { companyTitle.getStyleClass().add("stock-title"); Label symbolLabel = new Label(stock.getSymbol()); symbolLabel.getStyleClass().add("stock-symbol-large"); - VBox titleBox = new VBox(5, companyTitle, symbolLabel); + final VBox titleBox = new VBox(5, companyTitle, symbolLabel); HBox tradeControls = new HBox(10); tradeControls.setAlignment(Pos.CENTER_RIGHT); @@ -135,47 +158,38 @@ public void updatePriceLabel(final Stock stock) { } } - /** @return the search field. */ public TextField getSearchField() { return searchField; } - /** @return the stock list view. */ public ListView getStockList() { return stockList; } - /** @return quantity spinner. */ public Spinner getQuantitySpinner() { return quantitySpinner; } - /** @return confirm button. */ public Button getConfirmButton() { return confirmButton; } - /** @return receipt component. */ public Receipt getReceipt() { return receipt; } - /** @return back button. */ public Button getBackButton() { return backButton; } - /** @return alpha button. */ public Button getAlphaButton() { return alphaButton; } - /** @return gainer button. */ public Button getGainerButton() { return gainerButton; } - /** @return price button. */ public Button getPriceButton() { return priceButton; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java index 34b1fb3..37e958d 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java @@ -11,6 +11,14 @@ import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; +/** + * A reusable warning popup used to display alerts, confirmations, + * and important game messages. + * + *

The popup supports configurable icon, title, message text, and + * one or two action buttons depending on the context. + * It is used for game warnings, confirmations, and critical events. + */ public final class WarningApp extends Popup { private final Label iconLabel = new Label(); @@ -18,8 +26,14 @@ public final class WarningApp extends Popup { private final Label messageLabel = new Label(); private final Button primaryButton = new Button(); private final Button secondaryButton = new Button(); - private final HBox buttons; + /** + * Constructs a Warning popup window. + * + *

Initializes the UI layout including icon, title, message area, + * and action buttons. The popup is styled for modal warnings + * and can be configured dynamically using {@link #configure}. + */ public WarningApp() { super(370, 270, 0, 0, App.WARNING); @@ -52,7 +66,7 @@ public WarningApp() { secondaryButton.getStyleClass().add("back-button"); secondaryButton.setPrefWidth(130); - buttons = new HBox(12, secondaryButton, primaryButton); + HBox buttons = new HBox(12, secondaryButton, primaryButton); buttons.setAlignment(Pos.CENTER); VBox inner = new VBox(10, iconLabel, titleLabel, messageScroll, buttons); @@ -63,6 +77,14 @@ public WarningApp() { content.getChildren().setAll(inner); } + /** + * Configures the warning popup with a single primary action button. + * + * @param icon the icon/emoji displayed at the top of the popup + * @param title the title of the warning + * @param message the detailed warning message + * @param primaryLabel the label for the primary action button + */ public void configure( final String icon, final String title, @@ -82,6 +104,15 @@ public void configure( getCloseButton().setOnAction(event -> primaryButton.fire()); } + /** + * Configures the warning popup with both primary and secondary actions. + * + * @param icon the icon/emoji displayed at the top of the popup + * @param title the title of the warning + * @param message the detailed warning message + * @param secondaryLabel the label for the secondary/cancel button + * @param primaryLabel the label for the primary action button + */ public void configure( final String icon, final String title, diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WeekendReportApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WeekendReportApp.java index 8baca67..1e2e9a9 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WeekendReportApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WeekendReportApp.java @@ -16,7 +16,15 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -/** End-of-week financial report. */ +/** + * End-of-week financial report popup shown after the weekly simulation cycle. + * + *

Displays the player's trading performance, including net income, rent deduction, + * balance after rent, and a list of all transactions performed during the week. + * + *

The report also provides a qualitative verdict based on financial performance + * and allows the player to continue to the next in-game day. + */ public class WeekendReportApp extends Popup { private static final BigDecimal STRONG_WEEK_THRESHOLD = new BigDecimal("50.00"); @@ -33,6 +41,11 @@ public class WeekendReportApp extends Popup { private final Button continueButton = new Button("Continue to Sunday →"); + /** + * Constructs the weekend report popup. + * + *

Initializes the layout and binds the continue button to close/advance the report. + */ public WeekendReportApp() { super(740, 620, 0, 0, App.WEEKENDRAPPORT); @@ -62,7 +75,7 @@ private void buildLayout() { playerBlock.getStyleClass().add("rapport-player-block"); playerBlock.setPadding(new Insets(20, 16, 20, 16)); - Label transactionHeader = sectionHeader("Transactions"); + final Label transactionHeader = sectionHeader(); ScrollPane scrollPane = new ScrollPane(receiptList); scrollPane.setFitToWidth(true); @@ -72,7 +85,7 @@ private void buildLayout() { cashLabel.getStyleClass().add("rapport-line-value"); rentLabel.getStyleClass().addAll("rapport-line-value", "rapport-rent-value"); - HBox cashRow = footerRow("Cash Available", cashLabel); + final HBox cashRow = footerRow("Cash Available", cashLabel); HBox rentRow = footerRow("Rent Due", rentLabel); rentRow.getStyleClass().add("rapport-rent-row"); @@ -104,8 +117,8 @@ private void buildLayout() { ); } - private static Label sectionHeader(String text) { - Label label = new Label(text); + private static Label sectionHeader() { + Label label = new Label("Transactions"); label.getStyleClass().add("rapport-section-header"); return label; } @@ -123,14 +136,15 @@ private static HBox footerRow(String labelText, Label valueLabel) { } /** - * Populates the rapport with the completed week's data. - * Rent has already been deducted from the player's balance before this is called. + * Populates the weekend report with financial data from the completed week. * - * @param transactions all transactions that occurred this week - * @param netIncome net cash change from trading (sales minus purchases) - * @param cashBeforeRent cash available before rent was deducted - * @param rent the rent amount that was deducted - * @param cashAfterRent cash balance after rent deduction (may be negative) + *

All transactions and balances reflect the state after rent has been deducted. + * + * @param transactions all transactions performed during the week + * @param netIncome net profit/loss from trading activities + * @param cashBeforeRent cash balance before rent was deducted + * @param rent rent amount deducted from the player + * @param cashAfterRent final balance after rent deduction */ public void populate( List transactions, @@ -237,9 +251,11 @@ static String formatBalance(BigDecimal amount) { return "$" + String.format("%,.2f", amount); } - /** Shows the rapport. */ + /** + * Displays the weekend report popup. + */ @Override - public void show(){ + public void show() { super.show(); } } \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/DesktopView.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/DesktopView.java index 19f352d..a72d501 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/DesktopView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/DesktopView.java @@ -3,19 +3,15 @@ import edu.ntnu.idi.idatt2003.gruppe42.controller.viewcontrollers.DesktopViewController; import edu.ntnu.idi.idatt2003.gruppe42.model.App; import edu.ntnu.idi.idatt2003.gruppe42.view.views.components.DesktopBottomBar; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import java.util.Objects; import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.effect.GaussianBlur; -import javafx.scene.input.KeyCode; import javafx.scene.layout.BorderPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; @@ -26,16 +22,28 @@ import javafx.scene.layout.StackPane; /** - * Desktop view: 10x6 app-icon grid, bottom status bar, and a layered modal overlay system. + * Main desktop UI view of the application. + * + *

The desktop view consists of: + *

    + *
  • A 10x6 grid of application icons
  • + *
  • A bottom status bar showing player and game state
  • + *
  • A layered modal system (rapport, warning, game over)
  • + *
  • A popup overlay system for draggable apps
  • + *
+ * + *

The view supports drag-and-drop app placement, modal locking, + * and dynamic updates from the {@link DesktopViewController}. */ public final class DesktopView { - + /** + * Represents the available modal overlay layers in the desktop UI. + */ public enum ModalLayer { RAPPORT, WARNING, GAME_OVER } private static final int COLS = 10; private static final int ROWS = 6; - private static final int HEART_COUNT = 3; private static final double BACKDROP_BLUR = 8.0; private final DesktopViewController controller; @@ -47,8 +55,8 @@ public enum ModalLayer { RAPPORT, WARNING, GAME_OVER } private final Pane warningOverlay = createOverlayPane(); private final Pane gameOverOverlay = createOverlayPane(); - private Node gridArea; - private DesktopBottomBar bottomBar; + private final Node gridArea; + private final DesktopBottomBar bottomBar; private final List popupRoots = new ArrayList<>(); @@ -58,7 +66,12 @@ public enum ModalLayer { RAPPORT, WARNING, GAME_OVER } private final EnumSet activeLayers = EnumSet.noneOf(ModalLayer.class); - /** Constructs the desktop view. */ + /** + * Constructs the desktop view and initializes the grid, bottom bar, + * and overlay layers. + * + * @param controller the controller responsible for handling UI actions + */ public DesktopView(final DesktopViewController controller) { this.controller = controller; this.desktopPane = new BorderPane(); @@ -72,10 +85,9 @@ public DesktopView(final DesktopViewController controller) { // Ensure the popup overlay is in a pane that allows x,y positioning, // and then place that pane in the center of the BorderPane. Pane centerArea = new Pane(gridArea, popupOverlay); - if (gridArea instanceof Region region) { - region.prefWidthProperty().bind(centerArea.widthProperty()); - region.prefHeightProperty().bind(centerArea.heightProperty()); - } + Region region = (Region) gridArea; + region.prefWidthProperty().bind(centerArea.widthProperty()); + region.prefHeightProperty().bind(centerArea.heightProperty()); popupOverlay.prefWidthProperty().bind(centerArea.widthProperty()); popupOverlay.prefHeightProperty().bind(centerArea.heightProperty()); @@ -84,7 +96,12 @@ public DesktopView(final DesktopViewController controller) { desktopPane.getStyleClass().add("desktop-root"); } - /** @return the scene root containing the desktop and popup overlay. */ + /** + * Returns the root node of the desktop scene graph. + * Initializes stylesheets and layout if not already created. + * + * @return the root {@link Pane} containing the desktop view + */ public Pane getRoot() { if (sceneRoot == null) { sceneRoot = new Pane(desktopPane); @@ -97,12 +114,20 @@ public Pane getRoot() { return sceneRoot; } - /** @return the inner BorderPane for gradient/style targeting. */ + /** + * Returns the main desktop container (used for styling and bindings). + * + * @return the root {@link BorderPane} of the desktop UI + */ public BorderPane getDesktopPane() { return desktopPane; } - /** @return the overlay pane that holds popups and modals. */ + /** + * Returns the overlay pane used for displaying popup windows. + * + * @return the popup overlay {@link Pane} + */ public Pane getPopupOverlay() { return popupOverlay; } @@ -128,22 +153,36 @@ private void loadStylesheets() { } } - - /** Registers a popup root. */ + /** + * Registers a popup node so it can be disabled/enabled when modals are active. + * + * @param popupRoot the root node of the popup + */ public void registerPopup(final Node popupRoot) { popupRoots.add(popupRoot); } - /** Registers a modal root. */ + /** + * Registers a modal root node for a given modal layer. + * + * @param layer the modal layer type + * @param modalRoot the root node of the modal + */ public void registerModal(final ModalLayer layer, final Node modalRoot) { switch (layer) { case RAPPORT -> rapportRoot = modalRoot; case WARNING -> warningRoot = modalRoot; case GAME_OVER -> gameOverRoot = modalRoot; + default -> throw new IllegalStateException("Unexpected modal layer: " + layer); } } - /** @return the overlay pane for the given layer. */ + /** + * Returns the overlay pane associated with a modal layer. + * + * @param layer the modal layer + * @return the overlay pane for that layer + */ public Pane getOverlay(final ModalLayer layer) { return switch (layer) { case RAPPORT -> rapportOverlay; @@ -152,13 +191,21 @@ public Pane getOverlay(final ModalLayer layer) { }; } - /** Activates modal layer. */ + /** + * Activates the given modal layer and updates UI locking. + * + * @param layer the layer to activate + */ public void enterLayer(final ModalLayer layer) { activeLayers.add(layer); recompute(); } - /** Deactivates modal layer. */ + /** + * Deactivates the given modal layer and updates UI locking. + * + * @param layer the layer to deactivate + */ public void exitLayer(final ModalLayer layer) { activeLayers.remove(layer); recompute(); @@ -272,25 +319,42 @@ private static boolean isDesktopApp(final App app) { } private String resource(final String path) { - return getClass().getResource(path).toExternalForm(); + return Objects.requireNonNull(getClass().getResource(path)).toExternalForm(); } - /** Updates player name. */ + /** + * Updates the displayed player name in the bottom bar. + * + * @param name the player name + */ public void updatePlayerName(final String name) { Platform.runLater(() -> bottomBar.getPlayerLabel().setText("User: " + name)); } - /** Updates player status. */ + /** + * Updates the displayed player status. + * + * @param status the status text + */ public void updatePlayerStatus(final String status) { Platform.runLater(() -> bottomBar.getPlayerStatusLabel().setText("Status: " + status)); } - /** Updates clock. */ + /** + * Updates the clock display. + * + * @param time formatted time string + */ public void updateClock(final String time) { Platform.runLater(() -> bottomBar.getClockLabel().setText(time)); } - /** Updates day. */ + /** + * Updates the day display and weekend button visibility. + * + * @param day the current day name + * @param isSunday whether the current day is Sunday + */ public void updateDay(final String day, final boolean isSunday) { Platform.runLater(() -> { bottomBar.getDayLabel().setText(day); @@ -298,27 +362,47 @@ public void updateDay(final String day, final boolean isSunday) { }); } - /** Updates week. */ + /** + * Updates the week display. + * + * @param week formatted week string + */ public void updateWeek(final String week) { Platform.runLater(() -> bottomBar.getWeekLabel().setText(week)); } - /** Updates weather. */ + /** + * Updates the weather display. + * + * @param weather weather description + */ public void updateWeather(final String weather) { Platform.runLater(() -> bottomBar.getWeatherLabel().setText(weather)); } - /** Updates temperature. */ + /** + * Updates the temperature display. + * + * @param temperature temperature value + */ public void updateTemperature(final String temperature) { Platform.runLater(() -> bottomBar.getTempLabel().setText(temperature + "°C")); } - /** @return the next week button. */ + /** + * Returns the "Next Week" button from the bottom bar. + * + * @return the next week {@link Button} + */ public Button getNextWeekButton() { return bottomBar.getNextWeekButton(); } - /** @return the settings button. */ + /** + * Returns the settings button from the bottom bar. + * + * @return the settings {@link Button} + */ public Button getSettingsButton() { return bottomBar.getSettingsButton(); } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java index 3f04b6f..d23c8b8 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java @@ -15,10 +15,13 @@ import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; -/** Start screen view. */ +/** + * Represents the start screen view of the application. + * + *

This view allows the user to enter a username, select a starting difficulty + * (starting money), and proceed into the game. It also provides access to settings.

+ */ public class StartView { - - private final TextField usernameField = new TextField(); private final Button loginButton = new Button("→"); private Button settingsButton; @@ -26,8 +29,12 @@ public class StartView { private final StackPane content = new StackPane(); private final BorderPane root = new BorderPane(); - - /** @return the root node. */ + /** + * Returns the root node of the start screen. If the UI has not yet been built, + * it constructs and initializes all components. + * + * @return the root {@link BorderPane} containing the start view + */ public BorderPane getRoot() { if (!root.getChildren().isEmpty()) { return root; // already built @@ -42,7 +49,8 @@ public BorderPane getRoot() { // Avatar VBox avatarContainer = new VBox(); avatarContainer.getStyleClass().add("avatar-container"); - Image logoImage = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/logo.jpg"))); + Image logoImage = new Image(Objects.requireNonNull(getClass() + .getResourceAsStream("/images/logo.jpg"))); ImageView logoView = new ImageView(logoImage); logoView.setPreserveRatio(true); logoView.setFitWidth(120); @@ -104,25 +112,35 @@ public BorderPane getRoot() { return root; } + /** + * Loads the CSS stylesheets required for the start view. + */ private void loadStylesheets() { root.getStylesheets().clear(); try { root.getStylesheets().addAll( - getClass().getResource("/css/global.css").toExternalForm(), - getClass().getResource("/css/login.css").toExternalForm() + Objects.requireNonNull(getClass().getResource("/css/global.css")).toExternalForm(), + Objects.requireNonNull(getClass().getResource("/css/login.css")).toExternalForm() ); } catch (Exception e) { System.err.println("[CSS] Failed to load stylesheets: " + e.getMessage()); } } - - /** @return the username field. */ + /** + * Returns the username input field. + * + * @return the {@link TextField} used for username input + */ public TextField getUsernameField() { return usernameField; } - /** @return selected difficulty mode. */ + /** + * Returns the selected starting difficulty option as text. + * + * @return the selected mode, or {@code null} if none is selected + */ public String getSelectedMode() { RadioButton selectedMode = (RadioButton) startingMoneyGroup.getSelectedToggle(); if (selectedMode == null) { @@ -131,12 +149,20 @@ public String getSelectedMode() { return selectedMode.getText(); } - /** @return login button. */ + /** + * Returns the login/continue button. + * + * @return the login {@link Button} + */ public Button getLoginButton() { return loginButton; } - /** @return settings button. */ + /** + * Returns the settings button. + * + * @return the settings {@link Button} + */ public Button getSettingsButton() { return settingsButton; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/DesktopBottomBar.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/DesktopBottomBar.java index dbb7310..4029551 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/DesktopBottomBar.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/DesktopBottomBar.java @@ -11,13 +11,19 @@ import javafx.scene.layout.Region; /** - * The bottom bar of the desktop view. - * Displays player information, lives, game time, and status. + * Bottom status bar for the desktop view. + * + *

Displays key player and game information such as: + *

    + *
  • Player name and status
  • + *
  • Lives (hearts)
  • + *
  • Current weather and temperature
  • + *
  • Day, week, and clock
  • + *
  • Controls for settings and advancing the week
  • + *
*/ public final class DesktopBottomBar extends HBox { - private static final int HEART_COUNT = 3; - private final Label playerLabel; private final Label playerStatusLabel; private final Label weatherLabel; @@ -29,9 +35,9 @@ public final class DesktopBottomBar extends HBox { private final Button settingsButton; /** - * Constructs the bottom bar. + * Constructs the bottom bar UI and binds it to the player model. * - * @param player the player model to display info from + * @param player the player model used to populate UI data */ public DesktopBottomBar(Player player) { super(15); @@ -50,7 +56,7 @@ public DesktopBottomBar(Player player) { playerStatusLabel.getStyleClass().add("desktop-label-bold"); playerStatusLabel.setMinWidth(Region.USE_PREF_SIZE); - HBox heartsBox = buildHeartsBox(player); + final HBox heartsBox = buildHeartsBox(player); nextWeekButton = new Button("Advance to next week"); nextWeekButton.getStyleClass().add("primary-button"); @@ -77,6 +83,14 @@ public DesktopBottomBar(Player player) { ); } + /** + * Builds the lives display using heart icons and binds it to the player. + * + *

The hearts update automatically when the player's lives change. + * + * @param player the player whose lives are displayed + * @return an {@link HBox} containing heart icons + */ private HBox buildHeartsBox(final Player player) { HBox box = new HBox(4); box.setAlignment(Pos.CENTER_LEFT); @@ -103,6 +117,12 @@ private HBox buildHeartsBox(final Player player) { return box; } + /** + * Creates a label with the given CSS style class. + * + * @param styleClass the CSS style class to apply + * @return a styled {@link Label} + */ private Label styledLabel(final String styleClass) { Label label = new Label(); label.getStyleClass().add(styleClass); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/Receipt.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/Receipt.java index a35fbf0..ec8c559 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/Receipt.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/Receipt.java @@ -15,7 +15,15 @@ import javafx.scene.layout.VBox; /** - * The single shared receipt component used everywhere in the game. + * Shared receipt component used to display transaction details in the game. + * + *

The receipt supports two modes: + *

    + *
  • Live preview for upcoming trades
  • + *
  • Historical view for completed transactions
  • + *
+ * + *

It calculates gross value, commission, and optional tax depending on trade type. */ public final class Receipt extends VBox { @@ -23,7 +31,12 @@ public final class Receipt extends VBox { private static final BigDecimal TAX_RATE = new BigDecimal("0.22"); private final Player player; - /** Creates an empty live-preview receipt. Call {@link #update} to populate. */ + /** + * Creates an empty live-preview receipt. + * Call {@link #update(Stock, BigDecimal)} to populate it. + * + * @param player the player used for tax estimation (might be {@code null} in read-only mode) + */ public Receipt(final Player player) { this.player = player; getStyleClass().add("receipt-container"); @@ -34,10 +47,11 @@ public Receipt(final Player player) { /** * Creates a populated receipt for a completed transaction. - * Used by the Weekend Rapport (read-only). + * + *

Used in read-only contexts such as the weekend report. * * @param tx the completed transaction to render - * @return a new populated receipt card + * @return a fully populated receipt instance */ public static Receipt forTransaction(final Transaction tx) { Receipt receipt = new Receipt(null); @@ -59,8 +73,12 @@ public static Receipt forTransaction(final Transaction tx) { } /** - * Re-renders as a live preview for the given stock and signed quantity. - * A positive quantity is a BUY; negative is a SELL. + * Updates the receipt as a live preview for a potential trade. + * + *

A positive quantity represents a BUY, and a negative quantity represents a SELL. + * + * @param stock the selected stock + * @param quantity the signed quantity of shares */ public void update(final Stock stock, final BigDecimal quantity) { if (stock == null || quantity == null) { @@ -83,7 +101,13 @@ public void update(final Stock stock, final BigDecimal quantity) { ); } - /** Computes the estimated capital-gains tax for a hypothetical sale. */ + /** + * Estimates capital gains tax for a hypothetical sale. + * + * @param stock the stock being sold + * @param qty the quantity being sold + * @return estimated tax, or {@link BigDecimal#ZERO} if not applicable + */ private BigDecimal estimateSaleTax(final Stock stock, final BigDecimal qty) { if (player == null) { return BigDecimal.ZERO; @@ -103,11 +127,11 @@ private BigDecimal estimateSaleTax(final Stock stock, final BigDecimal qty) { return profit.multiply(TAX_RATE).setScale(2, RoundingMode.HALF_UP); } - /** - * Renders the canonical receipt layout with the given values. - * Used by both live-preview and historical-transaction modes so the - * visual output is identical. + * Renders the receipt UI using the provided computed values. + * + *

This method is shared between live preview and historical transaction rendering + * to ensure consistent visual output. */ private void renderPopulated( final boolean isBuy, @@ -145,7 +169,7 @@ private void renderPopulated( totals.getChildren().add(keyValueLine("TOTAL:", formatUsd(total))); getChildren().addAll( - headerLabel("RECEIPT"), + headerLabel(), ruleStrong(), details, ruleSoft(), @@ -154,36 +178,48 @@ private void renderPopulated( ); } - private static Label headerLabel(final String text) { - Label l = new Label(text); + /** + * Creates a styled header label. + */ + private static Label headerLabel() { + Label l = new Label("RECEIPT"); l.getStyleClass().add("receipt-header"); return l; } + /** + * Creates a strong divider line. + */ private static Region ruleStrong() { Region r = new Region(); r.getStyleClass().add("receipt-divider-strong"); return r; } + /** + * Creates a soft divider line. + */ private static Region ruleSoft() { Region r = new Region(); r.getStyleClass().add("receipt-divider"); return r; } - private static Label receiptLine(final String text) { - Label l = new Label(text); - l.getStyleClass().add("receipt-text"); - return l; - } - + /** + * Creates a formatted key-value line. + */ private static Label keyValueLine(final String key, final String value) { Label l = new Label(String.format("%-12s %s", key, value)); l.getStyleClass().add("receipt-text"); return l; } + /** + * Formats a value as USD currency. + * + * @param amount the amount to format + * @return formatted currency string + */ private static String formatUsd(final BigDecimal amount) { return "$" + String.format("%,.2f", amount); } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/StockGraph.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/StockGraph.java index 23d85f7..9a47b6c 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/StockGraph.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/components/StockGraph.java @@ -1,24 +1,28 @@ package edu.ntnu.idi.idatt2003.gruppe42.view.views.components; import edu.ntnu.idi.idatt2003.gruppe42.model.Stock; +import java.math.BigDecimal; +import java.util.List; import javafx.application.Platform; import javafx.scene.chart.AreaChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.layout.VBox; -import java.math.BigDecimal; -import java.util.List; - /** * Component for displaying a stock price history graph. + * + *

Shows an area chart based on the historical price data of a {@link Stock}. + * The graph is updated dynamically and supports toggling visibility state. */ public final class StockGraph extends VBox { private final AreaChart areaChart; private boolean isVisible = false; /** - * Constructs a new StockGraph. + * Constructs a new StockGraph component. + * + *

Initializes the chart axes, styling, and default layout configuration. */ public StockGraph() { this.getStyleClass().add("stock-graph-container"); @@ -43,9 +47,9 @@ public StockGraph() { } /** - * Updates the graph with data from the specified stock. + * Updates the graph with historical price data from the given stock. * - * @param stock the stock to display + * @param stock the stock whose price history should be displayed */ public void update(final Stock stock) { if (stock == null) { @@ -78,18 +82,18 @@ public void update(final Stock stock) { } /** - * Returns the visibility. + * Returns whether the graph is currently marked as visible. * - * @return the value + * @return true if visible, false otherwise */ public boolean getVisibility() { return isVisible; } /** - * Sets the visibility. + * Sets whether the graph is considered visible. * - * @param visible the new value + * @param visible true to mark as visible, false otherwise */ public void setVisibility(final boolean visible) { isVisible = visible; diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/CriticalFeaturesTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/CriticalFeaturesTest.java index 2b98fba..53f96b2 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/CriticalFeaturesTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/CriticalFeaturesTest.java @@ -12,7 +12,10 @@ import org.junit.jupiter.api.Test; /** - * Tests for critical features of the Millions game. + * Integration-style tests for critical gameplay features in the Millions game. + * + *

Verifies core mechanics such as debt handling, life loss, portfolio management, + * and net worth calculation across multiple model components. */ public class CriticalFeaturesTest { private Player player; @@ -46,7 +49,8 @@ void testPortfolioManagement() { assertEquals(quantity, portfolio.getShares().get(0).getQuantity(), "Quantity should match"); portfolio.removeShare(new Share(apple, new BigDecimal("5"), buyPrice)); - assertEquals(new BigDecimal("5"), portfolio.getShares().get(0).getQuantity(), "Quantity should be reduced"); + assertEquals(new BigDecimal("5"), portfolio.getShares() + .get(0).getQuantity(), "Quantity should be reduced"); } @Test @@ -63,6 +67,7 @@ void testNetWorthCalculation() { // Total Sale Value = 1000 - 10 - 55 = 935 // Net Worth = 1000 + 935 = 1935 - assertEquals(new BigDecimal("1935.00"), player.getNetWorth(), "Net worth calculation incorrect"); + assertEquals(new BigDecimal("1935.00"), + player.getNetWorth(), "Net worth calculation incorrect"); } } diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ExchangeTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ExchangeTest.java index 2bd565c..11024ec 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ExchangeTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ExchangeTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; @@ -10,6 +9,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link Exchange} class. + * + *

Verifies stock management, search functionality, and time progression + * within the exchange simulation. + */ public class ExchangeTest { private Exchange exchange; @@ -37,7 +42,7 @@ void findStocksTest() { assertEquals(1, result.size()); assertEquals("Apple Inc.", result.get(0).getCompany()); - // Case insensitive match + // Case-insensitive match result = exchange.findStocks("apple"); assertEquals(1, result.size()); assertEquals("Apple Inc.", result.get(0).getCompany()); diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PlayerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PlayerTest.java index 48cec18..fb02274 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PlayerTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PlayerTest.java @@ -10,6 +10,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link Player} class. + * + *

Verifies money management, portfolio access, exception handling, + * and net worth calculations. + */ public class PlayerTest { private Player player; @@ -76,7 +82,11 @@ void getNetworthTest() { // Starting with 10000 money and adding 5000 moneys worth of AAPL then calculating the networth // with commissions taken account of will result in this: // 10000 + 5000 - (5000 * 0.01) = 14950 - player.getPortfolio().addShare(new Share(new Stock("AAPL", "Apple Inc.", new BigDecimal("5000")), new BigDecimal("1"), new BigDecimal("5000"))); + player.getPortfolio().addShare(new Share( + new Stock("AAPL", "Apple Inc.", + new BigDecimal("5000")), + new BigDecimal("1"), + new BigDecimal("5000"))); assertEquals(new BigDecimal("14950.00"), player.getNetWorth()); } } diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PortfolioTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PortfolioTest.java index 6680e62..a71b58d 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PortfolioTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/PortfolioTest.java @@ -8,6 +8,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link Exchange} class. + * + *

Verifies stock management, search functionality, and week progression behavior. + */ public class PortfolioTest { private Portfolio portfolio; private Stock stock; diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ShareTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ShareTest.java index 44715dc..7655ba4 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ShareTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/ShareTest.java @@ -5,6 +5,12 @@ import java.math.BigDecimal; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link Share} class. + * + *

Verifies that share properties (stock, quantity, and purchase price) + * are correctly stored and retrieved. + */ public class ShareTest { @Test void sharePropertiesTest() { diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockFileHandlerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockFileHandlerTest.java index 6e3af16..d61bcb4 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockFileHandlerTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockFileHandlerTest.java @@ -1,7 +1,6 @@ package edu.ntnu.idi.idatt2003.gruppe42.model; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Files; @@ -9,6 +8,12 @@ import java.util.List; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link StockFileHandler} class. + * + *

Verifies correct parsing of stock data from CSV files, + * including handling of valid entries, comments, and empty lines. + */ public class StockFileHandlerTest { @Test diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockTest.java index 7309f59..b90aebf 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/StockTest.java @@ -5,6 +5,11 @@ import java.math.BigDecimal; import org.junit.jupiter.api.Test; +/** + * Unit tests for the {@link Stock} class. + * + *

Verifies correct storage of stock properties and updates to sales price. + */ public class StockTest { @Test void stockPropertiesTest() {