Added dashboard page and initial account and transaction modules.
This commit is contained in:
parent
6900fdb481
commit
f9a0fea9ab
|
@ -84,6 +84,7 @@ public class PerfinApp extends Application {
|
|||
msgConsumer.accept("Initializing application views.");
|
||||
Platform.runLater(() -> {
|
||||
// App pages.
|
||||
router.map("dashboard", PerfinApp.class.getResource("/dashboard.fxml"));
|
||||
router.map("accounts", PerfinApp.class.getResource("/accounts-view.fxml"));
|
||||
router.map("account", PerfinApp.class.getResource("/account-view.fxml"));
|
||||
router.map("edit-account", PerfinApp.class.getResource("/edit-account.fxml"));
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.view.component.module.AccountsModule;
|
||||
import com.andrewlalis.perfin.view.component.module.DashboardModule;
|
||||
import com.andrewlalis.perfin.view.component.module.RecentTransactionsModule;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
|
||||
public class DashboardController implements RouteSelectionListener {
|
||||
@FXML public ScrollPane modulesScrollPane;
|
||||
@FXML public FlowPane modulesFlowPane;
|
||||
|
||||
private DashboardModule accountsModule;
|
||||
private DashboardModule transactionsModule;
|
||||
|
||||
@FXML public void initialize() {
|
||||
var viewportWidth = modulesScrollPane.viewportBoundsProperty().map(Bounds::getWidth);
|
||||
modulesFlowPane.minWidthProperty().bind(viewportWidth);
|
||||
modulesFlowPane.prefWidthProperty().bind(viewportWidth);
|
||||
modulesFlowPane.maxWidthProperty().bind(viewportWidth);
|
||||
|
||||
accountsModule = new AccountsModule(modulesFlowPane);
|
||||
accountsModule.columnsProperty.set(2);
|
||||
|
||||
transactionsModule = new RecentTransactionsModule(modulesFlowPane);
|
||||
transactionsModule.columnsProperty.set(2);
|
||||
|
||||
modulesFlowPane.getChildren().addAll(accountsModule, transactionsModule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteSelected(Object context) {
|
||||
accountsModule.refreshContents();
|
||||
transactionsModule.refreshContents();
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ public class MainViewController {
|
|||
}
|
||||
);
|
||||
|
||||
router.navigate("accounts");
|
||||
router.navigate("dashboard");
|
||||
|
||||
// Initialize the help manual components.
|
||||
helpPane.managedProperty().bind(helpPane.visibleProperty());
|
||||
|
@ -75,14 +75,6 @@ public class MainViewController {
|
|||
router.navigateForward();
|
||||
}
|
||||
|
||||
@FXML public void goToAccounts() {
|
||||
router.replace("accounts");
|
||||
}
|
||||
|
||||
@FXML public void goToTransactions() {
|
||||
router.replace("transactions");
|
||||
}
|
||||
|
||||
@FXML public void viewProfiles() {
|
||||
ProfilesStage.open(mainContainer.getScene().getWindow());
|
||||
}
|
||||
|
@ -106,4 +98,8 @@ public class MainViewController {
|
|||
@FXML public void helpViewTransactions() {
|
||||
helpRouter.replace("transactions");
|
||||
}
|
||||
|
||||
@FXML public void goToDashboard() {
|
||||
router.replace("dashboard");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ public class ProfilesViewController {
|
|||
Profile.setCurrent(PerfinApp.profileLoader.load(name));
|
||||
ProfileLoader.saveLastProfile(name);
|
||||
ProfilesStage.closeView();
|
||||
router.replace("accounts");
|
||||
router.replace("dashboard");
|
||||
if (showPopup) Popups.message(profilesVBox, "The profile \"" + name + "\" has been loaded.");
|
||||
return true;
|
||||
} catch (ProfileLoadException e) {
|
||||
|
|
|
@ -17,6 +17,8 @@ public interface AccountRepository extends Repository, AutoCloseable {
|
|||
long insert(AccountType type, String accountNumber, String name, Currency currency);
|
||||
Page<Account> findAll(PageRequest pagination);
|
||||
List<Account> findAllOrderedByRecentHistory();
|
||||
List<Account> findTopNOrderedByRecentHistory(int n);
|
||||
List<Account> findTopNRecentlyActive(int n, int daysSinceLastActive);
|
||||
List<Account> findAllByCurrency(Currency currency);
|
||||
Optional<Account> findById(long id);
|
||||
void updateName(long id, String name);
|
||||
|
|
|
@ -28,6 +28,7 @@ public interface TransactionRepository extends Repository, AutoCloseable {
|
|||
);
|
||||
Optional<Transaction> findById(long id);
|
||||
Page<Transaction> findAll(PageRequest pagination);
|
||||
List<Transaction> findRecentN(int n);
|
||||
long countAll();
|
||||
long countAllAfter(long transactionId);
|
||||
long countAllByAccounts(Set<Long> accountIds);
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.andrewlalis.perfin.data.impl;
|
|||
import com.andrewlalis.perfin.data.*;
|
||||
import com.andrewlalis.perfin.data.pagination.Page;
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.data.util.DbUtil;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.AccountEntry;
|
||||
|
@ -66,6 +67,40 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Account> findTopNOrderedByRecentHistory(int n) {
|
||||
return DbUtil.findAll(
|
||||
conn,
|
||||
"""
|
||||
SELECT DISTINCT ON (account.id) account.*, hi.timestamp AS _
|
||||
FROM account
|
||||
LEFT OUTER JOIN history_account ha ON ha.account_id = account.id
|
||||
LEFT OUTER JOIN history_item hi ON hi.history_id = ha.history_id
|
||||
WHERE NOT account.archived
|
||||
ORDER BY hi.timestamp DESC, account.created_at DESC
|
||||
LIMIT\s""" + n,
|
||||
JdbcAccountRepository::parseAccount
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Account> findTopNRecentlyActive(int n, int daysSinceLastActive) {
|
||||
LocalDateTime cutoff = DateUtil.nowAsUTC().minusDays(daysSinceLastActive);
|
||||
return DbUtil.findAll(
|
||||
conn,
|
||||
"""
|
||||
SELECT DISTINCT ON (account.id) account.*, hi.timestamp AS _
|
||||
FROM account
|
||||
LEFT OUTER JOIN history_account ha ON ha.account_id = account.id
|
||||
LEFT OUTER JOIN history_item hi ON hi.history_id = ha.history_id
|
||||
WHERE NOT account.archived AND hi.timestamp >= ?
|
||||
ORDER BY hi.timestamp DESC, account.created_at DESC
|
||||
LIMIT\s""" + n,
|
||||
List.of(DbUtil.timestampFromUtcLDT(cutoff)),
|
||||
JdbcAccountRepository::parseAccount
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Account> findAllByCurrency(Currency currency) {
|
||||
return DbUtil.findAll(
|
||||
|
|
|
@ -142,6 +142,15 @@ public record JdbcTransactionRepository(Connection conn, Path contentDir) implem
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Transaction> findRecentN(int n) {
|
||||
return DbUtil.findAll(
|
||||
conn,
|
||||
"SELECT * FROM transaction ORDER BY timestamp DESC LIMIT " + n,
|
||||
JdbcTransactionRepository::parseTransaction
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAll() {
|
||||
return DbUtil.findOne(conn, "SELECT COUNT(id) FROM transaction", Collections.emptyList(), rs -> rs.getLong(1)).orElse(0L);
|
||||
|
|
|
@ -25,7 +25,7 @@ import static com.andrewlalis.perfin.PerfinApp.router;
|
|||
* A compact tile that displays information about an account.
|
||||
*/
|
||||
public class AccountTile extends BorderPane {
|
||||
private static final Map<AccountType, String> ACCOUNT_TYPE_COLORS = Map.of(
|
||||
public static final Map<AccountType, String> ACCOUNT_TYPE_COLORS = Map.of(
|
||||
AccountType.CHECKING, "-fx-theme-account-type-checking",
|
||||
AccountType.SAVINGS, "-fx-theme-account-type-savings",
|
||||
AccountType.CREDIT_CARD, "-fx-theme-account-type-credit-card"
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.helpRouter;
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
/**
|
||||
* A component that renders markdown-ish text as a series of TextFlow elements,
|
||||
|
@ -26,9 +27,15 @@ public class StyledText extends VBox {
|
|||
private StringProperty text;
|
||||
private boolean initialized = false;
|
||||
|
||||
public StyledText() {
|
||||
getStyleClass().add("spacing-extra");
|
||||
}
|
||||
|
||||
public final void setText(String value) {
|
||||
initialized = false;
|
||||
if (value == null) value = "";
|
||||
textProperty().set(value);
|
||||
layoutChildren(); // Re-render the underlying text.
|
||||
}
|
||||
|
||||
public final String getText() {
|
||||
|
@ -54,7 +61,6 @@ public class StyledText extends VBox {
|
|||
String s = getText();
|
||||
getChildren().clear();
|
||||
getChildren().addAll(renderText(s));
|
||||
getStyleClass().add("spacing-extra");
|
||||
initialized = true;
|
||||
}
|
||||
super.layoutChildren();
|
||||
|
@ -135,6 +141,8 @@ public class StyledText extends VBox {
|
|||
hyperlink.setOnAction(event -> PerfinApp.instance.getHostServices().showDocument(link));
|
||||
} else if (link.startsWith("help:")) {
|
||||
hyperlink.setOnAction(event -> helpRouter.navigate(link.substring(5).strip()));
|
||||
} else if (link.startsWith("app:")) {
|
||||
hyperlink.setOnAction(event -> router.navigate(link.substring(4).strip()));
|
||||
}
|
||||
}
|
||||
hyperlink.setBorder(Border.EMPTY);
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package com.andrewlalis.perfin.view.component.module;
|
||||
|
||||
import com.andrewlalis.perfin.data.AccountRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.MoneyValue;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.component.AccountTile;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
/**
|
||||
* A module that displays a basic overview of recent accounts.
|
||||
*/
|
||||
public class AccountsModule extends DashboardModule {
|
||||
private final VBox accountsVBox = new VBox();
|
||||
|
||||
public AccountsModule(Pane parent) {
|
||||
super(parent);
|
||||
accountsVBox.getStyleClass().add("tile-container");
|
||||
ScrollPane scrollPane = new ScrollPane(accountsVBox);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||
|
||||
Button addAccountButton = new Button("Add Account");
|
||||
addAccountButton.setOnAction(event -> router.navigate("edit-account", null));
|
||||
Button viewAllAccountsButton = new Button("All Accounts");
|
||||
viewAllAccountsButton.setOnAction(event -> router.navigate("accounts"));
|
||||
Button refreshButton = new Button("Refresh");
|
||||
refreshButton.setOnAction(event -> refreshContents());
|
||||
|
||||
this.getChildren().add(new ModuleHeader(
|
||||
"Recently Active Accounts",
|
||||
addAccountButton,
|
||||
viewAllAccountsButton,
|
||||
refreshButton
|
||||
));
|
||||
this.getChildren().add(scrollPane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshContents() {
|
||||
Profile.getCurrent().dataSource().mapRepoAsync(
|
||||
AccountRepository.class,
|
||||
AccountRepository::findAllOrderedByRecentHistory
|
||||
)
|
||||
.thenApply(accounts -> accounts.stream().map(AccountsModule::buildMiniAccountTile).toList())
|
||||
.thenAccept(nodes -> Platform.runLater(() -> {
|
||||
accountsVBox.getChildren().clear();
|
||||
accountsVBox.getChildren().addAll(nodes);
|
||||
}));
|
||||
}
|
||||
|
||||
private static Node buildMiniAccountTile(Account account) {
|
||||
BorderPane borderPane = new BorderPane();
|
||||
borderPane.getStyleClass().addAll("tile", "hand-cursor");
|
||||
borderPane.setOnMouseClicked(event -> router.navigate("account", account));
|
||||
|
||||
Label nameLabel = new Label(account.getName());
|
||||
nameLabel.getStyleClass().addAll("bold-text");
|
||||
Label numberLabel = new Label(account.getAccountNumber());
|
||||
numberLabel.getStyleClass().addAll("mono-font");
|
||||
Label typeLabel = new Label(account.getType().toString());
|
||||
typeLabel.getStyleClass().add("bold-text");
|
||||
typeLabel.setStyle("-fx-text-fill: " + AccountTile.ACCOUNT_TYPE_COLORS.get(account.getType()));
|
||||
Label balanceLabel = new Label("Computing balance...");
|
||||
balanceLabel.getStyleClass().addAll("mono-font");
|
||||
balanceLabel.setDisable(true);
|
||||
|
||||
Profile.getCurrent().dataSource().mapRepoAsync(
|
||||
AccountRepository.class,
|
||||
repo -> repo.deriveCurrentBalance(account.id)
|
||||
).thenAccept(bal -> Platform.runLater(() -> {
|
||||
String text = CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(bal, account.getCurrency()));
|
||||
balanceLabel.setText(text);
|
||||
if (account.getType().areDebitsPositive() && bal.compareTo(BigDecimal.ZERO) < 0) {
|
||||
balanceLabel.getStyleClass().add("negative-color-text-fill");
|
||||
} else if (!account.getType().areDebitsPositive() && bal.compareTo(BigDecimal.ZERO) < 0) {
|
||||
balanceLabel.getStyleClass().add("positive-color-text-fill");
|
||||
}
|
||||
balanceLabel.setDisable(false);
|
||||
}));
|
||||
|
||||
VBox contentBox = new VBox(nameLabel, numberLabel, typeLabel);
|
||||
borderPane.setCenter(contentBox);
|
||||
borderPane.setRight(balanceLabel);
|
||||
return borderPane;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.andrewlalis.perfin.view.component.module;
|
||||
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
/**
|
||||
* A container intended to display content on the app's dashboard, where modules
|
||||
* are arranged in a flexible grid based on some constraints.
|
||||
*/
|
||||
public class DashboardModule extends VBox {
|
||||
public final IntegerProperty columnsProperty = new SimpleIntegerProperty(1);
|
||||
public final DoubleProperty minColumnWidthProperty = new SimpleDoubleProperty(500);
|
||||
public final DoubleProperty rowHeightProperty = new SimpleDoubleProperty(500);
|
||||
|
||||
public DashboardModule(Pane parent) {
|
||||
ObservableValue<Double> dynamicWidth = parent.widthProperty().map(parentWidth -> {
|
||||
double parentWidthD = parentWidth.doubleValue();
|
||||
if (parentWidthD < minColumnWidthProperty.doubleValue() * columnsProperty.doubleValue()) return parentWidthD;
|
||||
return Math.floor(parentWidthD / columnsProperty.doubleValue());
|
||||
});
|
||||
this.minWidthProperty().bind(dynamicWidth);
|
||||
this.prefWidthProperty().bind(dynamicWidth);
|
||||
this.maxWidthProperty().bind(dynamicWidth);
|
||||
|
||||
this.minHeightProperty().bind(rowHeightProperty);
|
||||
this.prefHeightProperty().bind(rowHeightProperty);
|
||||
this.maxHeightProperty().bind(rowHeightProperty);
|
||||
}
|
||||
|
||||
public void refreshContents() {}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.andrewlalis.perfin.view.component.module;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
/**
|
||||
* A standardized header format for dashboard modules, which includes a left-aligned
|
||||
* title and right-aligned list of action items (usually buttons).
|
||||
*/
|
||||
public class ModuleHeader extends BorderPane {
|
||||
public ModuleHeader(String title, Node... actionItems) {
|
||||
this.getStyleClass().addAll("std-padding");
|
||||
|
||||
Label titleLabel = new Label(title);
|
||||
titleLabel.getStyleClass().addAll("bold-text", "large-font");
|
||||
this.setLeft(titleLabel);
|
||||
|
||||
HBox actionsHBox = new HBox();
|
||||
actionsHBox.getStyleClass().addAll("std-spacing", "small-font");
|
||||
actionsHBox.getChildren().addAll(actionItems);
|
||||
this.setRight(actionsHBox);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.andrewlalis.perfin.view.component.module;
|
||||
|
||||
import com.andrewlalis.perfin.control.TransactionsViewController;
|
||||
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.model.Transaction;
|
||||
import com.andrewlalis.perfin.view.BindingUtil;
|
||||
import com.andrewlalis.perfin.view.component.StyledText;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
public class RecentTransactionsModule extends DashboardModule {
|
||||
private final VBox transactionsVBox = new VBox();
|
||||
|
||||
public RecentTransactionsModule(Pane parent) {
|
||||
super(parent);
|
||||
transactionsVBox.getStyleClass().add("tile-container");
|
||||
ScrollPane scrollPane = new ScrollPane(transactionsVBox);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setFitToWidth(true);
|
||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||
|
||||
Button addTransactionButton = new Button("Add Transaction");
|
||||
addTransactionButton.setOnAction(event -> router.navigate("edit-transaction"));
|
||||
Button viewTransactionsButton = new Button("All Transactions");
|
||||
viewTransactionsButton.setOnAction(event -> router.navigate("transactions"));
|
||||
Button refreshButton = new Button("Refresh");
|
||||
refreshButton.setOnAction(event -> refreshContents());
|
||||
|
||||
this.getChildren().add(new ModuleHeader(
|
||||
"Recent Transactions",
|
||||
addTransactionButton,
|
||||
viewTransactionsButton,
|
||||
refreshButton
|
||||
));
|
||||
this.getChildren().add(scrollPane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshContents() {
|
||||
Profile.getCurrent().dataSource().mapRepoAsync(
|
||||
TransactionRepository.class,
|
||||
repo -> repo.findRecentN(5)
|
||||
)
|
||||
.thenApply(transactions -> transactions.stream().map(RecentTransactionsModule::buildMiniTransactionTile).toList())
|
||||
.thenAccept(nodes -> Platform.runLater(() -> {
|
||||
transactionsVBox.getChildren().clear();
|
||||
transactionsVBox.getChildren().addAll(nodes);
|
||||
}));
|
||||
}
|
||||
|
||||
private static Node buildMiniTransactionTile(Transaction tx) {
|
||||
BorderPane borderPane = new BorderPane();
|
||||
borderPane.getStyleClass().addAll("tile", "hand-cursor");
|
||||
borderPane.setOnMouseClicked(event -> router.navigate("transactions", new TransactionsViewController.RouteContext(tx.id)));
|
||||
|
||||
Label dateLabel = new Label(DateUtil.formatUTCAsLocalWithZone(tx.getTimestamp()));
|
||||
dateLabel.getStyleClass().addAll("mono-font", "small-font", "secondary-color-text-fill");
|
||||
StyledText linkedAccountsLabel = new StyledText();
|
||||
linkedAccountsLabel.getStyleClass().addAll("small-font");
|
||||
Profile.getCurrent().dataSource().mapRepoAsync(
|
||||
TransactionRepository.class,
|
||||
repo -> repo.findLinkedAccounts(tx.id)
|
||||
)
|
||||
.thenAccept(accounts -> Platform.runLater(() -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (accounts.hasCredit()) {
|
||||
sb.append("Credited from **").append(accounts.creditAccount().getName()).append("**");
|
||||
if (accounts.hasDebit()) sb.append(". ");
|
||||
}
|
||||
if (accounts.hasDebit()) {
|
||||
sb.append("Debited to **").append(accounts.debitAccount().getName()).append("**");
|
||||
}
|
||||
linkedAccountsLabel.setText(sb.toString());
|
||||
}));
|
||||
Label descriptionLabel = new Label(tx.getDescription());
|
||||
BindingUtil.bindManagedAndVisible(descriptionLabel, descriptionLabel.textProperty().isNotEmpty());
|
||||
|
||||
Label balanceLabel = new Label(CurrencyUtil.formatMoneyWithCurrencyPrefix(tx.getMoneyAmount()));
|
||||
balanceLabel.getStyleClass().addAll("mono-font");
|
||||
|
||||
VBox contentBox = new VBox(dateLabel, descriptionLabel, linkedAccountsLabel);
|
||||
borderPane.setCenter(contentBox);
|
||||
borderPane.setRight(balanceLabel);
|
||||
|
||||
return borderPane;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="com.andrewlalis.perfin.control.DashboardController"
|
||||
>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS" fx:id="modulesScrollPane" hbarPolicy="NEVER">
|
||||
<FlowPane fx:id="modulesFlowPane"/>
|
||||
</ScrollPane>
|
||||
</VBox>
|
|
@ -15,8 +15,7 @@
|
|||
<HBox styleClass="std-padding,std-spacing">
|
||||
<Button text="Back" onAction="#goBack"/>
|
||||
<Button text="Forward" onAction="#goForward"/>
|
||||
<Button text="Accounts" onAction="#goToAccounts"/>
|
||||
<Button text="Transactions" onAction="#goToTransactions"/>
|
||||
<Button text="Dashboard" onAction="#goToDashboard"/>
|
||||
<Button text="Profiles" onAction="#viewProfiles"/>
|
||||
|
||||
<Button text="View Help" fx:id="showManualButton" onAction="#showManual"/>
|
||||
|
|
Loading…
Reference in New Issue