diff --git a/design/splash-screen.svg b/design/splash-screen.svg index 580ab2f..20472aa 100644 --- a/design/splash-screen.svg +++ b/design/splash-screen.svg @@ -26,9 +26,9 @@ inkscape:pagecheckerboard="1" inkscape:deskcolor="#505050" inkscape:document-units="px" - inkscape:zoom="2.0863221" - inkscape:cx="143.55406" - inkscape:cy="122.22466" + inkscape:zoom="1.4752525" + inkscape:cx="125.40226" + inkscape:cy="94.899008" inkscape:window-width="1920" inkscape:window-height="1025" inkscape:window-x="1080" @@ -36,6 +36,11 @@ inkscape:window-maximized="1" inkscape:current-layer="layer1" /> + sodipodi:nodetypes="cccscccccccccccccccccccscsc" />PerFinPersonal Finance diff --git a/src/main/java/com/andrewlalis/perfin/PerfinApp.java b/src/main/java/com/andrewlalis/perfin/PerfinApp.java index 1519681..ea3918f 100644 --- a/src/main/java/com/andrewlalis/perfin/PerfinApp.java +++ b/src/main/java/com/andrewlalis/perfin/PerfinApp.java @@ -7,6 +7,7 @@ import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.view.ImageCache; import com.andrewlalis.perfin.view.SceneUtil; import com.andrewlalis.perfin.view.StartupSplashScreen; +import com.andrewlalis.perfin.view.component.ScrollPaneRouterView; import javafx.application.Application; import javafx.application.Platform; import javafx.scene.Scene; @@ -38,7 +39,7 @@ public class PerfinApp extends Application { * A router that controls which help page is being viewed in the side-pane. * Certain user actions may cause this router to navigate to certain pages. */ - public static final SceneRouter helpRouter = new SceneRouter(new AnchorPaneRouterView(true)); + public static final SceneRouter helpRouter = new SceneRouter(new ScrollPaneRouterView()); public static void main(String[] args) { launch(args); @@ -75,23 +76,24 @@ public class PerfinApp extends Application { }); } - private static void mapResourceRoute(String route, String resource) { - router.map(route, PerfinApp.class.getResource(resource)); - } - private static void defineRoutes(Consumer msgConsumer) { msgConsumer.accept("Initializing application views."); Platform.runLater(() -> { - mapResourceRoute("accounts", "/accounts-view.fxml"); - mapResourceRoute("account", "/account-view.fxml"); - mapResourceRoute("edit-account", "/edit-account.fxml"); - mapResourceRoute("transactions", "/transactions-view.fxml"); - mapResourceRoute("create-transaction", "/create-transaction.fxml"); - mapResourceRoute("create-balance-record", "/create-balance-record.fxml"); + // App pages. + 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")); + 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")); - // Map help pages. - helpRouter.map("help-test", PerfinApp.class.getResource("/help-pages/help-test.fxml")); + // Help pages. helpRouter.map("home", PerfinApp.class.getResource("/help-pages/home.fxml")); + helpRouter.map("accounts", PerfinApp.class.getResource("/help-pages/accounts-view.fxml")); + helpRouter.map("adding-an-account", PerfinApp.class.getResource("/help-pages/adding-an-account.fxml")); + helpRouter.map("transactions", PerfinApp.class.getResource("/help-pages/transactions-view.fxml")); + helpRouter.map("adding-a-transaction", PerfinApp.class.getResource("/help-pages/adding-a-transaction.fxml")); + helpRouter.map("profiles", PerfinApp.class.getResource("/help-pages/profiles.fxml")); }); } diff --git a/src/main/java/com/andrewlalis/perfin/control/MainViewController.java b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java index 1872308..1a55118 100644 --- a/src/main/java/com/andrewlalis/perfin/control/MainViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/MainViewController.java @@ -3,12 +3,13 @@ package com.andrewlalis.perfin.control; import com.andrewlalis.javafx_scene_router.AnchorPaneRouterView; import com.andrewlalis.perfin.view.BindingUtil; import com.andrewlalis.perfin.view.ProfilesStage; +import com.andrewlalis.perfin.view.component.ScrollPaneRouterView; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import static com.andrewlalis.perfin.PerfinApp.helpRouter; import static com.andrewlalis.perfin.PerfinApp.router; @@ -19,7 +20,8 @@ public class MainViewController { @FXML public Button showManualButton; @FXML public Button hideManualButton; - @FXML public VBox manualVBox; + @FXML public BorderPane helpPane; + @FXML public Button helpBackButton; @FXML public void initialize() { AnchorPaneRouterView routerView = (AnchorPaneRouterView) router.getView(); @@ -41,15 +43,26 @@ public class MainViewController { router.navigate("accounts"); // Initialize the help manual components. - manualVBox.managedProperty().bind(manualVBox.visibleProperty()); - manualVBox.setVisible(false); + helpPane.managedProperty().bind(helpPane.visibleProperty()); + helpPane.setVisible(false); showManualButton.managedProperty().bind(showManualButton.visibleProperty()); - showManualButton.visibleProperty().bind(manualVBox.visibleProperty().not()); + showManualButton.visibleProperty().bind(helpPane.visibleProperty().not()); hideManualButton.managedProperty().bind(hideManualButton.visibleProperty()); - hideManualButton.visibleProperty().bind(manualVBox.visibleProperty()); + hideManualButton.visibleProperty().bind(helpPane.visibleProperty()); - AnchorPaneRouterView helpRouterView = (AnchorPaneRouterView) helpRouter.getView(); - manualVBox.getChildren().add(helpRouterView.getAnchorPane()); + helpBackButton.managedProperty().bind(helpBackButton.visibleProperty()); + helpRouter.currentRouteProperty().addListener((observable, oldValue, newValue) -> { + helpBackButton.setVisible(helpRouter.getHistory().canGoBack()); + }); + helpBackButton.setOnAction(event -> helpRouter.navigateBack()); + + ScrollPaneRouterView helpRouterView = (ScrollPaneRouterView) helpRouter.getView(); + ScrollPane helpRouterScrollPane = helpRouterView.getScrollPane(); + helpRouterScrollPane.setMinWidth(200.0); + helpRouterScrollPane.setMaxWidth(400.0); + helpRouterScrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + helpRouterScrollPane.getStyleClass().addAll("padding-extra"); + helpPane.setCenter(helpRouterScrollPane); helpRouter.navigate("home"); } @@ -77,10 +90,25 @@ public class MainViewController { } @FXML public void showManual() { - manualVBox.setVisible(true); + helpPane.setVisible(true); } @FXML public void hideManual() { - manualVBox.setVisible(false); + helpPane.setVisible(false); + } + + @FXML public void helpViewHome() { + helpRouter.getHistory().clear(); + helpRouter.navigate("home"); + } + + @FXML public void helpViewAccounts() { + helpRouter.getHistory().clear(); + helpRouter.navigate("accounts"); + } + + @FXML public void helpViewTransactions() { + helpRouter.getHistory().clear(); + helpRouter.navigate("transactions"); } } diff --git a/src/main/java/com/andrewlalis/perfin/control/ProfilesViewController.java b/src/main/java/com/andrewlalis/perfin/control/ProfilesViewController.java index 1fb72eb..edeb452 100644 --- a/src/main/java/com/andrewlalis/perfin/control/ProfilesViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/ProfilesViewController.java @@ -5,8 +5,8 @@ import com.andrewlalis.perfin.data.ProfileLoadException; import com.andrewlalis.perfin.data.util.FileUtil; import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.view.ProfilesStage; -import javafx.beans.binding.BooleanExpression; -import javafx.beans.property.BooleanProperty; +import com.andrewlalis.perfin.view.component.validation.ValidationApplier; +import com.andrewlalis.perfin.view.component.validation.validators.PredicateValidator; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Button; @@ -16,6 +16,8 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; @@ -24,22 +26,16 @@ import java.util.List; import static com.andrewlalis.perfin.PerfinApp.router; public class ProfilesViewController { + private static final Logger log = LoggerFactory.getLogger(ProfilesViewController.class); + @FXML public VBox profilesVBox; @FXML public TextField newProfileNameField; - @FXML public Text newProfileNameErrorLabel; @FXML public Button addProfileButton; @FXML public void initialize() { - BooleanExpression newProfileNameValid = BooleanProperty.booleanExpression(newProfileNameField.textProperty() - .map(text -> ( - text != null && - !text.isBlank() && - Profile.validateName(text) && - !Profile.getAvailableProfiles().contains(text) - ))); - newProfileNameErrorLabel.managedProperty().bind(newProfileNameErrorLabel.visibleProperty()); - newProfileNameErrorLabel.visibleProperty().bind(newProfileNameValid.not().and(newProfileNameField.textProperty().isNotEmpty())); - newProfileNameErrorLabel.wrappingWidthProperty().bind(newProfileNameField.widthProperty()); + var newProfileNameValid = new ValidationApplier<>(new PredicateValidator() + .addPredicate(s -> s == null || s.isBlank() || Profile.validateName(s), "Profile name should consist of only lowercase numbers.") + ).attachToTextField(newProfileNameField); addProfileButton.disableProperty().bind(newProfileNameValid.not()); refreshAvailableProfiles(); @@ -106,7 +102,7 @@ public class ProfilesViewController { } private boolean openProfile(String name, boolean showPopup) { - System.out.println("Opening profile: " + name); + log.info("Opening profile \"{}\".", name); try { Profile.load(name); ProfilesStage.closeView(); diff --git a/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java b/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java index 4714cb9..526951d 100644 --- a/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java +++ b/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java @@ -62,10 +62,15 @@ public class StartupSplashScreen extends Stage implements Consumer { private void runTasks() { Thread.ofVirtual().start(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } for (var task : tasks) { try { task.accept(this); - Thread.sleep(100); + Thread.sleep(500); } catch (Exception e) { accept("Startup failed: " + e.getMessage()); e.printStackTrace(System.err); @@ -80,7 +85,7 @@ public class StartupSplashScreen extends Stage implements Consumer { } accept("Startup successful!"); try { - Thread.sleep(500); + Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/andrewlalis/perfin/view/component/ScrollPaneRouterView.java b/src/main/java/com/andrewlalis/perfin/view/component/ScrollPaneRouterView.java new file mode 100644 index 0000000..cf6b1ea --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/ScrollPaneRouterView.java @@ -0,0 +1,23 @@ +package com.andrewlalis.perfin.view.component; + +import com.andrewlalis.javafx_scene_router.RouterView; +import javafx.scene.Parent; +import javafx.scene.control.ScrollPane; + +public class ScrollPaneRouterView implements RouterView { + private final ScrollPane scrollPane = new ScrollPane(); + + public ScrollPaneRouterView() { + scrollPane.setFitToHeight(true); + scrollPane.setFitToWidth(true); + } + + @Override + public void showRouteNode(Parent node) { + scrollPane.setContent(node); + } + + public ScrollPane getScrollPane() { + return scrollPane; + } +} diff --git a/src/main/java/com/andrewlalis/perfin/view/component/StyledText.java b/src/main/java/com/andrewlalis/perfin/view/component/StyledText.java new file mode 100644 index 0000000..b6fdfd8 --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/StyledText.java @@ -0,0 +1,181 @@ +package com.andrewlalis.perfin.view.component; + +import com.andrewlalis.perfin.PerfinApp; +import javafx.beans.DefaultProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.property.StringPropertyBase; +import javafx.geometry.Insets; +import javafx.scene.AccessibleAttribute; +import javafx.scene.control.Hyperlink; +import javafx.scene.layout.Border; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; + +import java.util.ArrayList; +import java.util.List; + +import static com.andrewlalis.perfin.PerfinApp.helpRouter; + +/** + * A component that renders markdown-ish text as a series of TextFlow elements, + * styled according to some basic styles. + */ +@DefaultProperty("text") +public class StyledText extends VBox { + private StringProperty text; + private boolean initialized = false; + + public final void setText(String value) { + if (value == null) value = ""; + textProperty().set(value); + } + + public final String getText() { + return text == null ? "" : text.get(); + } + + public final StringProperty textProperty() { + if (text == null) { + text = new StringPropertyBase("") { + @Override public Object getBean() { return StyledText.this; } + @Override public String getName() { return "text"; } + @Override public void invalidated() { + notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT); + } + }; + } + return text; + } + + @Override + protected void layoutChildren() { + if (!initialized) { + String s = getText(); + getChildren().clear(); + getChildren().addAll(renderText(s)); + getStyleClass().add("spacing-extra"); + initialized = true; + } + super.layoutChildren(); + } + + private List renderText(String text) { + return new TextFlowBuilder().build(text); + } + + private static class TextFlowBuilder { + private final List flows = new ArrayList<>(); + private int idx = 0; + private final StringBuilder currentRun = new StringBuilder(); + private TextFlow currentParagraph; + + public List build(String text) { + flows.clear(); + idx = 0; + currentRun.setLength(0); + currentParagraph = new TextFlow(); + + while (idx < text.length()) { + if (text.startsWith("**", idx)) { + parseStyledText(text, "**", "bold-text"); + } else if (text.startsWith("*", idx)) { + parseStyledText(text, "*", "italic-text"); + } else if (text.startsWith("`", idx)) { + parseStyledText(text, "`", "mono-font"); + } else if (text.startsWith(" -- ", idx)) { + parsePageBreak(text); + } else if (text.startsWith("[", idx)) { + parseLink(text); + } else if (text.startsWith("#", idx) && (idx == 0 || (idx > 0 && text.charAt(idx - 1) == ' '))) { + parseHeader(text); + } else { + currentRun.append(text.charAt(idx)); + idx++; + } + } + appendTextIfPresent(); + appendParagraphIfPresent(); + return flows; + } + + private void parsePageBreak(String text) { + appendTextIfPresent(); + appendParagraphIfPresent(); + while (text.charAt(idx) == ' ') idx++; + while (text.charAt(idx) == '-') idx++; + while (text.charAt(idx) == ' ') idx++; + } + + private void parseStyledText(String text, String marker, String styleClass) { + appendTextIfPresent(); + int endIdx = text.indexOf(marker, idx + marker.length()); + Text textItem = new Text(text.substring(idx + marker.length(), endIdx)); + textItem.getStyleClass().add(styleClass); + currentParagraph.getChildren().add(textItem); + idx = endIdx + marker.length(); + } + + private void parseLink(String text) { + appendTextIfPresent(); + int labelEndIdx = text.indexOf(']', idx); + String label = text.substring(idx + 1, labelEndIdx); + idx = labelEndIdx + 1; + final String link; + if (text.charAt(labelEndIdx + 1) == '(') { + int linkEndIdx = text.indexOf(')', labelEndIdx + 2); + link = text.substring(labelEndIdx + 2, linkEndIdx); + idx = linkEndIdx + 1; + } else { + link = null; + } + Hyperlink hyperlink = new Hyperlink(label); + if (link != null) { + if (link.startsWith("http")) { + hyperlink.setOnAction(event -> PerfinApp.instance.getHostServices().showDocument(link)); + } else if (link.startsWith("help:")) { + hyperlink.setOnAction(event -> helpRouter.navigate(link.substring(5).strip())); + } + } + hyperlink.setBorder(Border.EMPTY); + hyperlink.setPadding(new Insets(0, 0, 0, 0)); + currentParagraph.getChildren().add(hyperlink); + } + + private void parseHeader(String text) { + appendTextIfPresent(); + appendParagraphIfPresent(); + int size = 0; + while (text.charAt(idx) == '#') { + idx++; + size++; + } + int endIdx = text.indexOf("#".repeat(size), idx); + Text header = new Text(text.substring(idx, endIdx).strip()); + idx = endIdx + size; + while (text.charAt(idx) == ' ') idx++; + String styleClass = switch(size) { + case 1 -> "large-font"; + case 2 -> "largest-font"; + default -> "largest-font"; + }; + header.getStyleClass().addAll(styleClass, "bold-text"); + currentParagraph.getChildren().add(header); + appendParagraphIfPresent(); + } + + private void appendTextIfPresent() { + if (!currentRun.isEmpty()) { + currentParagraph.getChildren().add(new Text(currentRun.toString())); + currentRun.setLength(0); + } + } + + private void appendParagraphIfPresent() { + if (!currentParagraph.getChildren().isEmpty()) { + flows.add(currentParagraph); + currentParagraph = new TextFlow(); + } + } + } +} diff --git a/src/main/resources/help-pages/accounts-view.fxml b/src/main/resources/help-pages/accounts-view.fxml new file mode 100644 index 0000000..dc1b590 --- /dev/null +++ b/src/main/resources/help-pages/accounts-view.fxml @@ -0,0 +1,35 @@ + + + + + + + ## The Accounts View ## + In the *Accounts view*, you'll see an overview of all the active + accounts in your *Perfin* profile. + -- + To add a new account, simply click on **Add an Account** from the menu + at the top. [Read about how to add an account here.](help:adding-an-account) + -- + Additionally, the top menu also shows an overview of the derived total + balance for each currency you own. This adds up the balances of all + accounts of like currency, and gives you a total of each. + -- + Accounts are, by default, ordered according to the most recent activity. + That means that your most active accounts will appear *first* in this + page, and your least active accounts will appear *last*. + + # Account Tiles # + Each account's tile displays some basic information about the account + up-front, like its name, number, balance, and type. Click on a tile to + navigate to that account's page for more detailed information, and to + make changes to the account. + -- + The *current balance* shown in each account tile is derived from the + account's last-known **balance record**, and all transactions that have + happened between then and now. + + diff --git a/src/main/resources/help-pages/adding-a-transaction.fxml b/src/main/resources/help-pages/adding-a-transaction.fxml new file mode 100644 index 0000000..0994e0d --- /dev/null +++ b/src/main/resources/help-pages/adding-a-transaction.fxml @@ -0,0 +1,65 @@ + + + + + + + ## Adding a Transaction ## + When you're adding a new transaction to your *Perfin* profile, there are + some details that you should be aware of. + + # Timestamp # + The timestamp is the date and time at which the transaction took place, + in your local time zone. Generally, it's encouraged to set this as the + real timestamp at which the transaction took place (the time shown on a + receipt or invoice, for example), rather than the timestamp provided by + your financial institution once the payment has been processed. + -- + It's formatted as `yyyy-mm-dd HH:MM:SS`, so for example, November 14th, + 2015 at 4:15pm would be written as `2015-11-14 16:15:00`. You can omit + the seconds if you like, however. + -- + Also note that you may not enter timestamps from the future; it just + doesn't make sense to do so. + + # Amount # + The total amount of the transaction, as a positive decimal value. This + is the final amount which you've paid or received, including any tax, + tips, or transaction fees. + + # Currency # + The currency of the transaction. This should be the same currency as the + account(s) that the transaction is linked to. + + # Linked Debit and Credit Accounts # + Every transaction, for it to mean something, needs to be linked to one + (or sometimes two) of your accounts. A transaction's impact on an + account depends on whether the transaction is being treated as a **Debit** + or a **Credit** on the account. + -- + The account linked as **Debit** is the one whose assets will + **increase** as a result of the transaction. Some common examples of + transactions with debit-linked accounts include deposits to checking + or savings accounts, or refunds to credit cards. + -- + The account linked as **Credit** is the one whose assets will + **decrease** as a result of the transaction. Some common examples of + transactions with credit-linked accounts include payments or purchases + with a checking or savings account, or a credit card. + -- + In short, if your account is *gaining money*, link it under **debit**. + If your account is *losing money*, link it under **credit**. + -- + For transfers between two accounts that are both tracked in *Perfin*, + the *sending* account should be linked under **credit**, and the + *receiving* account under **debit**. + + # Attachments # + Often, you'll have a receipt, invoice, bank statement, or some other + document which acts as proof of a transaction. You can attach files to + a transaction to save those files with it, as a reference. The files + will be copied to your *Perfin* profile. + + diff --git a/src/main/resources/help-pages/adding-an-account.fxml b/src/main/resources/help-pages/adding-an-account.fxml new file mode 100644 index 0000000..a41288c --- /dev/null +++ b/src/main/resources/help-pages/adding-an-account.fxml @@ -0,0 +1,45 @@ + + + + + + + ## Adding an Account ## + When adding an account, you'll need to provide some basic information + about the account so that Perfin can integrate it with its automatic + balance calculations and other functions. + + # Name # + The name of the account is just a bit of text that you can use to + identify the account from the rest of yours. Make sure it's unique, and + to-the-point, since the account's name is used elsewhere in the app to + refer to the account. + + # Number # + The number of the account is the unique account number provided by your + financial institution. For checking and savings accounts, it'll be the + account number provided by your bank, and for credit cards, it'll be the + credit card's number. + + # Currency # + The currency is pretty self-explanatory; simply select the currency that + your account uses. Perfin uses 3-character *currency codes* quite often, + and you can read about them [here](https://en.wikipedia.org/wiki/ISO_4217#List_of_currency_codes). + + # Account Type # + The account's type determines how certain balance calculations and + properties of the account work. For instance, a **Credit Card** + account's balance is interpreted to be the amount you owe to the card's + provider (the amount you need to pay off), while a **Checking** + account's balance is naturally the amount of money in your account. + + # Initial Balance # + In order to accurately compute your account's balance (without needing + to check back every hour with your financial institution), Perfin needs + to know what your account's starting balance is. This way, it can use + that balance, combined with any transactions you add, to tell you your + balance at any moment in time. + + diff --git a/src/main/resources/help-pages/help-test.fxml b/src/main/resources/help-pages/help-test.fxml deleted file mode 100644 index a01b7aa..0000000 --- a/src/main/resources/help-pages/help-test.fxml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - This is a testing help page, which is meant to be shown only for - testing the help system and navigation. - - - diff --git a/src/main/resources/help-pages/home.fxml b/src/main/resources/help-pages/home.fxml index cd3a519..6a4ff20 100644 --- a/src/main/resources/help-pages/home.fxml +++ b/src/main/resources/help-pages/home.fxml @@ -1,29 +1,35 @@ - + - - - diff --git a/src/main/resources/help-pages/profiles.fxml b/src/main/resources/help-pages/profiles.fxml new file mode 100644 index 0000000..9f83d5d --- /dev/null +++ b/src/main/resources/help-pages/profiles.fxml @@ -0,0 +1,28 @@ + + + + + + + ## Profiles ## + In *Perfin*, all the accounts, transactions, attachments, bank + statements, and other financial data, are stored in a *profile*. You can + think of it as a save file. It's a single folder that encapsulates all + your data. + -- + Perfin uses profiles to make it easy to work with multiple financial + portfolios, export and import bulk data, and to let *you*, the user, + access your data directly, as opposed to hiding it in some proprietary + file format. + -- + A running Perfin program always has one profile open at a time. You can + change the active profile by clicking **Profiles** from the app's top + menu, and then clicking **Open** next to the profile you'd like to use. + -- + By default, when you first start Perfin, a profile named *default* is + created for you to use. Most of the time, you won't need to worry about + using other profiles, but if you do, now you know how. + + diff --git a/src/main/resources/help-pages/transactions-view.fxml b/src/main/resources/help-pages/transactions-view.fxml new file mode 100644 index 0000000..a627185 --- /dev/null +++ b/src/main/resources/help-pages/transactions-view.fxml @@ -0,0 +1,23 @@ + + + + + + + ## The Transactions View ## + In the *Transactions view*, you're shown a list of all transactions that + have been added to your *Perfin* profile, along with some controls for + navigating this list. + -- + The transactions are ordered, by default, according to their timestamp, + with recent transactions appearing before older ones. + -- + Simply click on a transaction to view its details or make changes. + -- + To add a new transaction, click **Add Transaction** in the top menu. You + can read more about adding a transaction [here](help:adding-a-transaction). + + diff --git a/src/main/resources/images/splash-screen.png b/src/main/resources/images/splash-screen.png index 76d1e27..1cb68f2 100644 Binary files a/src/main/resources/images/splash-screen.png and b/src/main/resources/images/splash-screen.png differ diff --git a/src/main/resources/main-view.fxml b/src/main/resources/main-view.fxml index 318e645..a68e430 100644 --- a/src/main/resources/main-view.fxml +++ b/src/main/resources/main-view.fxml @@ -5,30 +5,52 @@ - - - -