Added account view, functional prototype for editing accounts.

This commit is contained in:
Andrew Lalis 2023-12-24 11:47:38 -05:00
parent 23a0a20acb
commit b1043c1624
15 changed files with 301 additions and 45 deletions

View File

@ -29,7 +29,7 @@
<dependency> <dependency>
<groupId>com.andrewlalis</groupId> <groupId>com.andrewlalis</groupId>
<artifactId>javafx-scene-router</artifactId> <artifactId>javafx-scene-router</artifactId>
<version>1.1.0</version> <version>1.3.0</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,14 +1,12 @@
package com.andrewlalis.perfin; package com.andrewlalis.perfin;
import com.andrewlalis.javafx_scene_router.SceneRouter; import com.andrewlalis.javafx_scene_router.SceneRouter;
import com.andrewlalis.perfin.control.MainViewController;
import com.andrewlalis.perfin.view.SplashScreenStage; import com.andrewlalis.perfin.view.SplashScreenStage;
import javafx.application.Application; import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.Consumer;
/** /**
* The class from which the JavaFX-based application starts. * The class from which the JavaFX-based application starts.
@ -39,16 +37,14 @@ public class PerfinApp extends Application {
private void initMainScreen(Stage stage) { private void initMainScreen(Stage stage) {
stage.hide(); stage.hide();
Scene mainViewScene = SceneUtil.load("/main-view.fxml", (Consumer<MainViewController>) c -> { Scene mainViewScene = SceneUtil.load("/main-view.fxml");
c.mainContainer.setCenter(router.getViewPane());
router.navigate("accounts");
});
stage.setScene(mainViewScene); stage.setScene(mainViewScene);
stage.setTitle("Perfin"); stage.setTitle("Perfin");
} }
private static void defineRoutes() { private static void defineRoutes() {
router.map("accounts", SceneUtil.loadNode("/accounts-view.fxml")); router.map("accounts", PerfinApp.class.getResource("/accounts-view.fxml"));
router.map("edit-account", SceneUtil.loadNode("/edit-account.fxml")); router.map("account", PerfinApp.class.getResource("/account-view.fxml"));
router.map("edit-account", PerfinApp.class.getResource("/edit-account.fxml"));
} }
} }

View File

@ -8,6 +8,8 @@ import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import static com.andrewlalis.perfin.PerfinApp.router;
public class AccountTileController { public class AccountTileController {
private Account account; private Account account;
@ -37,6 +39,7 @@ public class AccountTileController {
accountNameLabel.setText(account.getName()); accountNameLabel.setText(account.getName());
container.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { container.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
System.out.println("Clicked on " + account.getAccountNumber()); System.out.println("Clicked on " + account.getAccountNumber());
router.navigate("account", account);
}); });
}); });
} }

View File

@ -0,0 +1,36 @@
package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.model.Account;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class AccountViewController implements RouteSelectionListener {
private Account account;
@FXML
public Label titleLabel;
@FXML
public TextField accountNameField;
@FXML
public TextField accountNumberField;
@FXML
public TextField accountCreatedAtField;
@FXML
public TextField accountCurrencyField;
@Override
public void onRouteSelected(Object context) {
account = (Account) context;
titleLabel.setText("Account: " + account.getAccountNumber());
accountNameField.setText(account.getName());
accountNumberField.setText(account.getAccountNumber());
accountCurrencyField.setText(account.getCurrency().getDisplayName());
accountCreatedAtField.setText(account.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}

View File

@ -1,23 +1,23 @@
package com.andrewlalis.perfin.control; package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.SceneUtil; import com.andrewlalis.perfin.SceneUtil;
import com.andrewlalis.perfin.model.Account; import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.model.Profile;
import com.andrewlalis.perfin.view.BindingUtil; import com.andrewlalis.perfin.view.BindingUtil;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane; import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import java.util.function.Consumer; import java.util.function.Consumer;
public class AccountsViewController { import static com.andrewlalis.perfin.PerfinApp.router;
public class AccountsViewController implements RouteSelectionListener {
@FXML @FXML
public BorderPane mainContainer; public BorderPane mainContainer;
@FXML @FXML
@ -47,17 +47,21 @@ public class AccountsViewController {
noAccountsLabel.managedProperty().bind(noAccountsLabel.visibleProperty()); noAccountsLabel.managedProperty().bind(noAccountsLabel.visibleProperty());
accountsPane.visibleProperty().bind(listProp.emptyProperty().not()); accountsPane.visibleProperty().bind(listProp.emptyProperty().not());
accountsPane.managedProperty().bind(accountsPane.visibleProperty()); accountsPane.managedProperty().bind(accountsPane.visibleProperty());
// Populate the list of accounts once a profile is available.
Profile.whenLoaded(profile -> {
accountsList.setAll(profile.getDataSource().getAccountRepository().findAll());
});
} }
@FXML @FXML
public void createNewAccount() { public void createNewAccount() {
Stage mainStage = (Stage) mainContainer.getScene().getWindow(); router.navigate("edit-account");
Scene editAccountScene = SceneUtil.load("/edit-account.fxml"); }
mainStage.setScene(editAccountScene);
@Override
public void onRouteSelected(Object context) {
refreshAccounts();
}
public void refreshAccounts() {
Profile.whenLoaded(profile -> {
accountsList.setAll(profile.getDataSource().getAccountRepository().findAll());
});
} }
} }

View File

@ -0,0 +1,93 @@
package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.AccountType;
import com.andrewlalis.perfin.model.Profile;
import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import java.util.Currency;
import static com.andrewlalis.perfin.PerfinApp.router;
public class EditAccountController implements RouteSelectionListener {
private Account account;
@FXML
public Label titleLabel;
@FXML
public TextField accountNameField;
@FXML
public TextField accountNumberField;
@FXML
public ComboBox<Currency> accountCurrencyComboBox;
@FXML
public ChoiceBox<String> accountTypeChoiceBox;
@FXML
public void initialize() {
final String[] currencies = {
"USD",
"EUR",
"GBP"
};
for (String currencyCode : currencies) {
accountCurrencyComboBox.getItems().add(Currency.getInstance(currencyCode));
}
accountCurrencyComboBox.getSelectionModel().select(Currency.getInstance("USD"));
accountTypeChoiceBox.getItems().add("Checking");
accountTypeChoiceBox.getItems().add("Savings");
accountTypeChoiceBox.getItems().add("Credit Card");
accountTypeChoiceBox.getSelectionModel().select("Checking");
}
@Override
public void onRouteSelected(Object context) {
this.account = (Account) context;
if (account == null) {
titleLabel.setText("Editing New Account");
} else {
titleLabel.setText("Editing Account: " + account.getName());
}
resetForm();
}
@FXML
public void save() {
if (account == null) {
// If we're editing a new account.
String name = accountNameField.getText().strip();
String number = accountNumberField.getText().strip();
AccountType type = AccountType.parse(accountTypeChoiceBox.getValue());
Currency currency = accountCurrencyComboBox.getValue();
Account newAccount = new Account(type, number, name, currency);
Profile.getCurrent().getDataSource().getAccountRepository().insert(newAccount);
// Once we create the new account, go to the account.
router.getHistory().clear();
router.navigate("accounts");
} else {
throw new IllegalStateException("Not implemented.");
}
}
@FXML
public void cancel() {
router.navigateBack();
}
public void resetForm() {
if (account == null) {
accountNameField.setText("");
accountNumberField.setText("");
accountTypeChoiceBox.getSelectionModel().selectFirst();
accountCurrencyComboBox.getSelectionModel().select(Currency.getInstance("USD"));
} else {
// TODO: Set to original account.
}
}
}

View File

@ -1,7 +1,8 @@
package com.andrewlalis.perfin.control; package com.andrewlalis.perfin.control;
import javafx.event.ActionEvent; import com.andrewlalis.perfin.view.BindingUtil;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
@ -12,15 +13,30 @@ public class MainViewController {
public BorderPane mainContainer; public BorderPane mainContainer;
@FXML @FXML
public HBox mainFooter; public HBox mainFooter;
@FXML
public HBox breadcrumbHBox;
@FXML @FXML
public void goToAccounts() { public void initialize() {
mainContainer.setCenter(router.getViewPane());
BindingUtil.mapContent(
breadcrumbHBox.getChildren(),
router.getBreadCrumbs(),
breadCrumb -> {
Label label = new Label("> " + breadCrumb.route());
if (breadCrumb.current()) {
label.setStyle("-fx-font-weight: bold");
}
return label;
}
);
router.navigate("accounts"); router.navigate("accounts");
} }
@FXML @FXML
public void goToEditAccounts() { public void goToAccounts() {
router.navigate("edit-account"); router.navigate("accounts");
} }
@FXML @FXML

View File

@ -47,4 +47,12 @@ public class Account {
public Currency getCurrency() { public Currency getCurrency() {
return currency; return currency;
} }
public LocalDateTime getCreatedAt() {
return createdAt;
}
public long getId() {
return id;
}
} }

View File

@ -3,5 +3,15 @@ package com.andrewlalis.perfin.model;
public enum AccountType { public enum AccountType {
CHECKING, CHECKING,
SAVINGS, SAVINGS,
CREDIT_CARD CREDIT_CARD;
public static AccountType parse(String s) {
s = s.strip().toUpperCase();
return switch (s) {
case "CHECKING" -> CHECKING;
case "SAVINGS" -> SAVINGS;
case "CREDIT CARD", "CREDITCARD" -> CREDIT_CARD;
default -> throw new IllegalArgumentException("Invalid AccountType string: " + s);
};
}
} }

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.andrewlalis.perfin.control.AccountViewController"
stylesheets="@style/account-view.css"
styleClass="main-container"
>
<top>
<Label fx:id="titleLabel"/>
</top>
<center>
<VBox>
<VBox styleClass="account-property-box">
<Label text="Name"/>
<TextField fx:id="accountNameField" editable="false"/>
</VBox>
<VBox styleClass="account-property-box">
<Label text="Number"/>
<TextField fx:id="accountNumberField" editable="false"/>
</VBox>
<VBox styleClass="account-property-box">
<Label text="Currency"/>
<TextField fx:id="accountCurrencyField" editable="false"/>
</VBox>
<VBox styleClass="account-property-box">
<Label text="Created At"/>
<TextField fx:id="accountCreatedAtField" editable="false"/>
</VBox>
</VBox>
</center>
</BorderPane>

View File

@ -2,32 +2,39 @@
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<BorderPane
<BorderPane xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1"> xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.andrewlalis.perfin.control.EditAccountController"
stylesheets="@style/edit-account.css"
styleClass="main-container"
>
<top> <top>
<Label text="Edit Account"/> <Label fx:id="titleLabel" text="Edit Account"/>
</top> </top>
<center> <center>
<GridPane BorderPane.alignment="CENTER"> <GridPane BorderPane.alignment="CENTER" styleClass="fields-grid">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="NEVER" minWidth="10.0" /> <ColumnConstraints hgrow="NEVER" minWidth="10.0" />
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" /> <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
</columnConstraints> </columnConstraints>
<Label GridPane.columnIndex="0" GridPane.rowIndex="0" text="Name"/> <Label GridPane.columnIndex="0" GridPane.rowIndex="0" text="Name"/>
<TextField GridPane.columnIndex="1" GridPane.rowIndex="0" text="Bleh"/> <TextField fx:id="accountNameField" GridPane.columnIndex="1" GridPane.rowIndex="0" text="Bleh"/>
<Label GridPane.columnIndex="0" GridPane.rowIndex="1" text="Account Number"/> <Label GridPane.columnIndex="0" GridPane.rowIndex="1" text="Account Number"/>
<TextField GridPane.columnIndex="1" GridPane.rowIndex="1" text="1234"/> <TextField fx:id="accountNumberField" GridPane.columnIndex="1" GridPane.rowIndex="1" text="1234"/>
<Label GridPane.columnIndex="0" GridPane.rowIndex="2" text="Currency"/> <Label GridPane.columnIndex="0" GridPane.rowIndex="2" text="Currency"/>
<ChoiceBox GridPane.columnIndex="1" GridPane.rowIndex="2"> <ComboBox fx:id="accountCurrencyComboBox" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
</ChoiceBox>
<Label GridPane.columnIndex="0" GridPane.rowIndex="3" text="Account Type"/>
<ChoiceBox fx:id="accountTypeChoiceBox" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
</GridPane> </GridPane>
</center> </center>
<bottom> <bottom>
<FlowPane BorderPane.alignment="CENTER_RIGHT"> <FlowPane BorderPane.alignment="CENTER_RIGHT">
<Button text="Save" /> <Button text="Save" onAction="#save"/>
<Button text="Cancel" /> <Button text="Cancel" onAction="#cancel"/>
</FlowPane> </FlowPane>
</bottom> </bottom>
</BorderPane> </BorderPane>

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.*?>
<?import javafx.scene.layout.HBox?>
<BorderPane <BorderPane
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
@ -11,13 +10,16 @@
stylesheets="@style/main-view.css" stylesheets="@style/main-view.css"
> >
<top> <top>
<VBox>
<HBox fx:id="mainHeader"> <HBox fx:id="mainHeader">
<Label text="TODO: Add breadcrumb navigation/header here!"/>
<Button text="Back" onAction="#goBack"/> <Button text="Back" onAction="#goBack"/>
<Button text="Forward" onAction="#goForward"/> <Button text="Forward" onAction="#goForward"/>
<Button text="Go to accounts!" onAction="#goToAccounts"/> <Button text="Accounts" onAction="#goToAccounts"/>
<Button text="Go to edit accounts!" onAction="#goToEditAccounts"/>
</HBox> </HBox>
<HBox fx:id="breadcrumbHBox"/>
<Separator/>
</VBox>
</top> </top>
<bottom> <bottom>
<HBox fx:id="mainFooter" spacing="5"> <HBox fx:id="mainFooter" spacing="5">

View File

@ -0,0 +1,25 @@
.main-container {
-fx-padding: 5px;
}
#titleLabel {
-fx-font-weight: bold;
-fx-font-size: large;
}
.account-property-box {
-fx-padding: 5px 0 5px 0;
-fx-spacing: 3px;
}
.account-property-box > Label {
-fx-font-weight: bold;
}
.account-property-box > TextField {
-fx-min-width: 200px;
}
#accountNumberField {
-fx-font-family: monospace;
}

View File

@ -0,0 +1,14 @@
.main-container {
-fx-padding: 5px;
}
.fields-grid {
-fx-hgap: 3px;
-fx-vgap: 3px;
-fx-padding: 5px;
}
#titleLabel {
-fx-font-size: large;
-fx-font-weight: bold;
}

View File

@ -1,5 +1,11 @@
#mainHeader { #mainHeader {
-fx-padding: 3px; -fx-padding: 3px;
-fx-spacing: 3px;
}
#breadcrumbHBox {
-fx-spacing: 3px;
-fx-font-size: small;
} }
#mainFooter { #mainFooter {