Feature/improve analysis page (#609)
* Improve analysis page (show y-axis, extend chart in relation to days in market) * Update changelog
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { primaryColorRgb } from '@ghostfolio/common/config';
|
||||
import { parseDate } from '@ghostfolio/common/helper';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import {
|
||||
Chart,
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
PointElement,
|
||||
TimeScale
|
||||
} from 'chart.js';
|
||||
import { addMonths, isAfter, parseISO, subMonths } from 'date-fns';
|
||||
import { addDays, isAfter, parseISO, subDays } from 'date-fns';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-investment-chart',
|
||||
@@ -27,8 +28,10 @@ import { addMonths, isAfter, parseISO, subMonths } from 'date-fns';
|
||||
templateUrl: './investment-chart.component.html',
|
||||
styleUrls: ['./investment-chart.component.scss']
|
||||
})
|
||||
export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
||||
@Input() daysInMarket: number;
|
||||
@Input() investments: InvestmentItem[];
|
||||
@Input() isInPercent = false;
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas;
|
||||
|
||||
@@ -45,8 +48,6 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
public ngOnInit() {}
|
||||
|
||||
public ngOnChanges() {
|
||||
if (this.investments) {
|
||||
this.initialize();
|
||||
@@ -61,19 +62,25 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
this.isLoading = true;
|
||||
|
||||
if (this.investments?.length > 0) {
|
||||
// Extend chart by three months (before)
|
||||
// Extend chart by 5% of days in market (before)
|
||||
const firstItem = this.investments[0];
|
||||
this.investments.unshift({
|
||||
...firstItem,
|
||||
date: subMonths(parseISO(firstItem.date), 3).toISOString(),
|
||||
date: subDays(
|
||||
parseISO(firstItem.date),
|
||||
this.daysInMarket * 0.05 || 90
|
||||
).toISOString(),
|
||||
investment: 0
|
||||
});
|
||||
|
||||
// Extend chart by three months (after)
|
||||
// Extend chart by 5% of days in market (after)
|
||||
const lastItem = this.investments[this.investments.length - 1];
|
||||
this.investments.push({
|
||||
...lastItem,
|
||||
date: addMonths(new Date(), 3).toISOString()
|
||||
date: addDays(
|
||||
parseDate(lastItem.date),
|
||||
this.daysInMarket * 0.05 || 90
|
||||
).toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -136,12 +143,26 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
}
|
||||
},
|
||||
y: {
|
||||
display: false,
|
||||
display: !this.isInPercent,
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
display: false
|
||||
display: true,
|
||||
callback: (tickValue, index, ticks) => {
|
||||
if (index === 0 || index === ticks.length - 1) {
|
||||
// Only print last and first legend entry
|
||||
if (typeof tickValue === 'number') {
|
||||
return tickValue.toFixed(2);
|
||||
}
|
||||
|
||||
return tickValue;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
mirror: true,
|
||||
z: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import { InvestmentChartComponent } from './investment-chart.component';
|
||||
@NgModule({
|
||||
declarations: [InvestmentChartComponent],
|
||||
exports: [InvestmentChartComponent],
|
||||
imports: [CommonModule, NgxSkeletonLoaderModule],
|
||||
providers: []
|
||||
imports: [CommonModule, NgxSkeletonLoaderModule]
|
||||
})
|
||||
export class GfInvestmentChartModule {}
|
||||
|
@@ -2,9 +2,9 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { PortfolioPosition, User } from '@ghostfolio/common/interfaces';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import { ToggleOption } from '@ghostfolio/common/types';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
@@ -16,28 +16,10 @@ import { takeUntil } from 'rxjs/operators';
|
||||
templateUrl: './analysis-page.html'
|
||||
})
|
||||
export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
public accounts: {
|
||||
[symbol: string]: Pick<PortfolioPosition, 'name'> & { value: number };
|
||||
};
|
||||
public continents: {
|
||||
[code: string]: { name: string; value: number };
|
||||
};
|
||||
public countries: {
|
||||
[code: string]: { name: string; value: number };
|
||||
};
|
||||
public daysInMarket: number;
|
||||
public deviceType: string;
|
||||
public hasImpersonationId: boolean;
|
||||
public period = 'current';
|
||||
public periodOptions: ToggleOption[] = [
|
||||
{ label: 'Initial', value: 'original' },
|
||||
{ label: 'Current', value: 'current' }
|
||||
];
|
||||
public investments: InvestmentItem[];
|
||||
public portfolioPositions: { [symbol: string]: PortfolioPosition };
|
||||
public positions: { [symbol: string]: any };
|
||||
public sectors: {
|
||||
[name: string]: { name: string; value: number };
|
||||
};
|
||||
public user: User;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
@@ -69,8 +51,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
this.dataService
|
||||
.fetchInvestments()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((response) => {
|
||||
this.investments = response;
|
||||
.subscribe(({ firstOrderDate, investments }) => {
|
||||
this.daysInMarket = differenceInDays(new Date(), firstOrderDate);
|
||||
this.investments = investments;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
@@ -11,6 +11,8 @@
|
||||
<mat-card-content>
|
||||
<gf-investment-chart
|
||||
class="h-100"
|
||||
[daysInMarket]="daysInMarket"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[investments]="investments"
|
||||
></gf-investment-chart>
|
||||
</mat-card-content>
|
||||
|
@@ -5,7 +5,6 @@ import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto
|
||||
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
||||
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
||||
import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface';
|
||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
|
||||
@@ -23,13 +22,13 @@ import {
|
||||
InfoItem,
|
||||
PortfolioChart,
|
||||
PortfolioDetails,
|
||||
PortfolioInvestments,
|
||||
PortfolioPerformance,
|
||||
PortfolioPublicDetails,
|
||||
PortfolioReport,
|
||||
PortfolioSummary,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
import { permissions } from '@ghostfolio/common/permissions';
|
||||
import { DateRange } from '@ghostfolio/common/types';
|
||||
import { DataSource, Order as OrderModel } from '@prisma/client';
|
||||
@@ -124,6 +123,18 @@ export class DataService {
|
||||
return info;
|
||||
}
|
||||
|
||||
public fetchInvestments(): Observable<PortfolioInvestments> {
|
||||
return this.http.get<any>('/api/portfolio/investments').pipe(
|
||||
map((response) => {
|
||||
if (response.firstOrderDate) {
|
||||
response.firstOrderDate = parseISO(response.firstOrderDate);
|
||||
}
|
||||
|
||||
return response;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public fetchSymbolItem({
|
||||
dataSource,
|
||||
includeHistoricalData = false,
|
||||
@@ -170,10 +181,6 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
public fetchInvestments() {
|
||||
return this.http.get<InvestmentItem[]>('/api/portfolio/investments');
|
||||
}
|
||||
|
||||
public fetchPortfolioDetails(aParams: { [param: string]: any }) {
|
||||
return this.http.get<PortfolioDetails>('/api/portfolio/details', {
|
||||
params: aParams
|
||||
|
Reference in New Issue
Block a user