Improved attachments preview in transaction and balance record view.
This commit is contained in:
parent
6b563003ec
commit
2c49dd5766
|
@ -86,6 +86,7 @@ public class PerfinApp extends Application {
|
|||
router.map("transactions", PerfinApp.class.getResource("/transactions-view.fxml"));
|
||||
router.map("create-transaction", PerfinApp.class.getResource("/create-transaction.fxml"));
|
||||
router.map("create-balance-record", PerfinApp.class.getResource("/create-balance-record.fxml"));
|
||||
router.map("balance-record", PerfinApp.class.getResource("/balance-record-view.fxml"));
|
||||
|
||||
// Help pages.
|
||||
helpRouter.map("home", PerfinApp.class.getResource("/help-pages/home.fxml"));
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.model.Attachment;
|
||||
import com.andrewlalis.perfin.model.BalanceRecord;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.component.AttachmentsViewPane;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
/**
|
||||
* Controller for the page which shows an overview of a balance record.
|
||||
*/
|
||||
public class BalanceRecordViewController implements RouteSelectionListener {
|
||||
private BalanceRecord balanceRecord;
|
||||
|
||||
@FXML public Label titleLabel;
|
||||
|
||||
@FXML public Label timestampLabel;
|
||||
@FXML public Label balanceLabel;
|
||||
@FXML public Label currencyLabel;
|
||||
@FXML public AttachmentsViewPane attachmentsViewPane;
|
||||
|
||||
@FXML public void initialize() {
|
||||
attachmentsViewPane.hideIfEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteSelected(Object context) {
|
||||
this.balanceRecord = (BalanceRecord) context;
|
||||
if (balanceRecord == null) return;
|
||||
titleLabel.setText("Balance Record #" + balanceRecord.id);
|
||||
timestampLabel.setText(DateUtil.formatUTCAsLocalWithZone(balanceRecord.getTimestamp()));
|
||||
balanceLabel.setText(CurrencyUtil.formatMoney(balanceRecord.getMoneyAmount()));
|
||||
currencyLabel.setText(balanceRecord.getCurrency().getDisplayName());
|
||||
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useBalanceRecordRepository(repo -> {
|
||||
List<Attachment> attachments = repo.findAttachments(balanceRecord.id);
|
||||
Platform.runLater(() -> attachmentsViewPane.setAttachments(attachments));
|
||||
}));
|
||||
}
|
||||
|
||||
@FXML public void delete() {
|
||||
boolean confirm = Popups.confirm("Are you sure you want to delete this balance record? This may have an effect on the derived balance of your account, as shown in Perfin.");
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useBalanceRecordRepository(repo -> {
|
||||
repo.deleteById(balanceRecord.id);
|
||||
});
|
||||
router.navigateBackAndClear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,18 +6,11 @@ import com.andrewlalis.perfin.model.Attachment;
|
|||
import com.andrewlalis.perfin.model.CreditAndDebitAccounts;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.model.Transaction;
|
||||
import com.andrewlalis.perfin.view.BindingUtil;
|
||||
import com.andrewlalis.perfin.view.component.AttachmentPreview;
|
||||
import com.andrewlalis.perfin.view.component.AttachmentsViewPane;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.TextFlow;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -36,9 +29,13 @@ public class TransactionViewController {
|
|||
@FXML public Hyperlink debitAccountLink;
|
||||
@FXML public Hyperlink creditAccountLink;
|
||||
|
||||
@FXML public VBox attachmentsContainer;
|
||||
@FXML public HBox attachmentsHBox;
|
||||
private final ObservableList<Attachment> attachmentsList = FXCollections.observableArrayList();
|
||||
@FXML public AttachmentsViewPane attachmentsViewPane;
|
||||
|
||||
@FXML public void initialize() {
|
||||
configureAccountLinkBindings(debitAccountLink);
|
||||
configureAccountLinkBindings(creditAccountLink);
|
||||
attachmentsViewPane.hideIfEmpty();
|
||||
}
|
||||
|
||||
public void setTransaction(Transaction transaction) {
|
||||
this.transaction = transaction;
|
||||
|
@ -48,8 +45,6 @@ public class TransactionViewController {
|
|||
timestampLabel.setText(DateUtil.formatUTCAsLocalWithZone(transaction.getTimestamp()));
|
||||
descriptionLabel.setText(transaction.getDescription());
|
||||
|
||||
configureAccountLinkBindings(debitAccountLink);
|
||||
configureAccountLinkBindings(creditAccountLink);
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
CreditAndDebitAccounts accounts = repo.findLinkedAccounts(transaction.id);
|
||||
|
@ -66,19 +61,12 @@ public class TransactionViewController {
|
|||
});
|
||||
});
|
||||
|
||||
attachmentsContainer.managedProperty().bind(attachmentsContainer.visibleProperty());
|
||||
attachmentsContainer.visibleProperty().bind(new SimpleListProperty<>(attachmentsList).emptyProperty().not());
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
List<Attachment> attachments = repo.findAttachments(transaction.id);
|
||||
Platform.runLater(() -> attachmentsList.setAll(attachments));
|
||||
Platform.runLater(() -> attachmentsViewPane.setAttachments(attachments));
|
||||
});
|
||||
});
|
||||
attachmentsHBox.setMinHeight(AttachmentPreview.HEIGHT);
|
||||
attachmentsHBox.setPrefHeight(AttachmentPreview.HEIGHT);
|
||||
((ScrollPane) attachmentsHBox.getParent().getParent().getParent()).minHeightProperty().bind(attachmentsHBox.heightProperty().map(n -> n.doubleValue() + 2));
|
||||
((ScrollPane) attachmentsHBox.getParent().getParent().getParent()).prefHeightProperty().bind(attachmentsHBox.heightProperty().map(n -> n.doubleValue() + 2));
|
||||
BindingUtil.mapContent(attachmentsHBox.getChildren(), attachmentsList, AttachmentPreview::new);
|
||||
}
|
||||
|
||||
@FXML public void deleteTransaction() {
|
||||
|
@ -93,7 +81,6 @@ public class TransactionViewController {
|
|||
);
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
// TODO: Delete attachments first!
|
||||
repo.delete(transaction.id);
|
||||
router.replace("transactions");
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.data;
|
||||
|
||||
import com.andrewlalis.perfin.model.Attachment;
|
||||
import com.andrewlalis.perfin.model.BalanceRecord;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -14,5 +15,6 @@ public interface BalanceRecordRepository extends AutoCloseable {
|
|||
BalanceRecord findLatestByAccountId(long accountId);
|
||||
Optional<BalanceRecord> findClosestBefore(long accountId, LocalDateTime utcTimestamp);
|
||||
Optional<BalanceRecord> findClosestAfter(long accountId, LocalDateTime utcTimestamp);
|
||||
List<Attachment> findAttachments(long recordId);
|
||||
void deleteById(long id);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,21 @@ public record JdbcBalanceRecordRepository(Connection conn, Path contentDir) impl
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> findAttachments(long recordId) {
|
||||
return DbUtil.findAll(
|
||||
conn,
|
||||
"""
|
||||
SELECT *
|
||||
FROM attachment
|
||||
LEFT JOIN balance_record_attachment ba ON ba.attachment_id = attachment.id
|
||||
WHERE ba.balance_record_id = ?
|
||||
ORDER BY uploaded_at ASC, filename ASC""",
|
||||
List.of(recordId),
|
||||
JdbcAttachmentRepository::parseAttachment
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(long id) {
|
||||
DbUtil.updateOne(conn, "DELETE FROM balance_record WHERE id = ?", List.of(id));
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
import com.andrewlalis.perfin.control.AccountViewController;
|
||||
import com.andrewlalis.perfin.control.Popups;
|
||||
import com.andrewlalis.perfin.data.AccountHistoryItemRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.model.BalanceRecord;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.model.history.AccountHistoryItem;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
public class AccountHistoryBalanceRecordTile extends AccountHistoryItemTile {
|
||||
public AccountHistoryBalanceRecordTile(AccountHistoryItem item, AccountHistoryItemRepository repo, AccountViewController controller) {
|
||||
super(item);
|
||||
|
@ -25,16 +24,8 @@ public class AccountHistoryBalanceRecordTile extends AccountHistoryItemTile {
|
|||
var text = new TextFlow(new Text("Balance record #" + balanceRecord.id + " added with value of "), amountText);
|
||||
setCenter(text);
|
||||
|
||||
Hyperlink deleteLink = new Hyperlink("Delete this balance record");
|
||||
deleteLink.setOnAction(event -> {
|
||||
boolean confirm = Popups.confirm("Are you sure you want to delete this balance record? It will be removed permanently, and cannot be undone.");
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useBalanceRecordRepository(balanceRecordRepo -> {
|
||||
balanceRecordRepo.deleteById(balanceRecord.id);
|
||||
Platform.runLater(controller::reloadHistory);
|
||||
});
|
||||
}
|
||||
});
|
||||
setBottom(deleteLink);
|
||||
Hyperlink viewLink = new Hyperlink("View this balance record");
|
||||
viewLink.setOnAction(event -> router.navigate("balance-record", balanceRecord));
|
||||
setBottom(viewLink);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,15 @@ import com.andrewlalis.perfin.PerfinApp;
|
|||
import com.andrewlalis.perfin.model.Attachment;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.ImageCache;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -22,17 +27,21 @@ import java.util.Set;
|
|||
public class AttachmentPreview extends BorderPane {
|
||||
public static final double IMAGE_SIZE = 64.0;
|
||||
public static final double LABEL_SIZE = 18.0;
|
||||
public static final double HEIGHT = IMAGE_SIZE + LABEL_SIZE;
|
||||
public static final double HEIGHT = IMAGE_SIZE + LABEL_SIZE + 6;
|
||||
|
||||
public AttachmentPreview(Attachment attachment) {
|
||||
BorderPane contentContainer = new BorderPane();
|
||||
contentContainer.setPadding(new Insets(3));
|
||||
|
||||
Label nameLabel = new Label(attachment.getFilename());
|
||||
nameLabel.getStyleClass().add("small-font");
|
||||
VBox nameContainer = new VBox(nameLabel);
|
||||
nameContainer.setPrefHeight(LABEL_SIZE);
|
||||
nameContainer.setMaxHeight(LABEL_SIZE);
|
||||
nameContainer.setMinHeight(LABEL_SIZE);
|
||||
contentContainer.setBottom(nameContainer);
|
||||
nameLabel.setPrefHeight(LABEL_SIZE);
|
||||
nameLabel.setMaxHeight(LABEL_SIZE);
|
||||
nameLabel.setMinHeight(LABEL_SIZE);
|
||||
nameLabel.setMaxWidth(2 * IMAGE_SIZE);
|
||||
nameLabel.setAlignment(Pos.CENTER);
|
||||
BorderPane.setAlignment(nameLabel, Pos.CENTER);
|
||||
contentContainer.setBottom(nameLabel);
|
||||
|
||||
boolean showDocIcon = true;
|
||||
Set<String> imageTypes = Set.of("image/png", "image/jpeg", "image/gif", "image/bmp");
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
import com.andrewlalis.perfin.model.Attachment;
|
||||
import com.andrewlalis.perfin.view.BindingUtil;
|
||||
import javafx.beans.property.ListProperty;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A pane which shows a list of attachments in a horizontally scrolling
|
||||
* container.
|
||||
*/
|
||||
public class AttachmentsViewPane extends VBox {
|
||||
private final StringProperty titleProperty = new SimpleStringProperty("Attachments");
|
||||
private final ObservableList<Attachment> attachments = FXCollections.observableArrayList();
|
||||
private final ListProperty<Attachment> attachmentListProperty = new SimpleListProperty<>(attachments);
|
||||
|
||||
public AttachmentsViewPane() {
|
||||
Label titleLabel = new Label();
|
||||
titleLabel.getStyleClass().add("bold-text");
|
||||
titleLabel.textProperty().bind(titleProperty);
|
||||
|
||||
HBox attachmentsHBox = new HBox();
|
||||
attachmentsHBox.setMinHeight(AttachmentPreview.HEIGHT);
|
||||
attachmentsHBox.setPrefHeight(AttachmentPreview.HEIGHT);
|
||||
attachmentsHBox.setMaxHeight(AttachmentPreview.HEIGHT);
|
||||
attachmentsHBox.getStyleClass().add("std-spacing");
|
||||
BindingUtil.mapContent(attachmentsHBox.getChildren(), attachments, AttachmentPreview::new);
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane(attachmentsHBox);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.minViewportHeightProperty().bind(attachmentsHBox.heightProperty());
|
||||
scrollPane.prefViewportHeightProperty().bind(attachmentsHBox.heightProperty());
|
||||
|
||||
getChildren().addAll(titleLabel, scrollPane);
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
titleProperty.set(title);
|
||||
}
|
||||
|
||||
public void setAttachments(List<Attachment> attachments) {
|
||||
this.attachments.setAll(attachments);
|
||||
}
|
||||
|
||||
public ListProperty<Attachment> listProperty() {
|
||||
return attachmentListProperty;
|
||||
}
|
||||
|
||||
public void hideIfEmpty() {
|
||||
managedProperty().bind(visibleProperty());
|
||||
visibleProperty().bind(attachmentListProperty.emptyProperty().not());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.andrewlalis.perfin.view.component.AttachmentsViewPane?>
|
||||
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<BorderPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="com.andrewlalis.perfin.control.BalanceRecordViewController"
|
||||
>
|
||||
<top>
|
||||
<Label fx:id="titleLabel" styleClass="large-font,bold-text,std-padding"/>
|
||||
</top>
|
||||
<center>
|
||||
<VBox styleClass="std-padding,std-spacing">
|
||||
<PropertiesPane vgap="5" hgap="5">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints minWidth="100" halignment="LEFT" hgrow="NEVER"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" halignment="LEFT"/>
|
||||
</columnConstraints>
|
||||
|
||||
<Label text="Timestamp" styleClass="bold-text"/>
|
||||
<Label fx:id="timestampLabel" styleClass="mono-font"/>
|
||||
|
||||
<Label text="Balance" styleClass="bold-text"/>
|
||||
<Label fx:id="balanceLabel" styleClass="mono-font"/>
|
||||
|
||||
<Label text="Currency" styleClass="bold-text"/>
|
||||
<Label fx:id="currencyLabel" styleClass="mono-font"/>
|
||||
</PropertiesPane>
|
||||
<AttachmentsViewPane fx:id="attachmentsViewPane"/>
|
||||
</VBox>
|
||||
</center>
|
||||
<bottom>
|
||||
<FlowPane styleClass="std-padding,std-spacing">
|
||||
<Button text="Delete" onAction="#delete"/>
|
||||
</FlowPane>
|
||||
</bottom>
|
||||
</BorderPane>
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.andrewlalis.perfin.view.component.AttachmentsViewPane?>
|
||||
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||
<BorderPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="com.andrewlalis.perfin.control.TransactionViewController"
|
||||
|
@ -22,7 +23,7 @@
|
|||
<ColumnConstraints minWidth="100" halignment="LEFT" hgrow="NEVER"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" halignment="LEFT"/>
|
||||
</columnConstraints>
|
||||
<Label text="Amount" styleClass="bold-text" style="-fx-min-width: 100px;"/>
|
||||
<Label text="Amount" styleClass="bold-text"/>
|
||||
<Label fx:id="amountLabel" styleClass="mono-font"/>
|
||||
|
||||
<Label text="Timestamp" styleClass="bold-text"/>
|
||||
|
@ -41,12 +42,7 @@
|
|||
<Hyperlink fx:id="creditAccountLink"/>
|
||||
</TextFlow>
|
||||
</VBox>
|
||||
<VBox fx:id="attachmentsContainer">
|
||||
<Label text="Attachments" styleClass="bold-text"/>
|
||||
<ScrollPane fitToWidth="true" fitToHeight="true">
|
||||
<HBox fx:id="attachmentsHBox" styleClass="std-padding,std-spacing"/>
|
||||
</ScrollPane>
|
||||
</VBox>
|
||||
<AttachmentsViewPane fx:id="attachmentsViewPane"/>
|
||||
<FlowPane styleClass="std-padding, std-spacing">
|
||||
<Button text="Delete this transaction" onAction="#deleteTransaction"/>
|
||||
</FlowPane>
|
||||
|
|
Loading…
Reference in New Issue