Added properties pane and improved the account view.

This commit is contained in:
Andrew Lalis 2024-01-04 12:37:16 -05:00
parent 32fb7e8eb8
commit 4c69cd1662
7 changed files with 151 additions and 71 deletions

View File

@ -7,9 +7,15 @@ import com.andrewlalis.perfin.model.Profile;
import com.andrewlalis.perfin.model.history.AccountHistoryItem;
import com.andrewlalis.perfin.view.component.AccountHistoryItemTile;
import javafx.application.Platform;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import java.time.LocalDateTime;
@ -21,27 +27,44 @@ 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;
@FXML public TextField accountBalanceField;
@FXML public Label accountNameLabel;
@FXML public Label accountNumberLabel;
@FXML public Label accountCurrencyLabel;
@FXML public Label accountCreatedAtLabel;
@FXML public Label accountBalanceLabel;
@FXML public BooleanProperty accountArchivedProperty = new SimpleBooleanProperty(false);
@FXML public VBox historyItemsVBox;
@FXML public Button loadMoreHistoryButton;
private LocalDateTime loadHistoryFrom;
private final int historyLoadSize = 5;
@FXML public VBox actionsVBox;
@FXML public void initialize() {
actionsVBox.getChildren().forEach(node -> {
Button button = (Button) node;
BooleanExpression buttonActive = accountArchivedProperty;
if (button.getText().equalsIgnoreCase("Unarchive")) {
buttonActive = buttonActive.not();
}
button.disableProperty().bind(buttonActive);
button.managedProperty().bind(button.visibleProperty());
button.visibleProperty().bind(button.disableProperty().not());
});
}
@Override
public void onRouteSelected(Object context) {
account = (Account) context;
accountArchivedProperty.set(account.isArchived());
titleLabel.setText("Account #" + account.id);
accountNameField.setText(account.getName());
accountNumberField.setText(account.getAccountNumber());
accountCurrencyField.setText(account.getCurrency().getDisplayName());
accountCreatedAtField.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceField::setText);
accountNameLabel.setText(account.getName());
accountNumberLabel.setText(account.getAccountNumber());
accountCurrencyLabel.setText(account.getCurrency().getDisplayName());
accountCreatedAtLabel.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceLabel::setText);
reloadHistory();
}
@ -73,12 +96,16 @@ public class AccountViewController implements RouteSelectionListener {
"later if you need to."
).showAndWait();
if (confirmResult.isPresent() && confirmResult.get() == ButtonType.OK) {
Profile.getCurrent().getDataSource().useAccountRepository(repo -> repo.archive(account));
Profile.getCurrent().getDataSource().useAccountRepository(repo -> repo.archive(account.id));
router.getHistory().clear();
router.navigate("accounts");
}
}
@FXML public void unarchiveAccount() {
System.out.println("Unarchiving");
}
@FXML
public void deleteAccount() {
var confirmResult = new Alert(

View File

@ -21,7 +21,8 @@ public interface AccountRepository extends AutoCloseable {
void updateName(long id, String name);
void update(Account account);
void delete(Account account);
void archive(Account account);
void archive(long accountId);
void unarchive(long accountId);
BigDecimal deriveBalance(long accountId, Instant timestamp);
default BigDecimal deriveCurrentBalance(long accountId) {

View File

@ -147,8 +147,19 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
}
@Override
public void archive(Account account) {
DbUtil.updateOne(conn, "UPDATE account SET archived = TRUE WHERE id = ?", List.of(account.id));
public void archive(long accountId) {
DbUtil.doTransaction(conn, () -> {
DbUtil.updateOne(conn, "UPDATE account SET archived = TRUE WHERE id = ?", List.of(accountId));
new JdbcAccountHistoryItemRepository(conn).recordText(DateUtil.nowAsUTC(), accountId, "Account has been archived.");
});
}
@Override
public void unarchive(long accountId) {
DbUtil.doTransaction(conn, () -> {
DbUtil.updateOne(conn, "UPDATE account SET archived = FALSE WHERE id = ?", List.of(accountId));
new JdbcAccountHistoryItemRepository(conn).recordText(DateUtil.nowAsUTC(), accountId, "Account has been unarchived.");
});
}
public static Account parseAccount(ResultSet rs) throws SQLException {

View File

@ -10,11 +10,9 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.*;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextFlow;
@ -54,9 +52,11 @@ public class DataSourcePaginationControls extends BorderPane {
maxPagesText.managedProperty().bind(maxPagesText.visibleProperty());
maxPagesText.visibleProperty().bind(maxPages.greaterThan(0));
TextFlow pageText = new TextFlow(new Text("Page "), currentPageLabel, maxPagesText);
pageText.setTextAlignment(TextAlignment.CENTER);
BorderPane pageTextContainer = new BorderPane(pageText);
BorderPane.setAlignment(pageText, Pos.CENTER);
AnchorPane pageTextContainer = new AnchorPane(pageText);
AnchorPane.setTopAnchor(pageText, 4.0);
AnchorPane.setRightAnchor(pageText, 0.0);
AnchorPane.setBottomAnchor(pageText, 0.0);
AnchorPane.setLeftAnchor(pageText, 0.0);
Button previousPageButton = new Button("Previous Page");

View File

@ -0,0 +1,55 @@
package com.andrewlalis.perfin.view.component;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import java.util.ArrayList;
import java.util.List;
/**
* A specially-formatted {@link GridPane} that arranges its children into a
* two-column grid representing key-value pairs.
*/
public class PropertiesPane extends GridPane {
public PropertiesPane() {
ColumnConstraints keyConstraints = new ColumnConstraints();
keyConstraints.setHgrow(Priority.NEVER);
keyConstraints.setHalignment(HPos.LEFT);
keyConstraints.setMinWidth(10.0);
ColumnConstraints valueConstraints = new ColumnConstraints();
valueConstraints.setHgrow(Priority.ALWAYS);
valueConstraints.setHalignment(HPos.LEFT);
valueConstraints.setMinWidth(10.0);
getColumnConstraints().setAll(keyConstraints, valueConstraints);
}
@Override
protected void layoutChildren() {
// Apply grid positioning to all children in the order in which they appear, like so:
// key 1 value 1
// key 2 value 2
// ... and so on.
int rowCount = getManagedChildren().size() / 2;
List<RowConstraints> rows = new ArrayList<>(rowCount);
for (int i = 0; i < rowCount; i++) {
RowConstraints c = new RowConstraints();
c.setValignment(VPos.TOP);
c.setVgrow(Priority.NEVER);
rows.add(c);
}
getRowConstraints().setAll(rows);
for (int i = 0; i < getManagedChildren().size(); i++) {
Node child = getManagedChildren().get(i);
int column = i % 2;
int row = i / 2;
GridPane.setRowIndex(child, row);
GridPane.setColumnIndex(child, column);
}
super.layoutChildren();
}
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<BorderPane
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.andrewlalis.perfin.control.AccountViewController"
stylesheets="@style/account-view.css,@style/base.css"
stylesheets="@style/base.css"
>
<top>
<HBox styleClass="std-padding,std-spacing">
<Label fx:id="titleLabel" styleClass="large-text,bold-text"/>
</HBox>
<Label fx:id="titleLabel" styleClass="std-padding,large-text,bold-text"/>
</top>
<center>
<VBox>
@ -19,50 +19,53 @@
<BorderPane>
<center>
<VBox styleClass="std-padding,std-spacing">
<HBox>
<FlowPane>
<!-- Main account properties. -->
<VBox HBox.hgrow="SOMETIMES">
<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" styleClass="mono-font"/>
</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 styleClass="account-property-box">
<Label text="Current Balance"/>
<TextField fx:id="accountBalanceField" editable="false"/>
</VBox>
<PropertiesPane vgap="5" hgap="5">
<Label text="Name" styleClass="bold-text"/>
<Label fx:id="accountNameLabel"/>
<Label text="Number" styleClass="bold-text"/>
<Label fx:id="accountNumberLabel" styleClass="mono-font"/>
<Label text="Currency" styleClass="bold-text"/>
<Label fx:id="accountCurrencyLabel"/>
<Label text="Created At" styleClass="bold-text"/>
<Label fx:id="accountCreatedAtLabel" styleClass="mono-font"/>
<VBox>
<Label text="Current Balance" styleClass="bold-text" fx:id="balanceLabel"/>
<Text
style="-fx-font-size: x-small; -fx-fill: grey"
wrappingWidth="${balanceLabel.width}"
>Computed using the last recorded balance and all transactions since.</Text>
</VBox>
<Label fx:id="accountBalanceLabel" styleClass="mono-font"/>
</PropertiesPane>
<VBox HBox.hgrow="SOMETIMES">
<Label text="Panel 2"/>
</VBox>
</HBox>
</FlowPane>
</VBox>
</center>
<right>
<VBox styleClass="std-padding,std-spacing">
<Label text="Actions" styleClass="bold-text"/>
<VBox fx:id="actionsVBox" styleClass="std-spacing">
<Button text="Edit" onAction="#goToEditPage"/>
<Button text="Record Balance" onAction="#goToCreateBalanceRecord"/>
<Button text="Archive" onAction="#archiveAccount"/>
<Button text="Delete" onAction="#deleteAccount"/>
<Button text="Unarchive" onAction="#unarchiveAccount"/>
</VBox>
</VBox>
</right>
</BorderPane>
<!-- Account history -->
<VBox VBox.vgrow="ALWAYS">
<Label text="History" styleClass="bold-text"/>
<Label text="History" styleClass="bold-text,std-padding"/>
<VBox>
<ScrollPane fitToHeight="true" fitToWidth="true">
<VBox fx:id="historyItemsVBox" style="-fx-padding: 10px; -fx-spacing: 10px;"/>

View File

@ -1,17 +0,0 @@
.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: 100px;
-fx-max-width: 300px;
}
.actions-box > Button {
-fx-max-width: 500px;
}