Refactored styling in most views and components to follow base CSS.
This commit is contained in:
parent
a914197634
commit
02d392d6c7
|
@ -10,8 +10,13 @@ import com.andrewlalis.perfin.view.StartupSplashScreen;
|
|||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
@ -21,6 +26,7 @@ import java.util.function.Consumer;
|
|||
* The class from which the JavaFX-based application starts.
|
||||
*/
|
||||
public class PerfinApp extends Application {
|
||||
private static final Logger log = LoggerFactory.getLogger(PerfinApp.class);
|
||||
public static final Path APP_DIR = Path.of(System.getProperty("user.home", "."), ".perfin");
|
||||
public static PerfinApp instance;
|
||||
|
||||
|
@ -36,6 +42,7 @@ public class PerfinApp extends Application {
|
|||
@Override
|
||||
public void start(Stage stage) {
|
||||
instance = this;
|
||||
loadFonts();
|
||||
var splashScreen = new StartupSplashScreen(List.of(
|
||||
PerfinApp::defineRoutes,
|
||||
PerfinApp::initAppDir,
|
||||
|
@ -97,4 +104,27 @@ public class PerfinApp extends Application {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadFonts() {
|
||||
List<String> fontResources = List.of(
|
||||
"/font/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Medium.ttf",
|
||||
"/font/Roboto/Roboto-Regular.ttf",
|
||||
"/font/Roboto/Roboto-Bold.ttf",
|
||||
"/font/Roboto/Roboto-Italic.ttf",
|
||||
"/font/Roboto/Roboto-BoldItalic.ttf"
|
||||
);
|
||||
for (String res : fontResources) {
|
||||
URL resourceUrl = PerfinApp.class.getResource(res);
|
||||
if (resourceUrl == null) {
|
||||
log.warn("Font resource {} was not found.", res);
|
||||
} else {
|
||||
Font font = Font.loadFont(PerfinApp.class.getResource(res).toExternalForm(), 10);
|
||||
if (font == null) {
|
||||
log.warn("Failed to load font {}.", res);
|
||||
} else {
|
||||
log.debug("Loaded font: Family = {}, Name = {}, Style = {}.", font.getFamily(), font.getName(), font.getStyle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ public class MainViewController {
|
|||
breadCrumb -> {
|
||||
Label label = new Label("> " + breadCrumb.route());
|
||||
if (breadCrumb.current()) {
|
||||
label.setStyle("-fx-font-weight: bold");
|
||||
label.getStyleClass().add("bold-text");
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
|
|
@ -66,22 +66,15 @@ public class ProfilesViewController {
|
|||
for (String profileName : profileNames) {
|
||||
boolean isCurrent = profileName.equals(currentProfile);
|
||||
AnchorPane profilePane = new AnchorPane();
|
||||
profilePane.setStyle("""
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
""");
|
||||
profilePane.getStyleClass().add("tile");
|
||||
|
||||
Text nameTextElement = new Text(profileName);
|
||||
nameTextElement.setStyle("-fx-font-size: large;");
|
||||
nameTextElement.getStyleClass().add("large-font");
|
||||
TextFlow nameLabel = new TextFlow(nameTextElement);
|
||||
if (isCurrent) {
|
||||
nameTextElement.setStyle("-fx-font-size: large; -fx-font-weight: bold;");
|
||||
nameTextElement.getStyleClass().addAll("large-font", "bold-text");
|
||||
Text currentProfileIndicator = new Text(" Currently Selected Profile");
|
||||
currentProfileIndicator.setStyle("""
|
||||
-fx-font-size: small;
|
||||
-fx-fill: grey;
|
||||
""");
|
||||
currentProfileIndicator.getStyleClass().addAll("small-font", "secondary-color-fill");
|
||||
nameLabel.getChildren().add(currentProfileIndicator);
|
||||
}
|
||||
AnchorPane.setLeftAnchor(nameLabel, 0.0);
|
||||
|
|
|
@ -39,6 +39,16 @@ public class Account extends IdEntity {
|
|||
return "..." + accountNumber.substring(accountNumber.length() - suffixLength);
|
||||
}
|
||||
|
||||
public String getAccountNumberGrouped(int groupSize, char separator) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int idx = 0;
|
||||
while (idx < accountNumber.length()) {
|
||||
sb.append(accountNumber.charAt(idx++));
|
||||
if (idx % groupSize == 0 && idx < accountNumber.length()) sb.append(separator);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
String numberSuffix = getAccountNumberSuffix();
|
||||
return name + " (" + numberSuffix + ")";
|
||||
|
|
|
@ -87,9 +87,10 @@ public class AccountEntry extends IdEntity {
|
|||
* @return The effective value of this entry, either positive or negative.
|
||||
*/
|
||||
public BigDecimal getEffectiveValue(AccountType accountType) {
|
||||
return switch (accountType) {
|
||||
case CHECKING, SAVINGS -> type == Type.DEBIT ? amount : amount.negate();
|
||||
case CREDIT_CARD -> type == Type.DEBIT ? amount.negate() : amount;
|
||||
};
|
||||
if (accountType.areDebitsPositive()) {
|
||||
return type == Type.DEBIT ? amount : amount.negate();
|
||||
} else {
|
||||
return type == Type.DEBIT ? amount.negate() : amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,20 @@ package com.andrewlalis.perfin.model;
|
|||
* Represents the different possible account types in Perfin.
|
||||
*/
|
||||
public enum AccountType {
|
||||
CHECKING("Checking"),
|
||||
SAVINGS("Savings"),
|
||||
CREDIT_CARD("Credit Card");
|
||||
CHECKING("Checking", true),
|
||||
SAVINGS("Savings", true),
|
||||
CREDIT_CARD("Credit Card", false);
|
||||
|
||||
private final String name;
|
||||
private final boolean debitsPositive;
|
||||
|
||||
AccountType(String name) {
|
||||
AccountType(String name, boolean debitsPositive) {
|
||||
this.name = name;
|
||||
this.debitsPositive = debitsPositive;
|
||||
}
|
||||
|
||||
public boolean areDebitsPositive() {
|
||||
return debitsPositive;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,7 @@ public class AccountComboBoxCellFactory implements Callback<ListView<Account>, L
|
|||
|
||||
public AccountListCell(String emptyCellText) {
|
||||
this.emptyCellText = emptyCellText;
|
||||
label.setStyle("-fx-text-fill: black;");
|
||||
label.getStyleClass().add("normal-color-text-fill");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,7 +53,10 @@ public class StartupSplashScreen extends Stage implements Consumer<String> {
|
|||
textArea.setFocusTraversable(false);
|
||||
|
||||
Scene scene = new Scene(root, 400.0, 200.0);
|
||||
scene.getStylesheets().add(StartupSplashScreen.class.getResource("/style/startup-splash-screen.css").toExternalForm());
|
||||
scene.getStylesheets().addAll(
|
||||
StartupSplashScreen.class.getResource("/style/base.css").toExternalForm(),
|
||||
StartupSplashScreen.class.getResource("/style/startup-splash-screen.css").toExternalForm()
|
||||
);
|
||||
return scene;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,10 @@ import javafx.scene.layout.BorderPane;
|
|||
*/
|
||||
public abstract class AccountHistoryItemTile extends BorderPane {
|
||||
public AccountHistoryItemTile(AccountHistoryItem item) {
|
||||
setStyle("""
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
""");
|
||||
getStyleClass().add("tile");
|
||||
|
||||
Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp()));
|
||||
timestampLabel.setStyle("-fx-font-size: small;");
|
||||
timestampLabel.getStyleClass().add("small-font");
|
||||
setTop(timestampLabel);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.AccountType;
|
||||
import com.andrewlalis.perfin.model.MoneyValue;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
|
@ -14,6 +17,7 @@ import javafx.scene.layout.Priority;
|
|||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
@ -30,14 +34,7 @@ public class AccountTile extends BorderPane {
|
|||
|
||||
public AccountTile(Account account) {
|
||||
setPrefWidth(350.0);
|
||||
setStyle("""
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-style: solid;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
-fx-cursor: hand;
|
||||
""");
|
||||
getStyleClass().addAll("tile", "hand-cursor");
|
||||
|
||||
setTop(getHeader(account));
|
||||
setBottom(getFooter(account));
|
||||
|
@ -48,7 +45,7 @@ public class AccountTile extends BorderPane {
|
|||
|
||||
private Node getHeader(Account account) {
|
||||
Text title = new Text("Account #" + account.id);
|
||||
title.setStyle("-fx-font-size: large; -fx-font-weight: bold;");
|
||||
title.getStyleClass().addAll("large-font", "bold-text");
|
||||
return title;
|
||||
}
|
||||
|
||||
|
@ -56,7 +53,7 @@ public class AccountTile extends BorderPane {
|
|||
Label currencyLabel = new Label(account.getCurrency().getCurrencyCode());
|
||||
Label typeLabel = new Label(account.getType().toString() + " Account");
|
||||
HBox footerHBox = new HBox(currencyLabel, typeLabel);
|
||||
footerHBox.setStyle("-fx-font-size: x-small; -fx-spacing: 3px;");
|
||||
footerHBox.getStyleClass().addAll("std-spacing", "small-font");
|
||||
return footerHBox;
|
||||
}
|
||||
|
||||
|
@ -73,15 +70,33 @@ public class AccountTile extends BorderPane {
|
|||
valueConstraints.setHalignment(HPos.RIGHT);
|
||||
propertiesPane.getColumnConstraints().setAll(keyConstraints, valueConstraints);
|
||||
|
||||
Label accountNameLabel = newPropertyValue(account.getName());
|
||||
Label accountNameLabel = new Label(account.getName());
|
||||
accountNameLabel.setWrapText(true);
|
||||
accountNameLabel.getStyleClass().add("italic-text");
|
||||
|
||||
Label accountTypeLabel = newPropertyValue(account.getType().toString());
|
||||
Label accountNumberLabel = new Label(account.getAccountNumber());
|
||||
accountNumberLabel.getStyleClass().add("mono-font");
|
||||
|
||||
Label accountTypeLabel = new Label(account.getType().toString());
|
||||
accountTypeLabel.setTextFill(ACCOUNT_TYPE_COLORS.get(account.getType()));
|
||||
accountTypeLabel.setStyle("-fx-font-weight: bold;");
|
||||
accountTypeLabel.getStyleClass().add("bold-text");
|
||||
|
||||
Label balanceLabel = newPropertyValue("Computing balance...");
|
||||
Label balanceLabel = new Label("Computing balance...");
|
||||
balanceLabel.getStyleClass().addAll("mono-font");
|
||||
balanceLabel.setDisable(true);
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
BigDecimal balance = repo.deriveCurrentBalance(account.id);
|
||||
String text = CurrencyUtil.formatMoney(new MoneyValue(balance, account.getCurrency()));
|
||||
Platform.runLater(() -> {
|
||||
balanceLabel.setText(text);
|
||||
if (account.getType().areDebitsPositive() && balance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
balanceLabel.getStyleClass().add("negative-color-text-fill");
|
||||
} else if (!account.getType().areDebitsPositive() && balance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
balanceLabel.getStyleClass().add("positive-color-text-fill");
|
||||
}
|
||||
balanceLabel.setDisable(false);
|
||||
});
|
||||
}));
|
||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account, text -> {
|
||||
balanceLabel.setText(text);
|
||||
balanceLabel.setDisable(false);
|
||||
|
@ -91,7 +106,7 @@ public class AccountTile extends BorderPane {
|
|||
newPropertyLabel("Account Name"),
|
||||
accountNameLabel,
|
||||
newPropertyLabel("Account Number"),
|
||||
newPropertyValue(account.getAccountNumber()),
|
||||
accountNumberLabel,
|
||||
newPropertyLabel("Account Type"),
|
||||
accountTypeLabel,
|
||||
newPropertyLabel("Current Balance"),
|
||||
|
@ -102,18 +117,7 @@ public class AccountTile extends BorderPane {
|
|||
|
||||
private static Label newPropertyLabel(String text) {
|
||||
Label lbl = new Label(text);
|
||||
lbl.setStyle("""
|
||||
-fx-font-weight: bold;
|
||||
""");
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private static Label newPropertyValue(String text) {
|
||||
Label lbl = new Label(text);
|
||||
lbl.setStyle("""
|
||||
-fx-font-family: monospace;
|
||||
-fx-font-size: large;
|
||||
""");
|
||||
lbl.getStyleClass().add("bold-text");
|
||||
return lbl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class AttachmentPreview extends BorderPane {
|
|||
public AttachmentPreview(Attachment attachment) {
|
||||
BorderPane contentContainer = new BorderPane();
|
||||
Label nameLabel = new Label(attachment.getFilename());
|
||||
nameLabel.setStyle("-fx-font-size: small;");
|
||||
nameLabel.getStyleClass().add("small-font");
|
||||
VBox nameContainer = new VBox(nameLabel);
|
||||
nameContainer.setPrefHeight(LABEL_SIZE);
|
||||
nameContainer.setMaxHeight(LABEL_SIZE);
|
||||
|
|
|
@ -27,42 +27,30 @@ import static com.andrewlalis.perfin.PerfinApp.router;
|
|||
*/
|
||||
public class TransactionTile extends BorderPane {
|
||||
public final BooleanProperty selected = new SimpleBooleanProperty(false);
|
||||
private static final String UNSELECTED_STYLE = """
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-style: solid;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
-fx-cursor: hand;
|
||||
""";
|
||||
private static final String SELECTED_STYLE = """
|
||||
-fx-border-color: white;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-style: solid;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
-fx-cursor: hand;
|
||||
""";
|
||||
|
||||
public TransactionTile(Transaction transaction) {
|
||||
setStyle(UNSELECTED_STYLE);
|
||||
getStyleClass().addAll("tile", "hand-cursor");
|
||||
|
||||
setTop(getHeader(transaction));
|
||||
setCenter(getBody(transaction));
|
||||
setBottom(getFooter(transaction));
|
||||
|
||||
styleProperty().bind(selected.map(value -> value ? SELECTED_STYLE : UNSELECTED_STYLE));
|
||||
selected.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
getStyleClass().add("tile-border-selected");
|
||||
} else {
|
||||
getStyleClass().remove("tile-border-selected");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Node getHeader(Transaction transaction) {
|
||||
Label currencyLabel = new Label(CurrencyUtil.formatMoney(transaction.getMoneyAmount()));
|
||||
currencyLabel.setStyle("-fx-font-family: monospace;");
|
||||
currencyLabel.getStyleClass().add("mono-font");
|
||||
HBox headerHBox = new HBox(
|
||||
currencyLabel
|
||||
);
|
||||
headerHBox.setStyle("""
|
||||
-fx-spacing: 3px;
|
||||
""");
|
||||
headerHBox.getStyleClass().addAll("std-spacing");
|
||||
return headerHBox;
|
||||
}
|
||||
|
||||
|
@ -77,14 +65,14 @@ public class TransactionTile extends BorderPane {
|
|||
Hyperlink link = new Hyperlink(acc.getShortName());
|
||||
link.setOnAction(event -> router.navigate("account", acc));
|
||||
Text prefix = new Text("Credited from");
|
||||
prefix.setFill(Color.RED);
|
||||
prefix.getStyleClass().add("negative-color-fill");
|
||||
Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link)));
|
||||
});
|
||||
accounts.ifDebit(acc -> {
|
||||
Hyperlink link = new Hyperlink(acc.getShortName());
|
||||
link.setOnAction(event -> router.navigate("account", acc));
|
||||
Text prefix = new Text("Debited to");
|
||||
prefix.setFill(Color.GREEN);
|
||||
prefix.getStyleClass().add("positive-color-fill");
|
||||
Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link)));
|
||||
});
|
||||
});
|
||||
|
@ -96,10 +84,7 @@ public class TransactionTile extends BorderPane {
|
|||
HBox footerHBox = new HBox(
|
||||
timestampLabel
|
||||
);
|
||||
footerHBox.setStyle("""
|
||||
-fx-spacing: 3px;
|
||||
-fx-font-size: small;
|
||||
""");
|
||||
footerHBox.getStyleClass().addAll("std-spacing", "small-font");
|
||||
return footerHBox;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<VBox>
|
||||
<Label text="Current Balance" styleClass="bold-text" fx:id="balanceLabel"/>
|
||||
<Text
|
||||
style="-fx-font-size: x-small; -fx-fill: grey"
|
||||
styleClass="small-font,secondary-color-fill"
|
||||
wrappingWidth="${balanceLabel.width}"
|
||||
>Computed using the last recorded balance and all transactions since.</Text>
|
||||
</VBox>
|
||||
|
@ -65,7 +65,7 @@
|
|||
<Label text="History" styleClass="bold-text,std-padding"/>
|
||||
<VBox>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||
<VBox fx:id="historyItemsVBox" style="-fx-padding: 10px; -fx-spacing: 10px;"/>
|
||||
<VBox fx:id="historyItemsVBox" styleClass="padding-extra-1,spacing-extra-1"/>
|
||||
</ScrollPane>
|
||||
<AnchorPane>
|
||||
<Button
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<center>
|
||||
<VBox>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
|
||||
<FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="5" hgap="5" styleClass="std-padding"/>
|
||||
<FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="10" hgap="10" styleClass="padding-extra-1"/>
|
||||
</ScrollPane>
|
||||
<Label fx:id="noAccountsLabel" BorderPane.alignment="TOP_LEFT" styleClass="std-padding" text="No accounts have been added to this profile."/>
|
||||
</VBox>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</VBox>
|
||||
<VBox>
|
||||
<Label text="Description" labelFor="${descriptionField}" styleClass="bold-text"/>
|
||||
<Label text="Maximum of 255 characters." styleClass="small-text"/>
|
||||
<Label text="Maximum of 255 characters." styleClass="small-font"/>
|
||||
<TextArea
|
||||
fx:id="descriptionField"
|
||||
styleClass="mono-font"
|
||||
|
@ -57,7 +57,7 @@
|
|||
<!-- Container for attachments -->
|
||||
<VBox fx:id="attachmentsVBox">
|
||||
<Label text="Attachments" styleClass="bold-text"/>
|
||||
<Label text="Attach receipts, invoices, or other content to this transaction." styleClass="small-text" wrapText="true"/>
|
||||
<Label text="Attach receipts, invoices, or other content to this transaction." styleClass="small-font" wrapText="true"/>
|
||||
<!-- FileSelectionArea inserted here! -->
|
||||
</VBox>
|
||||
</VBox>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<Button text="Transactions" onAction="#goToTransactions"/>
|
||||
<Button text="Profiles" onAction="#viewProfiles"/>
|
||||
</HBox>
|
||||
<HBox fx:id="breadcrumbHBox" styleClass="std-spacing,small-text"/>
|
||||
<HBox fx:id="breadcrumbHBox" styleClass="std-spacing,small-font"/>
|
||||
</VBox>
|
||||
</top>
|
||||
<bottom>
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
.root {
|
||||
-fx-font-family: "Roboto", sans-serif;
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: rgb(26, 26, 26);
|
||||
}
|
||||
|
||||
.mono-font {
|
||||
-fx-font-family: monospace;
|
||||
-fx-font-family: "JetBrains Mono", monospace;
|
||||
}
|
||||
|
||||
.large-font {
|
||||
-fx-font-size: 18px;
|
||||
}
|
||||
|
||||
.small-font {
|
||||
-fx-font-size: 10px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
|
@ -11,8 +25,8 @@
|
|||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.small-text {
|
||||
-fx-font-size: small;
|
||||
.italic-text {
|
||||
-fx-font-style: italic;
|
||||
}
|
||||
|
||||
.large-text {
|
||||
|
@ -22,14 +36,62 @@
|
|||
.std-padding {
|
||||
-fx-padding: 3px;
|
||||
}
|
||||
.padding-extra {
|
||||
-fx-padding: 6px;
|
||||
}
|
||||
.padding-extra-1 {
|
||||
-fx-padding: 10px;
|
||||
}
|
||||
|
||||
.std-spacing {
|
||||
-fx-spacing: 3px;
|
||||
}
|
||||
|
||||
.spacing-extra {
|
||||
-fx-spacing: 6px;
|
||||
}
|
||||
.spacing-extra-1 {
|
||||
-fx-spacing: 10px;
|
||||
}
|
||||
|
||||
.hand-cursor {
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
/* Standard "tile" styling. */
|
||||
.tile {
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-style: solid;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-padding: 5px;
|
||||
}
|
||||
|
||||
.tile-border-selected {
|
||||
-fx-border-color: darkgray;
|
||||
}
|
||||
|
||||
/* Standard colors */
|
||||
.normal-color-text-fill {
|
||||
-fx-text-fill: rgb(26, 26, 26);
|
||||
}
|
||||
|
||||
.secondary-color-fill {
|
||||
-fx-fill: rgb(99, 99, 99);
|
||||
}
|
||||
|
||||
.negative-color-text-fill {
|
||||
-fx-text-fill: rgb(247, 37, 69);
|
||||
}
|
||||
.negative-color-fill {
|
||||
-fx-fill: rgb(247, 37, 69);
|
||||
}
|
||||
|
||||
.positive-color-text-fill {
|
||||
-fx-text-fill: rgb(43, 196, 77);
|
||||
}
|
||||
.positive-color-fill {
|
||||
-fx-fill: rgb(43, 196, 77);
|
||||
}
|
||||
|
||||
/* DEBUG BORDERS */
|
||||
.debug-border-1 {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</top>
|
||||
<center>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||
<VBox fx:id="transactionsVBox" styleClass="std-padding,spacing-extra"/>
|
||||
<VBox fx:id="transactionsVBox" styleClass="padding-extra-1,spacing-extra-1"/>
|
||||
</ScrollPane>
|
||||
</center>
|
||||
</BorderPane>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package com.andrewlalis.perfin.model;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Currency;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class AccountTest {
|
||||
@Test
|
||||
public void testGetAccountNumberSuffix() {
|
||||
assertEquals("...1234", getTestAccountWithNumber("4328-1234").getAccountNumberSuffix());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccountNumberGrouped() {
|
||||
assertEquals(
|
||||
"1234-5678-9101-1121",
|
||||
getTestAccountWithNumber("1234567891011121")
|
||||
.getAccountNumberGrouped(4, '-')
|
||||
);
|
||||
assertEquals(
|
||||
"123-456-7",
|
||||
getTestAccountWithNumber("1234567")
|
||||
.getAccountNumberGrouped(3, '-')
|
||||
);
|
||||
}
|
||||
|
||||
private Account getTestAccountWithNumber(String num) {
|
||||
return new Account(
|
||||
1,
|
||||
LocalDateTime.now(),
|
||||
false,
|
||||
AccountType.CHECKING,
|
||||
num,
|
||||
"Testing Account",
|
||||
Currency.getInstance("USD")
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue