From 384c0dd5e8f097d68a49b60d1738008fbf9f255c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Fri, 21 Sep 2018 13:57:17 +0300 Subject: [PATCH] Dashboard --- lib/data/models/payment_model.dart | 5 ++ lib/redux/dashboard/dashboard_selectors.dart | 39 ++++++++++ lib/ui/dashboard/dashboard_chart.dart | 17 ++--- lib/ui/dashboard/dashboard_panels.dart | 78 ++++++++++++++++++-- 4 files changed, 120 insertions(+), 19 deletions(-) diff --git a/lib/data/models/payment_model.dart b/lib/data/models/payment_model.dart index ebf0fdb73..625f041da 100644 --- a/lib/data/models/payment_model.dart +++ b/lib/data/models/payment_model.dart @@ -196,6 +196,11 @@ abstract class PaymentEntity extends Object @override double get listDisplayAmount => amount; + bool isBetween(String startDate, String endDate) { + return startDate.compareTo(paymentDate) <= 0 && + endDate.compareTo(paymentDate) == 1; + } + @override FormatNumberType get listDisplayAmountType => FormatNumberType.money; diff --git a/lib/redux/dashboard/dashboard_selectors.dart b/lib/redux/dashboard/dashboard_selectors.dart index 62cd9436b..95754a706 100644 --- a/lib/redux/dashboard/dashboard_selectors.dart +++ b/lib/redux/dashboard/dashboard_selectors.dart @@ -51,3 +51,42 @@ List chartOutstandingInvoices( return data; } + + +var memoizedChartPayments = memo2( + (DashboardUIState settings, BuiltMap paymentMap) => + chartPayments(settings, paymentMap)); + +List chartPayments( + DashboardUIState settings, BuiltMap paymentMap) { + final Map totals = {}; + + paymentMap.forEach((int, payment) { + if (payment.isDeleted) { + // skip it + } else if (!payment.isBetween(settings.startDate, settings.endDate)) { + // skip it + } else { + if (totals[payment.paymentDate] == null) { + totals[payment.paymentDate] = 0.0; + } + totals[payment.paymentDate] += payment.amount; + } + }); + + final List data = []; + + var date = DateTime.parse(settings.startDate); + final endDate = DateTime.parse(settings.endDate); + while (date.isBefore(endDate)) { + final key = convertDateTimeToSqlDate(date); + if (totals.containsKey(key)) { + data.add(ChartMoneyData(date, totals[key])); + } else { + data.add(ChartMoneyData(date, 0.0)); + } + date = date.add(Duration(days: 1)); + } + + return data; +} diff --git a/lib/ui/dashboard/dashboard_chart.dart b/lib/ui/dashboard/dashboard_chart.dart index 55e069133..7c6741cb6 100644 --- a/lib/ui/dashboard/dashboard_chart.dart +++ b/lib/ui/dashboard/dashboard_chart.dart @@ -67,17 +67,12 @@ class _DashboardChartState extends State { final bool isIncrease = widget.amount >= widget.previousAmount; final String changeAmount = (isIncrease ? '+' : '') + formatNumber(widget.amount - widget.previousAmount, context); - String changePercent; - if (widget.amount == 0) { - changePercent = '0%'; - } else if (widget.previousAmount == 0) { - changePercent = '∞%'; - } else { - changePercent = (isIncrease ? '+' : '-') + - formatNumber(widget.previousAmount / widget.amount * 100, context, - formatNumberType: FormatNumberType.percent); - } - final String changeString = '$changeAmount ($changePercent)'; + final changePercent = (isIncrease ? '+' : '-') + + formatNumber(widget.previousAmount / widget.amount * 100, context, + formatNumberType: FormatNumberType.percent); + final String changeString = widget.amount == 0 || widget.previousAmount == 0 + ? '' + : '$changeAmount ($changePercent)'; return FormCard( children: [ diff --git a/lib/ui/dashboard/dashboard_panels.dart b/lib/ui/dashboard/dashboard_panels.dart index 43db95aeb..efa076cc1 100644 --- a/lib/ui/dashboard/dashboard_panels.dart +++ b/lib/ui/dashboard/dashboard_panels.dart @@ -79,10 +79,10 @@ class DashboardPanels extends StatelessWidget { final series = [ charts.Series( - domainFn: (ChartMoneyData clickData, _) => clickData.date, - measureFn: (ChartMoneyData clickData, _) => clickData.amount, - colorFn: (ChartMoneyData clickData, _) => - charts.MaterialPalette.blue.shadeDefault, + domainFn: (ChartMoneyData chartData, _) => chartData.date, + measureFn: (ChartMoneyData chartData, _) => chartData.amount, + colorFn: (ChartMoneyData chartData, _) => + charts.MaterialPalette.blue.shadeDefault, id: 'invoices', displayName: settings.enableComparison ? localization.currentPeriod @@ -109,10 +109,10 @@ class DashboardPanels extends StatelessWidget { series.add( charts.Series( - domainFn: (ChartMoneyData clickData, _) => clickData.date, - measureFn: (ChartMoneyData clickData, _) => clickData.amount, - colorFn: (ChartMoneyData clickData, _) => - charts.MaterialPalette.gray.shadeDefault, + domainFn: (ChartMoneyData chartData, _) => chartData.date, + measureFn: (ChartMoneyData chartData, _) => chartData.amount, + colorFn: (ChartMoneyData chartData, _) => + charts.MaterialPalette.gray.shadeDefault, id: 'previous', displayName: localization.previousPeriod, data: previousData, @@ -131,12 +131,74 @@ class DashboardPanels extends StatelessWidget { title: localization.invoices); } + Widget _paymentChart(BuildContext context) { + final localization = AppLocalization.of(context); + final settings = viewModel.dashboardUIState; + + final data = memoizedChartPayments( + settings, viewModel.state.paymentState.map); + + final series = [ + charts.Series( + domainFn: (ChartMoneyData chartData, _) => chartData.date, + measureFn: (ChartMoneyData chartData, _) => chartData.amount, + colorFn: (ChartMoneyData chartData, _) => + charts.MaterialPalette.blue.shadeDefault, + id: 'payments', + displayName: settings.enableComparison + ? localization.currentPeriod + : localization.payments, + data: data, + ), + ]; + + double total = 0.0; + double previousTotal = 0.0; + data.forEach((dynamic item) { + total += item.amount; + }); + + if (settings.enableComparison) { + final offsetData = memoizedChartOutstandingInvoices( + viewModel.dashboardUIState.rebuild((b) => b..offset += 1), + viewModel.state.invoiceState.map); + + final List previousData = []; + for (int i = 0; i < data.length; i++) { + previousData.add(ChartMoneyData(data[i].date, offsetData[i].amount)); + } + + series.add( + charts.Series( + domainFn: (ChartMoneyData chartData, _) => chartData.date, + measureFn: (ChartMoneyData chartData, _) => chartData.amount, + colorFn: (ChartMoneyData chartData, _) => + charts.MaterialPalette.gray.shadeDefault, + id: 'previous', + displayName: localization.previousPeriod, + data: previousData, + ), + ); + + previousData.forEach((dynamic item) { + previousTotal += item.amount; + }); + } + + return DashboardChart( + series: series, + amount: total, + previousAmount: previousTotal, + title: localization.payments); + } + @override Widget build(BuildContext context) { return ListView( children: [ _header(context), _invoiceChart(context), + _paymentChart(context), ], ); }