diff --git a/pom.xml b/pom.xml
index ae13057..8504fba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
com.andrewlalis
javafx-scene-router
- 1.4.0
+ 1.5.1
diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java
index f755632..7c375aa 100644
--- a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java
@@ -2,12 +2,15 @@ package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.model.Account;
+import com.andrewlalis.perfin.model.Profile;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import java.time.format.DateTimeFormatter;
+import static com.andrewlalis.perfin.PerfinApp.router;
+
public class AccountViewController implements RouteSelectionListener {
private Account account;
@@ -32,4 +35,18 @@ public class AccountViewController implements RouteSelectionListener {
accountCurrencyField.setText(account.getCurrency().getDisplayName());
accountCreatedAtField.setText(account.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
+
+ @FXML
+ public void goToEditPage() {
+ router.navigate("edit-account", account);
+ }
+
+ @FXML
+ public void deleteAccount() {
+ Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
+ repo.delete(account);
+ });
+ router.getHistory().clear();
+ router.navigate("accounts");
+ }
}
diff --git a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java
index d65a0e9..16d6ec6 100644
--- a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java
@@ -25,7 +25,11 @@ public class EditAccountController implements RouteSelectionListener {
@FXML
public ComboBox accountCurrencyComboBox;
@FXML
- public ChoiceBox accountTypeChoiceBox;
+ public ChoiceBox accountTypeChoiceBox;
+
+ private boolean editingNewAccount() {
+ return account == null;
+ }
@FXML
public void initialize() {
@@ -39,16 +43,16 @@ public class EditAccountController implements RouteSelectionListener {
}
accountCurrencyComboBox.getSelectionModel().select(Currency.getInstance("USD"));
- accountTypeChoiceBox.getItems().add("Checking");
- accountTypeChoiceBox.getItems().add("Savings");
- accountTypeChoiceBox.getItems().add("Credit Card");
- accountTypeChoiceBox.getSelectionModel().select("Checking");
+ accountTypeChoiceBox.getItems().add(AccountType.CHECKING);
+ accountTypeChoiceBox.getItems().add(AccountType.SAVINGS);
+ accountTypeChoiceBox.getItems().add(AccountType.CREDIT_CARD);
+ accountTypeChoiceBox.getSelectionModel().select(AccountType.CHECKING);
}
@Override
public void onRouteSelected(Object context) {
this.account = (Account) context;
- if (account == null) {
+ if (editingNewAccount()) {
titleLabel.setText("Editing New Account");
} else {
titleLabel.setText("Editing Account: " + account.getName());
@@ -58,26 +62,38 @@ public class EditAccountController implements RouteSelectionListener {
@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);
+ try (var accountRepo = Profile.getCurrent().getDataSource().getAccountRepository()) {
+ if (editingNewAccount()) {
+ String name = accountNameField.getText().strip();
+ String number = accountNumberField.getText().strip();
+ AccountType type = accountTypeChoiceBox.getValue();
+ Currency currency = accountCurrencyComboBox.getValue();
+ Account newAccount = new Account(type, number, name, currency);
+ long id = accountRepo.insert(newAccount);
+ Account savedAccount = accountRepo.findById(id).orElseThrow();
- // Once we create the new account, go to the account.
- router.getHistory().clear();
- router.navigate("accounts");
- } else {
- throw new IllegalStateException("Not implemented.");
+ // Once we create the new account, go to the account.
+ router.getHistory().clear();
+ router.navigate("account", savedAccount);
+ } else {
+ System.out.println("Updating account " + account.getName());
+ account.setName(accountNameField.getText().strip());
+ account.setAccountNumber(accountNumberField.getText().strip());
+ account.setType(accountTypeChoiceBox.getValue());
+ account.setCurrency(accountCurrencyComboBox.getValue());
+ accountRepo.update(account);
+ Account updatedAccount = accountRepo.findById(account.getId()).orElseThrow();
+ router.getHistory().clear();
+ router.navigate("account", updatedAccount);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
@FXML
public void cancel() {
- router.navigateBack();
+ router.navigateBackAndClear();
}
public void resetForm() {
@@ -87,7 +103,10 @@ public class EditAccountController implements RouteSelectionListener {
accountTypeChoiceBox.getSelectionModel().selectFirst();
accountCurrencyComboBox.getSelectionModel().select(Currency.getInstance("USD"));
} else {
- // TODO: Set to original account.
+ accountNameField.setText(account.getName());
+ accountNumberField.setText(account.getAccountNumber());
+ accountTypeChoiceBox.getSelectionModel().select(account.getType());
+ accountCurrencyComboBox.getSelectionModel().select(account.getCurrency());
}
}
}
diff --git a/src/main/java/com/andrewlalis/perfin/control/MainViewController.java b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java
index 02f7045..34da78d 100644
--- a/src/main/java/com/andrewlalis/perfin/control/MainViewController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java
@@ -21,7 +21,6 @@ public class MainViewController {
public void initialize() {
AnchorPaneRouterView routerView = (AnchorPaneRouterView) router.getView();
mainContainer.setCenter(routerView.getAnchorPane());
- routerView.getAnchorPane().setStyle("-fx-border-color: orange;");
// Set up a simple breadcrumb display in the top bar.
BindingUtil.mapContent(
breadcrumbHBox.getChildren(),
@@ -40,6 +39,7 @@ public class MainViewController {
@FXML
public void goToAccounts() {
+ router.getHistory().clear();
router.navigate("accounts");
}
diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java
index d565045..9dce9c0 100644
--- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java
+++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java
@@ -6,9 +6,11 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
-public interface AccountRepository {
+public interface AccountRepository extends AutoCloseable {
long insert(Account account);
List findAll();
Optional findById(long id);
BigDecimal deriveCurrentBalance(long id);
+ void update(Account account);
+ void delete(Account account);
}
diff --git a/src/main/java/com/andrewlalis/perfin/data/DataSource.java b/src/main/java/com/andrewlalis/perfin/data/DataSource.java
index f91e141..5e5c5a3 100644
--- a/src/main/java/com/andrewlalis/perfin/data/DataSource.java
+++ b/src/main/java/com/andrewlalis/perfin/data/DataSource.java
@@ -2,4 +2,7 @@ package com.andrewlalis.perfin.data;
public interface DataSource {
AccountRepository getAccountRepository();
+ default void useAccountRepository(ThrowableConsumer repoConsumer) {
+ DbUtil.useClosable(this::getAccountRepository, repoConsumer);
+ }
}
diff --git a/src/main/java/com/andrewlalis/perfin/data/DbUtil.java b/src/main/java/com/andrewlalis/perfin/data/DbUtil.java
index 2fe29cf..608f588 100644
--- a/src/main/java/com/andrewlalis/perfin/data/DbUtil.java
+++ b/src/main/java/com/andrewlalis/perfin/data/DbUtil.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.function.Supplier;
public final class DbUtil {
private DbUtil() {}
@@ -90,4 +91,12 @@ public final class DbUtil {
public static LocalDateTime utcLDTFromTimestamp(Timestamp ts) {
return ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime();
}
+
+ public static void useClosable(Supplier supplier, ThrowableConsumer consumer) {
+ try (T t = supplier.get()) {
+ consumer.accept(t);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/src/main/java/com/andrewlalis/perfin/data/SqlRunnable.java b/src/main/java/com/andrewlalis/perfin/data/SqlRunnable.java
new file mode 100644
index 0000000..fd5f0fc
--- /dev/null
+++ b/src/main/java/com/andrewlalis/perfin/data/SqlRunnable.java
@@ -0,0 +1,8 @@
+package com.andrewlalis.perfin.data;
+
+import java.sql.SQLException;
+
+@FunctionalInterface
+public interface SqlRunnable {
+ void run() throws SQLException;
+}
diff --git a/src/main/java/com/andrewlalis/perfin/data/ThrowableConsumer.java b/src/main/java/com/andrewlalis/perfin/data/ThrowableConsumer.java
new file mode 100644
index 0000000..ffc7fcf
--- /dev/null
+++ b/src/main/java/com/andrewlalis/perfin/data/ThrowableConsumer.java
@@ -0,0 +1,6 @@
+package com.andrewlalis.perfin.data;
+
+@FunctionalInterface
+public interface ThrowableConsumer {
+ void accept(T value) throws Exception;
+}
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 9d47453..269e929 100644
--- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java
+++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java
@@ -2,6 +2,7 @@ package com.andrewlalis.perfin.data.impl;
import com.andrewlalis.perfin.data.AccountRepository;
import com.andrewlalis.perfin.data.DbUtil;
+import com.andrewlalis.perfin.data.UncheckedSqlException;
import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.AccountType;
@@ -46,6 +47,32 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
return BigDecimal.valueOf(0, 4);
}
+ @Override
+ public void update(Account account) {
+ DbUtil.updateOne(
+ conn,
+ "UPDATE account SET name = ?, account_number = ?, currency = ?, account_type = ? WHERE id = ?",
+ List.of(
+ account.getName(),
+ account.getAccountNumber(),
+ account.getCurrency().getCurrencyCode(),
+ account.getType().name(),
+ account.getId()
+ )
+ );
+ }
+
+ @Override
+ public void delete(Account account) {
+ try (var stmt = conn.prepareStatement("DELETE FROM account WHERE id = ?")) {
+ stmt.setLong(1, account.getId());
+ int rows = stmt.executeUpdate();
+ if (rows != 1) throw new SQLException("Affected " + rows + " rows instead of expected 1.");
+ } catch (SQLException e) {
+ throw new UncheckedSqlException(e);
+ }
+ }
+
private static Account parseAccount(ResultSet rs) throws SQLException {
long id = rs.getLong("id");
LocalDateTime createdAt = DbUtil.utcLDTFromTimestamp(rs.getTimestamp("created_at"));
@@ -55,4 +82,9 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
Currency currency = Currency.getInstance(rs.getString("currency"));
return new Account(id, createdAt, type, accountNumber, name, currency);
}
+
+ @Override
+ public void close() throws Exception {
+ conn.close();
+ }
}
diff --git a/src/main/java/com/andrewlalis/perfin/model/Account.java b/src/main/java/com/andrewlalis/perfin/model/Account.java
index 6970096..a62b2c2 100644
--- a/src/main/java/com/andrewlalis/perfin/model/Account.java
+++ b/src/main/java/com/andrewlalis/perfin/model/Account.java
@@ -48,6 +48,22 @@ public class Account {
return currency;
}
+ public void setType(AccountType type) {
+ this.type = type;
+ }
+
+ public void setAccountNumber(String accountNumber) {
+ this.accountNumber = accountNumber;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setCurrency(Currency currency) {
+ this.currency = currency;
+ }
+
public LocalDateTime getCreatedAt() {
return createdAt;
}
diff --git a/src/main/java/com/andrewlalis/perfin/model/AccountType.java b/src/main/java/com/andrewlalis/perfin/model/AccountType.java
index ab70752..2de9ba3 100644
--- a/src/main/java/com/andrewlalis/perfin/model/AccountType.java
+++ b/src/main/java/com/andrewlalis/perfin/model/AccountType.java
@@ -1,9 +1,20 @@
package com.andrewlalis.perfin.model;
public enum AccountType {
- CHECKING,
- SAVINGS,
- CREDIT_CARD;
+ CHECKING("Checking"),
+ SAVINGS("Savings"),
+ CREDIT_CARD("Credit Card");
+
+ private final String name;
+
+ AccountType(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
public static AccountType parse(String s) {
s = s.strip().toUpperCase();
diff --git a/src/main/resources/account-view.fxml b/src/main/resources/account-view.fxml
index 4a23cb0..0a251ef 100644
--- a/src/main/resources/account-view.fxml
+++ b/src/main/resources/account-view.fxml
@@ -8,13 +8,12 @@
fx:controller="com.andrewlalis.perfin.control.AccountViewController"
stylesheets="@style/account-view.css"
styleClass="main-container"
- style="-fx-border-color: green;"
>
-
+
@@ -34,9 +33,10 @@
-
-
-
+
+
+
+
diff --git a/src/main/resources/main-view.fxml b/src/main/resources/main-view.fxml
index 7dde622..624f7f7 100644
--- a/src/main/resources/main-view.fxml
+++ b/src/main/resources/main-view.fxml
@@ -8,7 +8,6 @@
fx:id="mainContainer"
fx:controller="com.andrewlalis.perfin.control.MainViewController"
stylesheets="@style/main-view.css"
- style="-fx-border-color: purple;"
>
diff --git a/src/main/resources/style/account-view.css b/src/main/resources/style/account-view.css
index f9d8a5c..dc08c10 100644
--- a/src/main/resources/style/account-view.css
+++ b/src/main/resources/style/account-view.css
@@ -24,3 +24,11 @@
#accountNumberField {
-fx-font-family: monospace;
}
+
+.actions-box {
+ -fx-spacing: 3px;
+}
+
+.actions-box > Button {
+ -fx-max-width: 500px;
+}