Extend investment timeline by month (#1066)
* Extend investment timeline grouped by month * Update changelog
This commit is contained in:
@@ -22,7 +22,10 @@ import {
|
||||
transformTickToAbbreviation
|
||||
} from '@ghostfolio/common/helper';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import { GroupBy } from '@ghostfolio/common/types';
|
||||
import {
|
||||
BarController,
|
||||
BarElement,
|
||||
Chart,
|
||||
LineController,
|
||||
LineElement,
|
||||
@@ -42,6 +45,7 @@ import { addDays, isAfter, parseISO, subDays } from 'date-fns';
|
||||
export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
@Input() currency: string;
|
||||
@Input() daysInMarket: number;
|
||||
@Input() groupBy: GroupBy;
|
||||
@Input() investments: InvestmentItem[];
|
||||
@Input() isInPercent = false;
|
||||
@Input() locale: string;
|
||||
@@ -53,6 +57,8 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
|
||||
public constructor() {
|
||||
Chart.register(
|
||||
BarController,
|
||||
BarElement,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
@@ -78,7 +84,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
private initialize() {
|
||||
this.isLoading = true;
|
||||
|
||||
if (this.investments?.length > 0) {
|
||||
if (!this.groupBy && this.investments?.length > 0) {
|
||||
// Extend chart by 5% of days in market (before)
|
||||
const firstItem = this.investments[0];
|
||||
this.investments.unshift({
|
||||
@@ -102,13 +108,14 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
const data = {
|
||||
labels: this.investments.map((position) => {
|
||||
return position.date;
|
||||
labels: this.investments.map((investmentItem) => {
|
||||
return investmentItem.date;
|
||||
}),
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
|
||||
borderColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
|
||||
borderWidth: 2,
|
||||
borderWidth: this.groupBy ? 0 : 2,
|
||||
data: this.investments.map((position) => {
|
||||
return position.investment;
|
||||
}),
|
||||
@@ -137,6 +144,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
||||
data,
|
||||
options: {
|
||||
animation: false,
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0
|
||||
@@ -192,12 +200,12 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
},
|
||||
plugins: [getVerticalHoverLinePlugin(this.chartCanvas)],
|
||||
type: 'line'
|
||||
type: this.groupBy ? 'bar' : 'line'
|
||||
});
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
private getTooltipPluginConfiguration() {
|
||||
|
@@ -4,6 +4,7 @@ import { ImpersonationStorageService } from '@ghostfolio/client/services/imperso
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { Position, User } from '@ghostfolio/common/interfaces';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import { GroupBy, ToggleOption } from '@ghostfolio/common/types';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
import { sortBy } from 'lodash';
|
||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||
@@ -22,6 +23,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
public deviceType: string;
|
||||
public hasImpersonationId: boolean;
|
||||
public investments: InvestmentItem[];
|
||||
public investmentsByMonth: InvestmentItem[];
|
||||
public mode: GroupBy;
|
||||
public modeOptions: ToggleOption[] = [
|
||||
{ label: 'Monthly', value: 'month' },
|
||||
{ label: 'Accumulating', value: undefined }
|
||||
];
|
||||
public top3: Position[];
|
||||
public user: User;
|
||||
|
||||
@@ -55,6 +62,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
this.dataService
|
||||
.fetchInvestmentsByMonth()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(({ investments }) => {
|
||||
this.investmentsByMonth = investments;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
this.dataService
|
||||
.fetchPositions({ range: 'max' })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@@ -86,6 +102,10 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public onChangeGroupBy(aMode: GroupBy) {
|
||||
this.mode = aMode;
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.unsubscribeSubject.next();
|
||||
this.unsubscribeSubject.complete();
|
||||
|
@@ -2,8 +2,19 @@
|
||||
<div class="investment-chart row">
|
||||
<div class="col-lg">
|
||||
<h3 class="d-flex justify-content-center mb-3" i18n>Analysis</h3>
|
||||
<div class="mb-3">
|
||||
<div class="h5 mb-3" i18n>Investment Timeline</div>
|
||||
<div class="mb-4">
|
||||
<div class="align-items-center d-flex mb-4">
|
||||
<div class="flex-grow-1 h5 mb-0 text-truncate" i18n>
|
||||
Investment Timeline
|
||||
</div>
|
||||
<gf-toggle
|
||||
class="d-none d-lg-block"
|
||||
[defaultValue]="mode"
|
||||
[isLoading]="false"
|
||||
[options]="modeOptions"
|
||||
(change)="onChangeGroupBy($event.value)"
|
||||
></gf-toggle>
|
||||
</div>
|
||||
<gf-investment-chart
|
||||
class="h-100"
|
||||
[currency]="user?.settings?.baseCurrency"
|
||||
@@ -11,6 +22,17 @@
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[investments]="investments"
|
||||
[locale]="user?.settings?.locale"
|
||||
[ngClass]="{ 'd-none': mode }"
|
||||
></gf-investment-chart>
|
||||
<gf-investment-chart
|
||||
class="h-100"
|
||||
groupBy="month"
|
||||
[currency]="user?.settings?.baseCurrency"
|
||||
[daysInMarket]="daysInMarket"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[investments]="investmentsByMonth"
|
||||
[locale]="user?.settings?.locale"
|
||||
[ngClass]="{ 'd-none': !mode }"
|
||||
></gf-investment-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module';
|
||||
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
@@ -14,6 +15,7 @@ import { AnalysisPageComponent } from './analysis-page.component';
|
||||
AnalysisPageRoutingModule,
|
||||
CommonModule,
|
||||
GfInvestmentChartModule,
|
||||
GfToggleModule,
|
||||
GfValueModule,
|
||||
MatCardModule,
|
||||
NgxSkeletonLoaderModule
|
||||
|
@@ -204,6 +204,22 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
public fetchInvestmentsByMonth(): Observable<PortfolioInvestments> {
|
||||
return this.http
|
||||
.get<any>('/api/v1/portfolio/investments', {
|
||||
params: { groupBy: 'month' }
|
||||
})
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response.firstOrderDate) {
|
||||
response.firstOrderDate = parseISO(response.firstOrderDate);
|
||||
}
|
||||
|
||||
return response;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public fetchSymbolItem({
|
||||
dataSource,
|
||||
includeHistoricalData,
|
||||
|
Reference in New Issue
Block a user