From a9d565e8bd5f7016a54d299f45f5c1b378cdc2b1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 8 Aug 2026 08:35:03 +0200 Subject: [PATCH] Align axis of dividend and investment timeline charts --- .../analysis/analysis-page.component.ts | 101 +++++++++++------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 424f1bf5b..d3e3d8a31 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -51,10 +51,11 @@ import { IonIcon } from '@ionic/angular/standalone'; import { SymbolProfile } from '@prisma/client'; import { addIcons } from 'ionicons'; import { copyOutline, ellipsisVertical } from 'ionicons/icons'; -import { isNumber, sortBy } from 'lodash'; +import { isNumber, keyBy, sortBy, union } from 'lodash'; import ms from 'ms'; import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import { forkJoin } from 'rxjs'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -254,53 +255,75 @@ export class GfAnalysisPageComponent implements OnInit { this.isLoadingDividendTimelineChart = true; this.isLoadingInvestmentTimelineChart = true; - this.dataService - .fetchDividends({ + forkJoin({ + dividends: this.dataService.fetchDividends({ filters: this.userService.getFilters(), groupBy: this.mode(), range: this.user?.settings?.dateRange ?? DEFAULT_DATE_RANGE - }) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(({ dividends }) => { - this.dividendsByGroup = dividends; - - this.isLoadingDividendTimelineChart = false; - - this.changeDetectorRef.markForCheck(); - }); - - this.dataService - .fetchInvestments({ + }), + investments: this.dataService.fetchInvestments({ filters: this.userService.getFilters(), groupBy: this.mode(), range: this.user?.settings?.dateRange ?? DEFAULT_DATE_RANGE }) + }) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(({ investments, savingsRate, streaks }) => { - this.investmentsByGroup = investments; - this.savingsRatePerMonth = savingsRate; - this.streaks = streaks; - this.unitCurrentStreak = - this.mode() === 'year' - ? this.streaks?.currentStreak === 1 - ? translate('YEAR') - : translate('YEARS') - : this.streaks?.currentStreak === 1 - ? translate('MONTH') - : translate('MONTHS'); - this.unitLongestStreak = - this.mode() === 'year' - ? this.streaks?.longestStreak === 1 - ? translate('YEAR') - : translate('YEARS') - : this.streaks?.longestStreak === 1 - ? translate('MONTH') - : translate('MONTHS'); - - this.isLoadingInvestmentTimelineChart = false; + .subscribe( + ({ + dividends: { dividends }, + investments: { investments, savingsRate, streaks } + }) => { + // Expand both timelines to the union of their groups so that the + // charts share the same axis, independent of whether a dividend or + // an investment has been tracked in a given group + const dividendByDate = keyBy(dividends, 'date'); + const investmentByDate = keyBy(investments, 'date'); + + const dates = sortBy( + union(Object.keys(dividendByDate), Object.keys(investmentByDate)) + ); - this.changeDetectorRef.markForCheck(); - }); + this.dividendsByGroup = dates.map((date) => { + return { + date, + investment: dividendByDate[date]?.investment ?? 0 + }; + }); + + this.investmentsByGroup = dates.map((date) => { + return { + date, + investment: investmentByDate[date]?.investment ?? 0 + }; + }); + + this.savingsRatePerMonth = savingsRate; + this.streaks = streaks; + + this.unitCurrentStreak = + this.mode() === 'year' + ? this.streaks?.currentStreak === 1 + ? translate('YEAR') + : translate('YEARS') + : this.streaks?.currentStreak === 1 + ? translate('MONTH') + : translate('MONTHS'); + + this.unitLongestStreak = + this.mode() === 'year' + ? this.streaks?.longestStreak === 1 + ? translate('YEAR') + : translate('YEARS') + : this.streaks?.longestStreak === 1 + ? translate('MONTH') + : translate('MONTHS'); + + this.isLoadingDividendTimelineChart = false; + this.isLoadingInvestmentTimelineChart = false; + + this.changeDetectorRef.markForCheck(); + } + ); } private update() {