diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java index c437454..bfe1ec7 100644 --- a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java @@ -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( diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java index fffa253..53f7550 100644 --- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java @@ -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) { 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 031895d..f501afc 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java @@ -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 { diff --git a/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java b/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java index f2944c2..74ad29f 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java @@ -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"); diff --git a/src/main/java/com/andrewlalis/perfin/view/component/PropertiesPane.java b/src/main/java/com/andrewlalis/perfin/view/component/PropertiesPane.java new file mode 100644 index 0000000..eb5745c --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/PropertiesPane.java @@ -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 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(); + } +} diff --git a/src/main/resources/account-view.fxml b/src/main/resources/account-view.fxml index fef1343..d4316e9 100644 --- a/src/main/resources/account-view.fxml +++ b/src/main/resources/account-view.fxml @@ -1,17 +1,17 @@ + + - - +
@@ -19,50 +19,53 @@
- + - - - + +