From f5f624881f3b8900fa56d655ec040b5ee5d8a0af Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Thu, 18 Apr 2024 10:32:36 -0400 Subject: [PATCH] Add placeholders for dashboard features (#642) * Add placeholders for new dashboard * Fix tests and lint errors --- app/controllers/pages_controller.rb | 9 + .../controllers/trendline_controller.js | 2 +- app/models/time_series/value.rb | 2 +- app/views/accounts/show.html.erb | 4 +- app/views/accounts/summary.html.erb | 8 +- app/views/layouts/application.html.erb | 4 +- app/views/pages/dashboard.html.erb | 180 +++++++++++------- .../dashboard/_allocation_chart.html.erb | 18 ++ .../pages/dashboard/_net_worth_chart.html.erb | 8 + app/views/shared/_balance_heading.html.erb | 23 --- app/views/shared/_modal.html.erb | 2 +- app/views/shared/_trend_change.html.erb | 4 +- app/views/shared/_value_heading.html.erb | 27 +++ config/locales/views/pages/en.yml | 14 +- 14 files changed, 196 insertions(+), 109 deletions(-) create mode 100644 app/views/pages/dashboard/_allocation_chart.html.erb create mode 100644 app/views/pages/dashboard/_net_worth_chart.html.erb delete mode 100644 app/views/shared/_balance_heading.html.erb create mode 100644 app/views/shared/_value_heading.html.erb diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 3ca53b26..9c92da59 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -7,6 +7,15 @@ class PagesController < ApplicationController @asset_series = snapshot[:asset_series] @liability_series = snapshot[:liability_series] @account_groups = Current.family.accounts.by_group(period: @period, currency: Current.family.currency) + + # TODO: Placeholders for trendlines + placeholder_series_data = 10.times.map do |i| + { date: Date.current - i.days, value: Money.new(0) } + end + @income_series = TimeSeries.new(placeholder_series_data) + @spending_series = TimeSeries.new(placeholder_series_data) + @savings_rate_series = TimeSeries.new(10.times.map { |i| { date: Date.current - i.days, value: 0 } }) + @investing_series = TimeSeries.new(placeholder_series_data) end def changelog diff --git a/app/javascript/controllers/trendline_controller.js b/app/javascript/controllers/trendline_controller.js index dba9d15a..3b2bb29e 100644 --- a/app/javascript/controllers/trendline_controller.js +++ b/app/javascript/controllers/trendline_controller.js @@ -22,7 +22,7 @@ export default class extends Controller { prepareData(series) { return series.values.map((d) => ({ date: new Date(d.date + "T00:00:00"), - value: +d.value.amount, + value: d.value.amount ? +d.value.amount : +d.value, })); } diff --git a/app/models/time_series/value.rb b/app/models/time_series/value.rb index 0b435f87..fbf8ab31 100644 --- a/app/models/time_series/value.rb +++ b/app/models/time_series/value.rb @@ -5,7 +5,7 @@ class TimeSeries::Value attr_reader :value, :date, :original def initialize(obj) - @original = obj[:original] || obj + @original = obj.fetch(:original, obj) if obj.is_a?(Hash) @date = obj[:date] diff --git a/app/views/accounts/show.html.erb b/app/views/accounts/show.html.erb index 7ef90757..e77469ef 100644 --- a/app/views/accounts/show.html.erb +++ b/app/views/accounts/show.html.erb @@ -46,10 +46,10 @@
- <%= render partial: "shared/balance_heading", locals: { + <%= render partial: "shared/value_heading", locals: { label: "Total Value", period: @period, - balance: @account.balance_money, + value: @account.balance_money, trend: @balance_series.trend } %>
diff --git a/app/views/accounts/summary.html.erb b/app/views/accounts/summary.html.erb index a9c72486..892ba587 100644 --- a/app/views/accounts/summary.html.erb +++ b/app/views/accounts/summary.html.erb @@ -9,10 +9,10 @@
- <%= render partial: "shared/balance_heading", locals: { + <%= render partial: "shared/value_heading", locals: { label: "Assets", period: @period, - balance: Current.family.assets, + value: Current.family.assets, trend: @asset_series.trend } %>
@@ -25,11 +25,11 @@
- <%= render partial: "shared/balance_heading", locals: { + <%= render partial: "shared/value_heading", locals: { label: "Liabilities", period: @period, size: "md", - balance: Current.family.liabilities, + value: Current.family.liabilities, trend: @liability_series.trend } %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index af2036b1..e2a7d01f 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -26,14 +26,14 @@
<%= safe_join(flash.map { |type, message| notification(message, type: type) }) %>
-
+
<% if content_for?(:sidebar) %> <%= yield :sidebar %> <% else %> <%= render "layouts/sidebar" %> <% end %>
-
+
<%= yield %>
diff --git a/app/views/pages/dashboard.html.erb b/app/views/pages/dashboard.html.erb index 2ca2b478..6aaee6b9 100644 --- a/app/views/pages/dashboard.html.erb +++ b/app/views/pages/dashboard.html.erb @@ -1,91 +1,127 @@
-
-

Dashboard

-

<%= t(".greeting", name: Current.user.first_name ) %>

-

<%= Date.current.strftime("%A, %b %d") %>

-
-
-
-
- <%= render partial: "shared/balance_heading", locals: { - label: "Net Worth", +
+
+

Dashboard

+

<%= t(".greeting", name: Current.user.first_name ) %>

+

<%= t(".subtitle") %>

+
+ <%= link_to new_account_path, class: "flex text-white text-sm font-medium items-center gap-1 bg-gray-900 rounded-lg p-2", data: { turbo_frame: "modal" } do %> + <%= lucide_icon("plus", class: "w-5 h-5") %> + <%= t(".new") %> + <% end %> +
+
+
+
+
+ <%= render partial: "shared/value_heading", locals: { + label: t(".net_worth"), period: @period, - balance: Current.family.net_worth, + value: Current.family.net_worth, trend: @net_worth_series.trend - } %> + } %> +
+ <%= form_with url: root_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %> + <%= render partial: "shared/period_select", locals: { value: @period.name } %> + <% end %>
- <%= form_with url: root_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %> - <%= render partial: "shared/period_select", locals: { value: @period.name } %> - <% end %> + <%= render partial: "pages/dashboard/net_worth_chart", locals: { series: @net_worth_series } %>
-
- <%= render partial: "shared/line_chart", locals: { series: @net_worth_series } %> +
+ <%= render partial: "pages/dashboard/allocation_chart" %>
-
-
-
- <%= render partial: "shared/balance_heading", locals: { - label: "Assets", - period: @period, - balance: Current.family.assets, - trend: @asset_series.trend - } %> +
+
+
+
+
+ <%= render partial: "shared/value_heading", locals: { + label: t(".income"), + period: @period, + value: @income_series.last.value, + trend: @income_series.trend + } %>
+ data-controller="trendline" + class="h-full w-2/5" + data-trendline-series-value="<%= @income_series.to_json %>" + data-trendline-classification-value="asset">
-
-
- <%= render partial: "shared/balance_heading", locals: { - label: "Liabilities", - period: @period, - size: "md", - balance: Current.family.liabilities, - trend: @liability_series.trend - } %> +
+
+
+
+ <%= render partial: "shared/value_heading", locals: { + label: t(".spending"), + period: @period, + value: @spending_series.last.value, + trend: @spending_series.trend + } %>
+ data-controller="trendline" + class="h-full w-2/5" + data-trendline-series-value="<%= @spending_series.to_json %>" + data-trendline-classification-value="asset">
+
+
+
+
+
+ <%= render partial: "shared/value_heading", locals: { + label: t(".savings_rate"), + period: @period, + value: @savings_rate_series.last.value, + trend: @savings_rate_series.trend, + is_percentage: true + } %> +
+
+
+
+
+
+
+ <%= render partial: "shared/value_heading", locals: { + label: t(".investing"), + period: @period, + value: @investing_series.last.value, + trend: @investing_series.trend + } %> +
+
-
-
-
-
- - -
-
- <%= link_to new_account_path, class: "flex items-center gap-1 p-2 text-gray-900 text-sm font-medium bg-gray-50 rounded-lg hover:bg-gray-100", data: { turbo_frame: "modal" } do %> - <%= lucide_icon("plus", class: "w-5 h-5 text-gray-500") %> -

<%= t(".new") %>

- <% end %> - <%= form_with url: root_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %> - <%= render partial: "shared/period_select", locals: { value: @period.name } %> - <% end %> -
-
-
-
- <%= render partial: "account_percentages_bar", locals: { account_groups: @account_groups[:assets].children } %> - <%= render partial: "account_percentages_table", locals: { account_groups: @account_groups[:assets].children } %> -
- +
+
+

<%= t(".transactions") %>

+
+

Coming soon...

-
+
+
+

<%= t(".recurring") %>

+
+

Coming soon...

+
+
+
+

<%= t(".categories") %>

+
+

Coming soon...

+
+
diff --git a/app/views/pages/dashboard/_allocation_chart.html.erb b/app/views/pages/dashboard/_allocation_chart.html.erb new file mode 100644 index 00000000..50797a28 --- /dev/null +++ b/app/views/pages/dashboard/_allocation_chart.html.erb @@ -0,0 +1,18 @@ +
+
+ + +
+
+
+
+

Coming soon...

+
+
+ +
+
diff --git a/app/views/pages/dashboard/_net_worth_chart.html.erb b/app/views/pages/dashboard/_net_worth_chart.html.erb new file mode 100644 index 00000000..72c43d61 --- /dev/null +++ b/app/views/pages/dashboard/_net_worth_chart.html.erb @@ -0,0 +1,8 @@ +<%# locals: (series:) %> +<% if series %> +
+<% else %> +
+

No data available for the selected period.

+
+<% end %> diff --git a/app/views/shared/_balance_heading.html.erb b/app/views/shared/_balance_heading.html.erb deleted file mode 100644 index b0caaa99..00000000 --- a/app/views/shared/_balance_heading.html.erb +++ /dev/null @@ -1,23 +0,0 @@ -<%# locals: (label:, period:, balance:, trend:, size: "lg")%> -
-

<%= label %>

-

- <%= balance.currency.symbol %> - font-medium"><%= format_money_without_symbol balance, precision: 0 %> - <%- if balance.currency.default_precision.positive? -%> - - <%= balance.currency.separator %><%= balance.cents_str %> - - <% end %> -

- <% if trend.nil? %> -

Data not available for the selected period

- <% elsif trend.direction == "flat" %> -

No change vs. prior period

- <% else %> -
- <%= render partial: "shared/trend_change", locals: { trend: trend } %> - <%= period_label(period) %> -
- <% end %> -
diff --git a/app/views/shared/_modal.html.erb b/app/views/shared/_modal.html.erb index 75174faf..c6ae75df 100644 --- a/app/views/shared/_modal.html.erb +++ b/app/views/shared/_modal.html.erb @@ -1,6 +1,6 @@ <%# locals: (content:) -%> <%= turbo_frame_tag "modal" do %> - +
<%= content %>
diff --git a/app/views/shared/_trend_change.html.erb b/app/views/shared/_trend_change.html.erb index c73e66bf..f7d9d337 100644 --- a/app/views/shared/_trend_change.html.erb +++ b/app/views/shared/_trend_change.html.erb @@ -1,10 +1,10 @@ -<%# locals: (trend:) %> +<%# locals: { trend: } %> <% styles = trend_styles(trend) %>

<% if trend.direction == "flat" %> No change <% else %> - <%= styles[:symbol] %><%= format_money trend.value.abs %> + <%= styles[:symbol] %><%= trend.value.is_a?(Money) ? format_money(trend.value.abs) : trend.value.abs %> (<%= lucide_icon(styles[:icon], class: "w-4 h-4 align-text-bottom inline") %><%= trend.percent %>%) <% end %>

diff --git a/app/views/shared/_value_heading.html.erb b/app/views/shared/_value_heading.html.erb new file mode 100644 index 00000000..af55c34a --- /dev/null +++ b/app/views/shared/_value_heading.html.erb @@ -0,0 +1,27 @@ +<%# locals: (label:, period:, value:, trend:, size: "lg", is_percentage: false)%> +
+

<%= label %>

+

+ <% if value.is_a?(Money) %> + <%= value.currency.symbol %> + font-medium"><%= format_money_without_symbol value, precision: 0 %> + <%- if value.currency.default_precision.positive? -%> + + <%= value.currency.separator %><%= value.cents_str %> + + <% end %> + <% else %> + font-medium"><%= is_percentage ? number_to_percentage(value, precision: 2) : value %> + <% end %> +

+ <% if trend.nil? %> +

Data not available for the selected period

+ <% elsif trend.direction == "flat" %> +

No change vs. prior period

+ <% else %> +
+ <%= render partial: "shared/trend_change", locals: { trend: trend } %> + <%= period_label(period) %> +
+ <% end %> +
diff --git a/config/locales/views/pages/en.yml b/config/locales/views/pages/en.yml index 0660c46d..7955dd66 100644 --- a/config/locales/views/pages/en.yml +++ b/config/locales/views/pages/en.yml @@ -2,5 +2,17 @@ en: pages: dashboard: + allocation_chart: + assets: Assets + debts: Debts + categories: Categories greeting: Welcome back, %{name} - new: New + income: Income (coming soon...) + investing: Investing (coming soon...) + net_worth: Net Worth + new: New account + recurring: Recurring + savings_rate: Savings Rate (coming soon...) + spending: Spending (coming soon...) + subtitle: Here's what's happening today + transactions: Transactions