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 @@
-
+
-
-
-
-
-
- This is the homepage for Perfin's help pages: a manual for how to
- use Perfin safely and effectively to manage your personal finances.
-
-
-
-
- Search for a topic below, or click on the help icon beside some
- component to navigate to its page.
-
-
+
+ ## Homepage ##
+ This is the homepage for Perfin's help pages: a manual for how to use
+ Perfin safely and effectively to manage your personal finances.
+ --
+ Search for a topic below, or follow one of the index links.
+
-
-
+
+
+ # Help Pages Index #
+ The following is a list of all help pages.
+ --
+ [Accounts View](help:accounts)
+ --
+ [Adding an Account](help:adding-an-account)
+ --
+ [Transactions View](help:transactions)
+ --
+ [Adding a Transaction](help:adding-a-transaction)
+ --
+ [Profiles](help:profiles)
+
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 @@
-
-
-
-
-
-
-
-
+