From 495abff1c2e1f7be1f51bd8bb33ac1eb67a85b46 Mon Sep 17 00:00:00 2001 From: Isaac Noda Date: Fri, 22 Oct 2021 18:42:16 -0600 Subject: [PATCH] Updates Year insights information --- app/assets/stylesheets/grid.scss | 6 +- app/assets/stylesheets/pages/insights.scss | 15 +++++ app/controllers/api/v1/reports_controller.rb | 38 +++++++++++-- app/javascript/components/insights/Month.jsx | 16 +++--- .../components/insights/PieChart.jsx | 2 +- app/javascript/components/insights/Year.jsx | 55 +++++++++++++------ app/javascript/helpers/numerics.js | 24 ++++++++ 7 files changed, 122 insertions(+), 34 deletions(-) diff --git a/app/assets/stylesheets/grid.scss b/app/assets/stylesheets/grid.scss index 51fce80..8e121fe 100644 --- a/app/assets/stylesheets/grid.scss +++ b/app/assets/stylesheets/grid.scss @@ -15,6 +15,10 @@ margin-top: 10px; } +.mt-50-sm { + margin-top: 50px; +} + @media (min-width: 750px) { .column, .columns { margin-left: 4%; @@ -79,7 +83,7 @@ display: flex !important; } - .mt-10-sm { + .mt-10-sm, .mt-50-sm { margin-top: 0; } } diff --git a/app/assets/stylesheets/pages/insights.scss b/app/assets/stylesheets/pages/insights.scss index 0268dd6..bb3de65 100644 --- a/app/assets/stylesheets/pages/insights.scss +++ b/app/assets/stylesheets/pages/insights.scss @@ -5,3 +5,18 @@ box-shadow: 2px 2px #d6d4d4; margin-top: 30px; } + +#insights .totals table { + width: 100%; + border-collapse: collapse; + + td { + border: 1px solid #d3d3d3; + text-align: left; + padding: 15px 10px; + } + + td.total { + background-color: #e7e7e7; + } +} diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index a0e4f29..5a2c124 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -1,23 +1,49 @@ module Api; module V1 class ReportsController < BaseController def year - results = ActiveRecord::Base.connection.execute(%{ - select categories.name AS category, to_char(date_trunc('month', paid_at), 'Mon') AS month, sum(expenses.amount)/100.0 AS amount + total = ActiveRecord::Base.connection.execute(%{ + select sum(expenses.amount) as amount + from expenses + where paid_at >= '#{params[:year].to_i}-01-01' + and paid_at < '#{params[:year].to_i + 1}-01-01' + }).first['amount'] + + category_percentages = ActiveRecord::Base.connection.execute(%{ + select categories.name AS category, to_char(date_trunc('month', paid_at), 'Mon') AS month, sum(expenses.amount) / #{total.to_f} AS percentage from expenses join categories on expenses.category_id = categories.id where paid_at >= '#{params[:year].to_i}-01-01' and paid_at < '#{params[:year].to_i + 1}-01-01' - group by month, categories.name; + group by month, categories.name + order by percentage; }) - total = ActiveRecord::Base.connection.execute(%{ - select sum(expenses.amount) as amount + category_totals = ActiveRecord::Base.connection.execute(%{ + select categories.name AS category, sum(expenses.amount) AS amount from expenses + join categories on expenses.category_id = categories.id where paid_at >= '#{params[:year].to_i}-01-01' and paid_at < '#{params[:year].to_i + 1}-01-01' + group by categories.name + order by amount; + }) + + category_amounts_by_month = ActiveRecord::Base.connection.execute(%{ + select categories.name AS category, to_char(date_trunc('month', paid_at), 'Mon') AS month, sum(expenses.amount) AS amount + from expenses + join categories on expenses.category_id = categories.id + where paid_at >= '#{params[:year].to_i}-01-01' + and paid_at < '#{params[:year].to_i + 1}-01-01' + group by month, categories.name; }) - render json: { results: results, total: total.first['amount'], categories: Category.all.select(:id, :name, :color).order(:name) } + render json: { + category_percentages: category_percentages, + category_totals: category_totals, + category_amounts_by_month: category_amounts_by_month, + total: total, + categories: Category.all.select(:id, :name, :color).order(:name) + } end def month diff --git a/app/javascript/components/insights/Month.jsx b/app/javascript/components/insights/Month.jsx index 18f74ff..f72a50c 100644 --- a/app/javascript/components/insights/Month.jsx +++ b/app/javascript/components/insights/Month.jsx @@ -53,6 +53,14 @@ const Month = ({ availableMonths }) => {
+
+
+ Total spend +

+ {Numerics.centsToDollars(spend)} +

+
+
{!goal && ( @@ -70,14 +78,6 @@ const Month = ({ availableMonths }) => {
)}
-
-
- Total spend -

- {Numerics.centsToDollars(spend)} -

-
-
diff --git a/app/javascript/components/insights/PieChart.jsx b/app/javascript/components/insights/PieChart.jsx index 575208e..f371dd9 100644 --- a/app/javascript/components/insights/PieChart.jsx +++ b/app/javascript/components/insights/PieChart.jsx @@ -24,7 +24,7 @@ const PieChart = ({ data, labels, colors }) => { }, tooltips: { callbacks: { - label: t => `${labels[t.index]}: $${Numerics.commify(data[t.index].toFixed(2))}`, + label: t => `${labels[t.index]}: ${data[t.index]}%`, }, }, }, diff --git a/app/javascript/components/insights/Year.jsx b/app/javascript/components/insights/Year.jsx index 37d6eb4..4c9917e 100644 --- a/app/javascript/components/insights/Year.jsx +++ b/app/javascript/components/insights/Year.jsx @@ -9,6 +9,7 @@ import { Numerics } from '../../helpers/main'; const Year = ({ availableYears }) => { const [year, setYear] = useState(availableYears[0]); const [yearTotal, setYearTotal] = useState(0); + const [categoryTotals, setCategoryTotals] = useState([]); const barChartLabels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const [barChartData, setBarChartData] = useState([]); const [pieChartData, setPieChartData] = useState({ @@ -22,28 +23,27 @@ const Year = ({ availableYears }) => { useEffect(() => { Reports.year({ year }).then( (resp) => { - const barChartDatasets = []; - resp.categories.forEach((category) => { - const dataPoints = []; - barChartLabels.forEach((mon) => { - const spendForCategoryAndMonth = resp.results.find((monthData) => monthData.month == mon && monthData.category == category.name); - dataPoints.push(spendForCategoryAndMonth ? spendForCategoryAndMonth.amount : 0); + const barChartDatasets = resp.categories.map((c) => { + const dataPoints = barChartLabels.map((mon) => { + const amount = resp.category_amounts_by_month.find((a) => a.month == mon && a.category == c.name)?.amount; + return Numerics.centsToFloat(amount || 0); }); - barChartDatasets.push({ label: category.name, data: dataPoints, backgroundColor: category.color }); + return { label: c.name, data: dataPoints, backgroundColor: c.color }; }); const pieChartDatasets = []; const pieChartLabels = []; const pieChartColors = []; - resp.categories.forEach((category) => { - pieChartLabels.push(category.name); - pieChartColors.push(category.color); - const totalForCategory = resp.results.filter((monthData) => monthData.category === category.name).reduce((a, b) => a + parseFloat(b.amount), 0); - pieChartDatasets.push(totalForCategory); + resp.categories.forEach((c) => { + pieChartLabels.push(c.name); + pieChartColors.push(c.color); + const percentage = resp.category_percentages.find((p) => p.category === c.name)?.percentage; + pieChartDatasets.push(Numerics.floatToPercent(percentage || 0)); }); setBarChartData(barChartDatasets); setPieChartData({ data: pieChartDatasets, colors: pieChartColors, labels: pieChartLabels }); + setCategoryTotals(resp.category_totals); setYearTotal(resp.total); }, () => { Alerts.genericError(); }, @@ -53,9 +53,6 @@ const Year = ({ availableYears }) => { return (
- - Total spend: {Numerics.centsToDollars(yearTotal)} -