Add experimental "Advanced Search" features, may incorporate into the main search interface yet...
This commit is contained in:
parent
a88ebc8e13
commit
2dbb3d944d
|
@ -3,17 +3,18 @@ package com.andrewlalis.perfin.control;
|
||||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||||
import com.andrewlalis.perfin.data.AccountRepository;
|
import com.andrewlalis.perfin.data.AccountRepository;
|
||||||
import com.andrewlalis.perfin.data.TransactionRepository;
|
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||||
|
import com.andrewlalis.perfin.data.TransactionVendorRepository;
|
||||||
import com.andrewlalis.perfin.data.impl.JdbcDataSource;
|
import com.andrewlalis.perfin.data.impl.JdbcDataSource;
|
||||||
import com.andrewlalis.perfin.data.pagination.Page;
|
import com.andrewlalis.perfin.data.pagination.Page;
|
||||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||||
import com.andrewlalis.perfin.data.pagination.Sort;
|
import com.andrewlalis.perfin.data.pagination.Sort;
|
||||||
import com.andrewlalis.perfin.data.search.JdbcTransactionSearcher;
|
import com.andrewlalis.perfin.data.search.JdbcTransactionSearcher;
|
||||||
import com.andrewlalis.perfin.data.search.SearchFilter;
|
import com.andrewlalis.perfin.data.search.SearchFilter;
|
||||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
|
||||||
import com.andrewlalis.perfin.data.util.Pair;
|
import com.andrewlalis.perfin.data.util.Pair;
|
||||||
import com.andrewlalis.perfin.model.Account;
|
import com.andrewlalis.perfin.model.Account;
|
||||||
import com.andrewlalis.perfin.model.Profile;
|
import com.andrewlalis.perfin.model.Profile;
|
||||||
import com.andrewlalis.perfin.model.Transaction;
|
import com.andrewlalis.perfin.model.Transaction;
|
||||||
|
import com.andrewlalis.perfin.model.TransactionVendor;
|
||||||
import com.andrewlalis.perfin.view.BindingUtil;
|
import com.andrewlalis.perfin.view.BindingUtil;
|
||||||
import com.andrewlalis.perfin.view.SceneUtil;
|
import com.andrewlalis.perfin.view.SceneUtil;
|
||||||
import com.andrewlalis.perfin.view.component.AccountSelectionBox;
|
import com.andrewlalis.perfin.view.component.AccountSelectionBox;
|
||||||
|
@ -25,18 +26,16 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.FileChooser;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||||
|
|
||||||
|
@ -57,7 +56,15 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
@FXML public BorderPane transactionsListBorderPane;
|
@FXML public BorderPane transactionsListBorderPane;
|
||||||
@FXML public TextField searchField;
|
@FXML public TextField searchField;
|
||||||
@FXML public AccountSelectionBox filterByAccountComboBox;
|
@FXML public AccountSelectionBox filterByAccountComboBox;
|
||||||
|
|
||||||
|
@FXML public CheckBox advancedSearchFeaturesCheckBox;
|
||||||
|
@FXML public VBox advancedSearchFeaturesVBox;
|
||||||
|
@FXML public VBox advancedSearchAccountChoicesVBox;
|
||||||
|
@FXML public VBox advancedSearchTagChoicesVBox;
|
||||||
|
@FXML public VBox advancedSearchVendorChoicesVBox;
|
||||||
|
|
||||||
@FXML public VBox transactionsVBox;
|
@FXML public VBox transactionsVBox;
|
||||||
|
|
||||||
private DataSourcePaginationControls paginationControls;
|
private DataSourcePaginationControls paginationControls;
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +85,15 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
selectedTransaction.set(null);
|
selectedTransaction.set(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize advanced search feature toggling.
|
||||||
|
BindingUtil.bindManagedAndVisible(filterByAccountComboBox.getParent(), advancedSearchFeaturesCheckBox.selectedProperty().not());
|
||||||
|
BindingUtil.bindManagedAndVisible(advancedSearchFeaturesVBox, advancedSearchFeaturesCheckBox.selectedProperty());
|
||||||
|
advancedSearchFeaturesCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
initializeAdvancedSearchFeatures();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.paginationControls = new DataSourcePaginationControls(
|
this.paginationControls = new DataSourcePaginationControls(
|
||||||
transactionsVBox.getChildren(),
|
transactionsVBox.getChildren(),
|
||||||
new DataSourcePaginationControls.PageFetcherFunction() {
|
new DataSourcePaginationControls.PageFetcherFunction() {
|
||||||
|
@ -124,6 +140,42 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeAdvancedSearchFeatures() {
|
||||||
|
Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||||
|
List<Account> allAccounts = repo.findAll(PageRequest.unpaged(Sort.asc("name"))).items();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
advancedSearchAccountChoicesVBox.getChildren().clear();
|
||||||
|
for (Account account : allAccounts) {
|
||||||
|
CheckBox checkBox = new CheckBox(account.getShortName());
|
||||||
|
checkBox.setSelected(false);
|
||||||
|
advancedSearchAccountChoicesVBox.getChildren().add(checkBox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Profile.getCurrent().dataSource().useRepoAsync(TransactionRepository.class, repo -> {
|
||||||
|
List<String> tags = repo.findAllTags();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
advancedSearchTagChoicesVBox.getChildren().clear();
|
||||||
|
for (var tag : tags) {
|
||||||
|
CheckBox checkBox = new CheckBox(tag);
|
||||||
|
checkBox.setSelected(false);
|
||||||
|
advancedSearchTagChoicesVBox.getChildren().add(checkBox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Profile.getCurrent().dataSource().useRepoAsync(TransactionVendorRepository.class, repo -> {
|
||||||
|
List<TransactionVendor> vendors = repo.findAll();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
advancedSearchVendorChoicesVBox.getChildren().clear();
|
||||||
|
for (var vendor : vendors) {
|
||||||
|
CheckBox checkBox = new CheckBox(vendor.getName());
|
||||||
|
checkBox.setSelected(false);
|
||||||
|
advancedSearchVendorChoicesVBox.getChildren().add(checkBox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRouteSelected(Object context) {
|
public void onRouteSelected(Object context) {
|
||||||
paginationControls.sorts.setAll(DEFAULT_SORTS);
|
paginationControls.sorts.setAll(DEFAULT_SORTS);
|
||||||
|
@ -162,31 +214,7 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML public void exportTransactions() {
|
@FXML public void exportTransactions() {
|
||||||
FileChooser fileChooser = new FileChooser();
|
Popups.message(transactionsListBorderPane, "Exporting transactions is not yet supported.");
|
||||||
fileChooser.setTitle("Export Transactions");
|
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", ".csv"));
|
|
||||||
File file = fileChooser.showSaveDialog(detailPanel.getScene().getWindow());
|
|
||||||
if (file != null) {
|
|
||||||
try (
|
|
||||||
var repo = Profile.getCurrent().dataSource().getTransactionRepository();
|
|
||||||
var out = new PrintWriter(file, StandardCharsets.UTF_8)
|
|
||||||
) {
|
|
||||||
out.println("id,utc-timestamp,amount,currency,description");
|
|
||||||
|
|
||||||
List<Transaction> allTransactions = repo.findAll(PageRequest.unpaged(Sort.desc("timestamp"))).items();
|
|
||||||
for (Transaction tx : allTransactions) {
|
|
||||||
out.println("%d,%s,%s,%s,%s".formatted(
|
|
||||||
tx.id,
|
|
||||||
tx.getTimestamp().format(DateUtil.DEFAULT_DATETIME_FORMAT),
|
|
||||||
tx.getAmount().toPlainString(),
|
|
||||||
tx.getCurrency().getCurrencyCode(),
|
|
||||||
tx.getDescription() == null ? "" : tx.getDescription()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Popups.error(transactionsListBorderPane, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SearchFilter> getCurrentSearchFilters() {
|
private List<SearchFilter> getCurrentSearchFilters() {
|
||||||
|
|
|
@ -2,11 +2,8 @@
|
||||||
|
|
||||||
<?import com.andrewlalis.perfin.view.component.AccountSelectionBox?>
|
<?import com.andrewlalis.perfin.view.component.AccountSelectionBox?>
|
||||||
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.control.ScrollPane?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.control.TextField?>
|
|
||||||
<BorderPane xmlns="http://javafx.com/javafx"
|
<BorderPane xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="com.andrewlalis.perfin.control.TransactionsViewController"
|
fx:controller="com.andrewlalis.perfin.control.TransactionsViewController"
|
||||||
|
@ -14,20 +11,43 @@
|
||||||
<top>
|
<top>
|
||||||
<HBox styleClass="std-padding,std-spacing">
|
<HBox styleClass="std-padding,std-spacing">
|
||||||
<Button text="Add Transaction" onAction="#addTransaction"/>
|
<Button text="Add Transaction" onAction="#addTransaction"/>
|
||||||
<Button text="Export Transactions" onAction="#exportTransactions"/>
|
<Button text="Export Transactions" onAction="#exportTransactions" disable="true"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
|
<!-- The main page content is an HBox with a list of transactions on the
|
||||||
|
left, and a detail panel on the right (which is hidden if no
|
||||||
|
transaction is selected). -->
|
||||||
<HBox>
|
<HBox>
|
||||||
<BorderPane fx:id="transactionsListBorderPane" HBox.hgrow="ALWAYS">
|
<BorderPane fx:id="transactionsListBorderPane" HBox.hgrow="ALWAYS">
|
||||||
<top>
|
<top>
|
||||||
<HBox styleClass="padding-extra,std-spacing">
|
<VBox styleClass="padding-extra,std-spacing">
|
||||||
<TextField fx:id="searchField" promptText="Search"/>
|
<TextField fx:id="searchField" promptText="Search" maxWidth="300" prefWidth="300" minWidth="100"/>
|
||||||
<PropertiesPane hgap="5" vgap="5">
|
<PropertiesPane hgap="5" vgap="5">
|
||||||
<Label text="Filter by Account"/>
|
<Label text="Filter by Account"/>
|
||||||
<AccountSelectionBox fx:id="filterByAccountComboBox" allowNone="true" showBalance="false"/>
|
<AccountSelectionBox fx:id="filterByAccountComboBox" allowNone="true" showBalance="false"/>
|
||||||
</PropertiesPane>
|
</PropertiesPane>
|
||||||
</HBox>
|
<CheckBox fx:id="advancedSearchFeaturesCheckBox" text="Advanced Search"/>
|
||||||
|
<VBox fx:id="advancedSearchFeaturesVBox" styleClass="std-spacing">
|
||||||
|
<Label text="Advanced search features shown here!"/>
|
||||||
|
<PropertiesPane hgap="5" vgap="5">
|
||||||
|
<Label text="Filter by Accounts"/>
|
||||||
|
<ScrollPane maxHeight="100">
|
||||||
|
<VBox fx:id="advancedSearchAccountChoicesVBox" styleClass="std-spacing"/>
|
||||||
|
</ScrollPane>
|
||||||
|
|
||||||
|
<Label text="Filter by Tags"/>
|
||||||
|
<ScrollPane maxHeight="100">
|
||||||
|
<VBox fx:id="advancedSearchTagChoicesVBox" styleClass="std-spacing"/>
|
||||||
|
</ScrollPane>
|
||||||
|
|
||||||
|
<Label text="Filter by Vendor"/>
|
||||||
|
<ScrollPane maxHeight="100">
|
||||||
|
<VBox fx:id="advancedSearchVendorChoicesVBox" styleClass="std-spacing"/>
|
||||||
|
</ScrollPane>
|
||||||
|
</PropertiesPane>
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<ScrollPane styleClass="tile-container-scroll">
|
<ScrollPane styleClass="tile-container-scroll">
|
||||||
|
@ -35,6 +55,7 @@
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
|
||||||
<VBox fx:id="detailPanel"/>
|
<VBox fx:id="detailPanel"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
</center>
|
</center>
|
||||||
|
|
Loading…
Reference in New Issue