Added the ability to navigate to a specific transaction.
This commit is contained in:
parent
b477e9ab3c
commit
173204c61c
|
@ -54,14 +54,14 @@ public class TransactionViewController {
|
|||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
CreditAndDebitAccounts accounts = repo.findLinkedAccounts(transaction.getId());
|
||||
Platform.runLater(() -> {
|
||||
if (accounts.hasDebit()) {
|
||||
debitAccountLink.setText(accounts.debitAccount().getShortName());
|
||||
debitAccountLink.setOnAction(event -> router.navigate("account", accounts.debitAccount()));
|
||||
}
|
||||
if (accounts.hasCredit()) {
|
||||
creditAccountLink.setText(accounts.creditAccount().getShortName());
|
||||
creditAccountLink.setOnAction(event -> router.navigate("account", accounts.creditAccount()));
|
||||
}
|
||||
accounts.ifDebit(acc -> {
|
||||
debitAccountLink.setText(acc.getShortName());
|
||||
debitAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
accounts.ifCredit(acc -> {
|
||||
creditAccountLink.setText(acc.getShortName());
|
||||
creditAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -105,5 +105,6 @@ public class TransactionViewController {
|
|||
TextFlow parent = (TextFlow) link.getParent();
|
||||
parent.managedProperty().bind(parent.visibleProperty());
|
||||
parent.visibleProperty().bind(link.textProperty().isNotEmpty());
|
||||
link.setText(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.util.Pair;
|
||||
import com.andrewlalis.perfin.view.SceneUtil;
|
||||
import com.andrewlalis.perfin.data.pagination.Page;
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.pagination.Sort;
|
||||
import com.andrewlalis.perfin.data.util.Pair;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.model.Transaction;
|
||||
import com.andrewlalis.perfin.view.SceneUtil;
|
||||
import com.andrewlalis.perfin.view.component.DataSourcePaginationControls;
|
||||
import com.andrewlalis.perfin.view.component.TransactionTile;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
|
@ -19,9 +19,21 @@ import javafx.scene.layout.BorderPane;
|
|||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
/**
|
||||
* Controller for the view of all transactions in a user's profile.
|
||||
* Transactions are displayed in a paginated manner, and this controller
|
||||
* accepts as a route context a {@link PageRequest} to initialize the results
|
||||
* to a specific page.
|
||||
*/
|
||||
public class TransactionsViewController implements RouteSelectionListener {
|
||||
public static List<Sort> DEFAULT_SORTS = List.of(Sort.desc("timestamp"));
|
||||
public static int DEFAULT_ITEMS_PER_PAGE = 5;
|
||||
public record RouteContext(Long selectedTransactionId) {}
|
||||
|
||||
@FXML public BorderPane transactionsListBorderPane;
|
||||
@FXML public VBox transactionsVBox;
|
||||
private DataSourcePaginationControls paginationControls;
|
||||
|
@ -80,8 +92,23 @@ public class TransactionsViewController implements RouteSelectionListener {
|
|||
|
||||
@Override
|
||||
public void onRouteSelected(Object context) {
|
||||
paginationControls.sorts.setAll(Sort.desc("timestamp"));
|
||||
this.paginationControls.setPage(1);
|
||||
paginationControls.sorts.setAll(DEFAULT_SORTS);
|
||||
paginationControls.itemsPerPage.set(DEFAULT_ITEMS_PER_PAGE);
|
||||
|
||||
// If a transaction id is given in the route context, navigate to the page it's on and select it.
|
||||
if (context instanceof RouteContext ctx && ctx.selectedTransactionId != null) {
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
repo.findById(ctx.selectedTransactionId).ifPresent(tx -> {
|
||||
long offset = repo.countAllAfter(tx.getId());
|
||||
int pageNumber = (int) (offset / DEFAULT_ITEMS_PER_PAGE) + 1;
|
||||
paginationControls.setPage(pageNumber).thenRun(() -> selectedTransaction.set(tx));
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
paginationControls.setPage(1);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -92,13 +119,13 @@ public class TransactionsViewController implements RouteSelectionListener {
|
|||
private TransactionTile makeTile(Transaction transaction) {
|
||||
var tile = new TransactionTile(transaction);
|
||||
tile.setOnMouseClicked(event -> {
|
||||
if (selectedTransaction.get() == null || selectedTransaction.get().getId() != transaction.getId()) {
|
||||
if (selectedTransaction.get() == null || !selectedTransaction.get().equals(transaction)) {
|
||||
selectedTransaction.set(transaction);
|
||||
} else {
|
||||
selectedTransaction.set(null);
|
||||
}
|
||||
});
|
||||
tile.selected.bind(selectedTransaction.map(t -> t != null && t.getId() == transaction.getId()));
|
||||
tile.selected.bind(selectedTransaction.map(t -> t != null && t.equals(transaction)));
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.nio.file.Path;
|
|||
import java.time.LocalDateTime;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TransactionRepository extends AutoCloseable {
|
||||
|
@ -22,8 +23,10 @@ public interface TransactionRepository extends AutoCloseable {
|
|||
CreditAndDebitAccounts linkedAccounts,
|
||||
List<Path> attachments
|
||||
);
|
||||
Optional<Transaction> findById(long id);
|
||||
Page<Transaction> findAll(PageRequest pagination);
|
||||
long countAll();
|
||||
long countAllAfter(long transactionId);
|
||||
Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination);
|
||||
CreditAndDebitAccounts findLinkedAccounts(long transactionId);
|
||||
List<Attachment> findAttachments(long transactionId);
|
||||
|
|
|
@ -2,10 +2,10 @@ package com.andrewlalis.perfin.data.impl;
|
|||
|
||||
import com.andrewlalis.perfin.data.AccountEntryRepository;
|
||||
import com.andrewlalis.perfin.data.AttachmentRepository;
|
||||
import com.andrewlalis.perfin.data.util.DbUtil;
|
||||
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||
import com.andrewlalis.perfin.data.pagination.Page;
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.util.DbUtil;
|
||||
import com.andrewlalis.perfin.model.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -14,10 +14,7 @@ import java.sql.Connection;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record JdbcTransactionRepository(Connection conn, Path contentDir) implements TransactionRepository {
|
||||
|
@ -55,6 +52,11 @@ public record JdbcTransactionRepository(Connection conn, Path contentDir) implem
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Transaction> findById(long id) {
|
||||
return DbUtil.findById(conn, "SELECT * FROM transaction WHERE id = ?", id, JdbcTransactionRepository::parseTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Transaction> findAll(PageRequest pagination) {
|
||||
return DbUtil.findAll(
|
||||
|
@ -70,6 +72,16 @@ public record JdbcTransactionRepository(Connection conn, Path contentDir) implem
|
|||
return DbUtil.findOne(conn, "SELECT COUNT(id) FROM transaction", Collections.emptyList(), rs -> rs.getLong(1)).orElse(0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAllAfter(long transactionId) {
|
||||
return DbUtil.findOne(
|
||||
conn,
|
||||
"SELECT COUNT(id) FROM transaction WHERE timestamp > (SELECT timestamp FROM transaction WHERE id = ?)",
|
||||
List.of(transactionId),
|
||||
rs -> rs.getLong(1)
|
||||
).orElse(0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination) {
|
||||
String idsStr = accountIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
||||
|
|
|
@ -11,11 +11,11 @@ import java.util.Currency;
|
|||
* entries that apply this transaction's amount to one or more accounts.
|
||||
*/
|
||||
public class Transaction {
|
||||
private long id;
|
||||
private LocalDateTime timestamp;
|
||||
private final long id;
|
||||
private final LocalDateTime timestamp;
|
||||
|
||||
private BigDecimal amount;
|
||||
private Currency currency;
|
||||
private final BigDecimal amount;
|
||||
private final Currency currency;
|
||||
private String description;
|
||||
|
||||
public Transaction(long id, LocalDateTime timestamp, BigDecimal amount, Currency currency, String description) {
|
||||
|
@ -26,13 +26,6 @@ public class Transaction {
|
|||
this.description = description;
|
||||
}
|
||||
|
||||
public Transaction(LocalDateTime timestamp, BigDecimal amount, Currency currency, String description) {
|
||||
this.timestamp = timestamp;
|
||||
this.amount = amount;
|
||||
this.currency = currency;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -52,4 +45,9 @@ public class Transaction {
|
|||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof Transaction tx && id == tx.id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
import com.andrewlalis.perfin.control.TransactionsViewController;
|
||||
import com.andrewlalis.perfin.data.AccountHistoryItemRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
|
@ -13,6 +14,8 @@ import javafx.scene.layout.BorderPane;
|
|||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
/**
|
||||
* A tile that shows a brief bit of information about an account history item.
|
||||
*/
|
||||
|
@ -41,6 +44,10 @@ public class AccountHistoryItemTile extends BorderPane {
|
|||
private Node buildAccountEntryItem(AccountEntry entry) {
|
||||
Text amountText = new Text(CurrencyUtil.formatMoney(entry.getSignedAmount(), entry.getCurrency()));
|
||||
Hyperlink transactionLink = new Hyperlink("Transaction #" + entry.getTransactionId());
|
||||
transactionLink.setOnAction(event -> router.navigate(
|
||||
"transactions",
|
||||
new TransactionsViewController.RouteContext(entry.getTransactionId())
|
||||
));
|
||||
return new TextFlow(
|
||||
transactionLink,
|
||||
new Text("posted as a " + entry.getType().name().toLowerCase() + " to this account, with a value of "),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
import com.andrewlalis.perfin.PerfinApp;
|
||||
import com.andrewlalis.perfin.model.Attachment;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.ImageCache;
|
||||
|
@ -11,6 +12,7 @@ import javafx.scene.paint.Color;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -58,7 +60,8 @@ public class AttachmentPreview extends BorderPane {
|
|||
this.setCenter(stackPane);
|
||||
this.setOnMouseClicked(event -> {
|
||||
if (this.isHover()) {
|
||||
System.out.println("Opening attachment: " + attachment.getFilename());
|
||||
Path filePath = attachment.getPath(Profile.getContentDir(Profile.getCurrent().getName()));
|
||||
PerfinApp.instance.getHostServices().showDocument(filePath.toAbsolutePath().toUri().toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import javafx.beans.property.IntegerProperty;
|
|||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
|
@ -20,6 +19,8 @@ import javafx.scene.text.Text;
|
|||
import javafx.scene.text.TextAlignment;
|
||||
import javafx.scene.text.TextFlow;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A pane that contains some controls for navigating a paginated data source.
|
||||
* That includes going to the next/previous page, setting the preferred page
|
||||
|
@ -66,10 +67,6 @@ public class DataSourcePaginationControls extends BorderPane {
|
|||
nextPageButton.disableProperty().bind(fetching.or(currentPage.greaterThanOrEqualTo(maxPages)));
|
||||
nextPageButton.setOnAction(event -> setPage(currentPage.get() + 1));
|
||||
|
||||
// sorts.addListener((ListChangeListener<Sort>) c -> {
|
||||
// setPage(1);
|
||||
// });
|
||||
|
||||
HBox hbox = new HBox(
|
||||
previousPageButton,
|
||||
pageTextContainer,
|
||||
|
@ -79,7 +76,8 @@ public class DataSourcePaginationControls extends BorderPane {
|
|||
setCenter(hbox);
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
public CompletableFuture<Void> setPage(int page) {
|
||||
CompletableFuture<Void> cf = new CompletableFuture<>();
|
||||
fetching.set(true);
|
||||
PageRequest pagination = new PageRequest(page - 1, itemsPerPage.get(), sorts);
|
||||
Thread.ofVirtual().start(() -> {
|
||||
|
@ -97,14 +95,17 @@ public class DataSourcePaginationControls extends BorderPane {
|
|||
}
|
||||
currentPage.set(page);
|
||||
fetching.set(false);
|
||||
cf.complete(null);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
Platform.runLater(() -> {
|
||||
target.clear();
|
||||
fetching.set(false);
|
||||
cf.complete(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue