From 2959947acc121330f7b234a2a9ccb6087ae06426 Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Wed, 20 Dec 2023 13:16:44 -0500 Subject: [PATCH] Added scene router, main view for the entire app, and more preparations for a multi-page application. --- .../com/andrewlalis/perfin/PerfinApp.java | 11 ++++- .../control/AccountsViewController.java | 29 +++++++++-- .../perfin/control/MainViewController.java | 24 +++++++++ .../perfin/data/AccountRepository.java | 2 + .../data/impl/JdbcAccountRepository.java | 7 +++ .../andrewlalis/perfin/view/SceneRouter.java | 49 +++++++++++++++++++ src/main/resources/accounts-view.fxml | 40 +++++++++------ src/main/resources/edit-account.fxml | 33 +++++++++++++ src/main/resources/main-view.fxml | 26 ++++++++++ src/main/resources/style/accounts-view.css | 10 +++- src/main/resources/style/main-view.css | 7 +++ 11 files changed, 216 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/andrewlalis/perfin/control/MainViewController.java create mode 100644 src/main/java/com/andrewlalis/perfin/view/SceneRouter.java create mode 100644 src/main/resources/edit-account.fxml create mode 100644 src/main/resources/main-view.fxml create mode 100644 src/main/resources/style/main-view.css diff --git a/src/main/java/com/andrewlalis/perfin/PerfinApp.java b/src/main/java/com/andrewlalis/perfin/PerfinApp.java index dcbb876..918dd4a 100644 --- a/src/main/java/com/andrewlalis/perfin/PerfinApp.java +++ b/src/main/java/com/andrewlalis/perfin/PerfinApp.java @@ -1,10 +1,14 @@ package com.andrewlalis.perfin; +import com.andrewlalis.perfin.control.MainViewController; +import com.andrewlalis.perfin.view.SceneRouter; import com.andrewlalis.perfin.view.SplashScreenStage; import javafx.application.Application; +import javafx.scene.Scene; import javafx.stage.Stage; import java.nio.file.Path; +import java.util.function.Consumer; /** * The class from which the JavaFX-based application starts. @@ -29,7 +33,12 @@ public class PerfinApp extends Application { private void initMainScreen(Stage stage) { stage.hide(); - stage.setScene(SceneUtil.load("/accounts-view.fxml")); + Scene mainViewScene = SceneUtil.load("/main-view.fxml", (Consumer) c -> { + c.router = new SceneRouter(c.mainContainer::setCenter) + .map("accounts", "/accounts-view.fxml") + .map("edit-account", "/edit-account.fxml"); + }); + stage.setScene(mainViewScene); stage.setTitle("Perfin"); } } \ No newline at end of file diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java index 9fe8edb..86dc784 100644 --- a/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java @@ -4,13 +4,17 @@ import com.andrewlalis.perfin.SceneUtil; import com.andrewlalis.perfin.model.Account; import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.view.BindingUtil; +import javafx.application.Platform; +import javafx.beans.property.SimpleListProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.Scene; +import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; +import javafx.stage.Stage; -import java.util.List; import java.util.function.Consumer; public class AccountsViewController { @@ -18,27 +22,42 @@ public class AccountsViewController { public BorderPane mainContainer; @FXML public FlowPane accountsPane; + @FXML + public Label noAccountsLabel; private final ObservableList accountsList = FXCollections.observableArrayList(); @FXML public void initialize() { + // Sync the size of the accounts pane to its container. accountsPane.minWidthProperty().bind(mainContainer.widthProperty()); accountsPane.prefWidthProperty().bind(mainContainer.widthProperty()); accountsPane.prefWrapLengthProperty().bind(mainContainer.widthProperty()); accountsPane.maxWidthProperty().bind(mainContainer.widthProperty()); + // Map each account in our list to an account tile element. BindingUtil.mapContent(accountsPane.getChildren(), accountsList, account -> SceneUtil.loadNode( "/account-tile.fxml", (Consumer) c -> c.setAccount(account) )); + + // Show the "no accounts" label when the accountsList is empty. + var listProp = new SimpleListProperty<>(accountsList); + noAccountsLabel.visibleProperty().bind(listProp.emptyProperty()); + noAccountsLabel.managedProperty().bind(noAccountsLabel.visibleProperty()); + accountsPane.visibleProperty().bind(listProp.emptyProperty().not()); + accountsPane.managedProperty().bind(accountsPane.visibleProperty()); + + // Populate the list of accounts once a profile is available. Profile.whenLoaded(profile -> { - populateAccounts(profile.getDataSource().getAccountRepository().findAll()); + accountsList.setAll(profile.getDataSource().getAccountRepository().findAll()); }); } - private void populateAccounts(List accounts) { - this.accountsList.clear(); - this.accountsList.addAll(accounts); + @FXML + public void createNewAccount() { + Stage mainStage = (Stage) mainContainer.getScene().getWindow(); + Scene editAccountScene = SceneUtil.load("/edit-account.fxml"); + mainStage.setScene(editAccountScene); } } diff --git a/src/main/java/com/andrewlalis/perfin/control/MainViewController.java b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java new file mode 100644 index 0000000..af7fb5d --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java @@ -0,0 +1,24 @@ +package com.andrewlalis.perfin.control; + +import com.andrewlalis.perfin.view.SceneRouter; +import javafx.fxml.FXML; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; + +public class MainViewController { + public SceneRouter router; + @FXML + public BorderPane mainContainer; + @FXML + public HBox mainFooter; + + @FXML + public void goToAccounts() { + router.goTo("accounts"); + } + + @FXML + public void goToEditAccounts() { + router.goTo("edit-account"); + } +} diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java index 4ba0d49..d565045 100644 --- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java @@ -2,6 +2,7 @@ package com.andrewlalis.perfin.data; import com.andrewlalis.perfin.model.Account; +import java.math.BigDecimal; import java.util.List; import java.util.Optional; @@ -9,4 +10,5 @@ public interface AccountRepository { long insert(Account account); List findAll(); Optional findById(long id); + BigDecimal deriveCurrentBalance(long id); } diff --git a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java index 1b0be21..9d47453 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java @@ -5,6 +5,7 @@ import com.andrewlalis.perfin.data.DbUtil; import com.andrewlalis.perfin.model.Account; import com.andrewlalis.perfin.model.AccountType; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,6 +40,12 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor return DbUtil.findById(conn, "SELECT * FROM account WHERE id = ?", id, JdbcAccountRepository::parseAccount); } + @Override + public BigDecimal deriveCurrentBalance(long id) { + // TODO: Implement this! + return BigDecimal.valueOf(0, 4); + } + private static Account parseAccount(ResultSet rs) throws SQLException { long id = rs.getLong("id"); LocalDateTime createdAt = DbUtil.utcLDTFromTimestamp(rs.getTimestamp("created_at")); diff --git a/src/main/java/com/andrewlalis/perfin/view/SceneRouter.java b/src/main/java/com/andrewlalis/perfin/view/SceneRouter.java new file mode 100644 index 0000000..96a925c --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/SceneRouter.java @@ -0,0 +1,49 @@ +package com.andrewlalis.perfin.view; + +import com.andrewlalis.perfin.SceneUtil; +import javafx.application.Platform; +import javafx.scene.Parent; +import javafx.scene.layout.Pane; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class SceneRouter { + private final Consumer setter; + private final Map routeMap = new HashMap<>(); + public Object context = null; + + public SceneRouter(Pane pane) { + this(p -> pane.getChildren().setAll(p)); + } + + public SceneRouter(Consumer setter) { + this.setter = setter; + } + + public SceneRouter map(String route, Parent scene) { + routeMap.put(route, scene); + return this; + } + + public SceneRouter map(String route, String fxml) { + return map(route, SceneUtil.loadNode(fxml)); + } + + public void goTo(String route, Object context) { + Parent node = routeMap.get(route); + if (node == null) throw new IllegalArgumentException("Route " + route + " is not mapped to any node."); + this.context = context; + Platform.runLater(() -> setter.accept(node)); + } + + public void goTo(String route) { + goTo(route, null); + } + + @SuppressWarnings("unchecked") + public T getContext() { + return (T) context; + } +} diff --git a/src/main/resources/accounts-view.fxml b/src/main/resources/accounts-view.fxml index 13c74e8..8406b08 100644 --- a/src/main/resources/accounts-view.fxml +++ b/src/main/resources/accounts-view.fxml @@ -1,8 +1,12 @@ - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + +
- + + +
diff --git a/src/main/resources/edit-account.fxml b/src/main/resources/edit-account.fxml new file mode 100644 index 0000000..db7302f --- /dev/null +++ b/src/main/resources/edit-account.fxml @@ -0,0 +1,33 @@ + + + + + + + + +
+ + + + + + +
+ + +