Added properties pane and improved the account view.
This commit is contained in:
parent
32fb7e8eb8
commit
4c69cd1662
|
@ -7,9 +7,15 @@ import com.andrewlalis.perfin.model.Profile;
|
||||||
import com.andrewlalis.perfin.model.history.AccountHistoryItem;
|
import com.andrewlalis.perfin.model.history.AccountHistoryItem;
|
||||||
import com.andrewlalis.perfin.view.component.AccountHistoryItemTile;
|
import com.andrewlalis.perfin.view.component.AccountHistoryItemTile;
|
||||||
import javafx.application.Platform;
|
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.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
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 javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
@ -21,27 +27,44 @@ public class AccountViewController implements RouteSelectionListener {
|
||||||
private Account account;
|
private Account account;
|
||||||
|
|
||||||
@FXML public Label titleLabel;
|
@FXML public Label titleLabel;
|
||||||
@FXML public TextField accountNameField;
|
|
||||||
@FXML public TextField accountNumberField;
|
@FXML public Label accountNameLabel;
|
||||||
@FXML public TextField accountCreatedAtField;
|
@FXML public Label accountNumberLabel;
|
||||||
@FXML public TextField accountCurrencyField;
|
@FXML public Label accountCurrencyLabel;
|
||||||
@FXML public TextField accountBalanceField;
|
@FXML public Label accountCreatedAtLabel;
|
||||||
|
@FXML public Label accountBalanceLabel;
|
||||||
|
@FXML public BooleanProperty accountArchivedProperty = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
@FXML public VBox historyItemsVBox;
|
@FXML public VBox historyItemsVBox;
|
||||||
@FXML public Button loadMoreHistoryButton;
|
@FXML public Button loadMoreHistoryButton;
|
||||||
private LocalDateTime loadHistoryFrom;
|
private LocalDateTime loadHistoryFrom;
|
||||||
private final int historyLoadSize = 5;
|
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
|
@Override
|
||||||
public void onRouteSelected(Object context) {
|
public void onRouteSelected(Object context) {
|
||||||
account = (Account) context;
|
account = (Account) context;
|
||||||
|
accountArchivedProperty.set(account.isArchived());
|
||||||
titleLabel.setText("Account #" + account.id);
|
titleLabel.setText("Account #" + account.id);
|
||||||
|
accountNameLabel.setText(account.getName());
|
||||||
accountNameField.setText(account.getName());
|
accountNumberLabel.setText(account.getAccountNumber());
|
||||||
accountNumberField.setText(account.getAccountNumber());
|
accountCurrencyLabel.setText(account.getCurrency().getDisplayName());
|
||||||
accountCurrencyField.setText(account.getCurrency().getDisplayName());
|
accountCreatedAtLabel.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
|
||||||
accountCreatedAtField.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
|
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceLabel::setText);
|
||||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceField::setText);
|
|
||||||
|
|
||||||
reloadHistory();
|
reloadHistory();
|
||||||
}
|
}
|
||||||
|
@ -73,12 +96,16 @@ public class AccountViewController implements RouteSelectionListener {
|
||||||
"later if you need to."
|
"later if you need to."
|
||||||
).showAndWait();
|
).showAndWait();
|
||||||
if (confirmResult.isPresent() && confirmResult.get() == ButtonType.OK) {
|
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.getHistory().clear();
|
||||||
router.navigate("accounts");
|
router.navigate("accounts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML public void unarchiveAccount() {
|
||||||
|
System.out.println("Unarchiving");
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void deleteAccount() {
|
public void deleteAccount() {
|
||||||
var confirmResult = new Alert(
|
var confirmResult = new Alert(
|
||||||
|
|
|
@ -21,7 +21,8 @@ public interface AccountRepository extends AutoCloseable {
|
||||||
void updateName(long id, String name);
|
void updateName(long id, String name);
|
||||||
void update(Account account);
|
void update(Account account);
|
||||||
void delete(Account account);
|
void delete(Account account);
|
||||||
void archive(Account account);
|
void archive(long accountId);
|
||||||
|
void unarchive(long accountId);
|
||||||
|
|
||||||
BigDecimal deriveBalance(long accountId, Instant timestamp);
|
BigDecimal deriveBalance(long accountId, Instant timestamp);
|
||||||
default BigDecimal deriveCurrentBalance(long accountId) {
|
default BigDecimal deriveCurrentBalance(long accountId) {
|
||||||
|
|
|
@ -147,8 +147,19 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void archive(Account account) {
|
public void archive(long accountId) {
|
||||||
DbUtil.updateOne(conn, "UPDATE account SET archived = TRUE WHERE id = ?", List.of(account.id));
|
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 {
|
public static Account parseAccount(ResultSet rs) throws SQLException {
|
||||||
|
|
|
@ -10,11 +10,9 @@ import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextAlignment;
|
import javafx.scene.text.TextAlignment;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
|
@ -54,9 +52,11 @@ public class DataSourcePaginationControls extends BorderPane {
|
||||||
maxPagesText.managedProperty().bind(maxPagesText.visibleProperty());
|
maxPagesText.managedProperty().bind(maxPagesText.visibleProperty());
|
||||||
maxPagesText.visibleProperty().bind(maxPages.greaterThan(0));
|
maxPagesText.visibleProperty().bind(maxPages.greaterThan(0));
|
||||||
TextFlow pageText = new TextFlow(new Text("Page "), currentPageLabel, maxPagesText);
|
TextFlow pageText = new TextFlow(new Text("Page "), currentPageLabel, maxPagesText);
|
||||||
pageText.setTextAlignment(TextAlignment.CENTER);
|
AnchorPane pageTextContainer = new AnchorPane(pageText);
|
||||||
BorderPane pageTextContainer = new BorderPane(pageText);
|
AnchorPane.setTopAnchor(pageText, 4.0);
|
||||||
BorderPane.setAlignment(pageText, Pos.CENTER);
|
AnchorPane.setRightAnchor(pageText, 0.0);
|
||||||
|
AnchorPane.setBottomAnchor(pageText, 0.0);
|
||||||
|
AnchorPane.setLeftAnchor(pageText, 0.0);
|
||||||
|
|
||||||
|
|
||||||
Button previousPageButton = new Button("Previous Page");
|
Button previousPageButton = new Button("Previous Page");
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.Text?>
|
||||||
<BorderPane
|
<BorderPane
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="com.andrewlalis.perfin.control.AccountViewController"
|
fx:controller="com.andrewlalis.perfin.control.AccountViewController"
|
||||||
stylesheets="@style/account-view.css,@style/base.css"
|
stylesheets="@style/base.css"
|
||||||
>
|
>
|
||||||
<top>
|
<top>
|
||||||
<HBox styleClass="std-padding,std-spacing">
|
<Label fx:id="titleLabel" styleClass="std-padding,large-text,bold-text"/>
|
||||||
<Label fx:id="titleLabel" styleClass="large-text,bold-text"/>
|
|
||||||
</HBox>
|
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<VBox>
|
<VBox>
|
||||||
|
@ -19,50 +19,53 @@
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<center>
|
<center>
|
||||||
<VBox styleClass="std-padding,std-spacing">
|
<VBox styleClass="std-padding,std-spacing">
|
||||||
<HBox>
|
<FlowPane>
|
||||||
<!-- Main account properties. -->
|
<!-- Main account properties. -->
|
||||||
<VBox HBox.hgrow="SOMETIMES">
|
<PropertiesPane vgap="5" hgap="5">
|
||||||
<VBox styleClass="account-property-box">
|
<Label text="Name" styleClass="bold-text"/>
|
||||||
<Label text="Name"/>
|
<Label fx:id="accountNameLabel"/>
|
||||||
<TextField fx:id="accountNameField" editable="false"/>
|
|
||||||
</VBox>
|
<Label text="Number" styleClass="bold-text"/>
|
||||||
<VBox styleClass="account-property-box">
|
<Label fx:id="accountNumberLabel" styleClass="mono-font"/>
|
||||||
<Label text="Number"/>
|
|
||||||
<TextField fx:id="accountNumberField" editable="false" styleClass="mono-font"/>
|
<Label text="Currency" styleClass="bold-text"/>
|
||||||
</VBox>
|
<Label fx:id="accountCurrencyLabel"/>
|
||||||
<VBox styleClass="account-property-box">
|
|
||||||
<Label text="Currency"/>
|
<Label text="Created At" styleClass="bold-text"/>
|
||||||
<TextField fx:id="accountCurrencyField" editable="false"/>
|
<Label fx:id="accountCreatedAtLabel" styleClass="mono-font"/>
|
||||||
</VBox>
|
|
||||||
<VBox styleClass="account-property-box">
|
<VBox>
|
||||||
<Label text="Created At"/>
|
<Label text="Current Balance" styleClass="bold-text" fx:id="balanceLabel"/>
|
||||||
<TextField fx:id="accountCreatedAtField" editable="false"/>
|
<Text
|
||||||
</VBox>
|
style="-fx-font-size: x-small; -fx-fill: grey"
|
||||||
<VBox styleClass="account-property-box">
|
wrappingWidth="${balanceLabel.width}"
|
||||||
<Label text="Current Balance"/>
|
>Computed using the last recorded balance and all transactions since.</Text>
|
||||||
<TextField fx:id="accountBalanceField" editable="false"/>
|
|
||||||
</VBox>
|
|
||||||
</VBox>
|
</VBox>
|
||||||
|
<Label fx:id="accountBalanceLabel" styleClass="mono-font"/>
|
||||||
|
</PropertiesPane>
|
||||||
<VBox HBox.hgrow="SOMETIMES">
|
<VBox HBox.hgrow="SOMETIMES">
|
||||||
<Label text="Panel 2"/>
|
<Label text="Panel 2"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</FlowPane>
|
||||||
</VBox>
|
</VBox>
|
||||||
</center>
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<VBox styleClass="std-padding,std-spacing">
|
<VBox styleClass="std-padding,std-spacing">
|
||||||
<Label text="Actions" styleClass="bold-text"/>
|
<Label text="Actions" styleClass="bold-text"/>
|
||||||
|
<VBox fx:id="actionsVBox" styleClass="std-spacing">
|
||||||
<Button text="Edit" onAction="#goToEditPage"/>
|
<Button text="Edit" onAction="#goToEditPage"/>
|
||||||
<Button text="Record Balance" onAction="#goToCreateBalanceRecord"/>
|
<Button text="Record Balance" onAction="#goToCreateBalanceRecord"/>
|
||||||
<Button text="Archive" onAction="#archiveAccount"/>
|
<Button text="Archive" onAction="#archiveAccount"/>
|
||||||
<Button text="Delete" onAction="#deleteAccount"/>
|
<Button text="Delete" onAction="#deleteAccount"/>
|
||||||
|
<Button text="Unarchive" onAction="#unarchiveAccount"/>
|
||||||
|
</VBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
|
||||||
<!-- Account history -->
|
<!-- Account history -->
|
||||||
<VBox VBox.vgrow="ALWAYS">
|
<VBox VBox.vgrow="ALWAYS">
|
||||||
<Label text="History" styleClass="bold-text"/>
|
<Label text="History" styleClass="bold-text,std-padding"/>
|
||||||
<VBox>
|
<VBox>
|
||||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||||
<VBox fx:id="historyItemsVBox" style="-fx-padding: 10px; -fx-spacing: 10px;"/>
|
<VBox fx:id="historyItemsVBox" style="-fx-padding: 10px; -fx-spacing: 10px;"/>
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue