Feature/improve numerical precision in holding detail dialog (#3584)
* Improve numerical precision in holding detail dialog * Update changelog
This commit is contained in:
parent
48eee5f865
commit
ba78c2783d
@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved the numerical precision in the holding detail dialog
|
||||
- Improved the handling of the numerical precision in the value component
|
||||
- Optimized the 7d data gathering by prioritizing the currencies
|
||||
- Improved the language localization for German (`de`)
|
||||
- Upgraded `Node.js` from version `18` to `20` (`Dockerfile`)
|
||||
@ -4829,7 +4831,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Added the attribute `precision` in the value component
|
||||
- Added the attribute `precision` to the value component
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-foote
|
||||
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config';
|
||||
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
DataProviderInfo,
|
||||
@ -84,18 +85,22 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
||||
public dataProviderInfo: DataProviderInfo;
|
||||
public dataSource: MatTableDataSource<Activity>;
|
||||
public dividendInBaseCurrency: number;
|
||||
public dividendInBaseCurrencyPrecision = 2;
|
||||
public dividendYieldPercentWithCurrencyEffect: number;
|
||||
public feeInBaseCurrency: number;
|
||||
public firstBuyDate: string;
|
||||
public historicalDataItems: LineChartItem[];
|
||||
public investment: number;
|
||||
public investmentPrecision = 2;
|
||||
public marketPrice: number;
|
||||
public maxPrice: number;
|
||||
public minPrice: number;
|
||||
public netPerformance: number;
|
||||
public netPerformancePrecision = 2;
|
||||
public netPerformancePercent: number;
|
||||
public netPerformancePercentWithCurrencyEffect: number;
|
||||
public netPerformanceWithCurrencyEffect: number;
|
||||
public netPerformanceWithCurrencyEffectPrecision = 2;
|
||||
public quantity: number;
|
||||
public quantityPrecision = 2;
|
||||
public reportDataGlitchMail: string;
|
||||
@ -161,10 +166,20 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
||||
this.dataProviderInfo = dataProviderInfo;
|
||||
this.dataSource = new MatTableDataSource(orders.reverse());
|
||||
this.dividendInBaseCurrency = dividendInBaseCurrency;
|
||||
|
||||
if (
|
||||
this.data.deviceType === 'mobile' &&
|
||||
this.dividendInBaseCurrency >= NUMERICAL_PRECISION_THRESHOLD
|
||||
) {
|
||||
this.dividendInBaseCurrencyPrecision = 0;
|
||||
}
|
||||
|
||||
this.dividendYieldPercentWithCurrencyEffect =
|
||||
dividendYieldPercentWithCurrencyEffect;
|
||||
|
||||
this.feeInBaseCurrency = feeInBaseCurrency;
|
||||
this.firstBuyDate = firstBuyDate;
|
||||
|
||||
this.historicalDataItems = historicalData.map(
|
||||
({ averagePrice, date, marketPrice }) => {
|
||||
this.benchmarkDataItems.push({
|
||||
@ -178,17 +193,58 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
this.investment = investment;
|
||||
|
||||
if (
|
||||
this.data.deviceType === 'mobile' &&
|
||||
this.investment >= NUMERICAL_PRECISION_THRESHOLD
|
||||
) {
|
||||
this.investmentPrecision = 0;
|
||||
}
|
||||
|
||||
this.marketPrice = marketPrice;
|
||||
this.maxPrice = maxPrice;
|
||||
this.minPrice = minPrice;
|
||||
this.netPerformance = netPerformance;
|
||||
|
||||
if (
|
||||
this.data.deviceType === 'mobile' &&
|
||||
this.netPerformance >= NUMERICAL_PRECISION_THRESHOLD
|
||||
) {
|
||||
this.netPerformancePrecision = 0;
|
||||
}
|
||||
|
||||
this.netPerformancePercent = netPerformancePercent;
|
||||
|
||||
this.netPerformancePercentWithCurrencyEffect =
|
||||
netPerformancePercentWithCurrencyEffect;
|
||||
|
||||
this.netPerformanceWithCurrencyEffect =
|
||||
netPerformanceWithCurrencyEffect;
|
||||
|
||||
if (
|
||||
this.data.deviceType === 'mobile' &&
|
||||
this.netPerformanceWithCurrencyEffect >=
|
||||
NUMERICAL_PRECISION_THRESHOLD
|
||||
) {
|
||||
this.netPerformanceWithCurrencyEffectPrecision = 0;
|
||||
}
|
||||
|
||||
this.quantity = quantity;
|
||||
|
||||
if (Number.isInteger(this.quantity)) {
|
||||
this.quantityPrecision = 0;
|
||||
} else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') {
|
||||
if (this.quantity < 1) {
|
||||
this.quantityPrecision = 7;
|
||||
} else if (this.quantity < 1000) {
|
||||
this.quantityPrecision = 5;
|
||||
} else if (this.quantity >= 10000000) {
|
||||
this.quantityPrecision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
|
||||
this.sectors = {};
|
||||
this.SymbolProfile = SymbolProfile;
|
||||
@ -282,18 +338,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
);
|
||||
|
||||
if (Number.isInteger(this.quantity)) {
|
||||
this.quantityPrecision = 0;
|
||||
} else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') {
|
||||
if (this.quantity < 1) {
|
||||
this.quantityPrecision = 7;
|
||||
} else if (this.quantity < 1000) {
|
||||
this.quantityPrecision = 5;
|
||||
} else if (this.quantity > 10000000) {
|
||||
this.quantityPrecision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
);
|
||||
|
@ -47,6 +47,7 @@
|
||||
[colorizeSign]="true"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[precision]="netPerformanceWithCurrencyEffectPrecision"
|
||||
[unit]="data.baseCurrency"
|
||||
[value]="netPerformanceWithCurrencyEffect"
|
||||
>Change with currency effect</gf-value
|
||||
@ -58,6 +59,7 @@
|
||||
[colorizeSign]="true"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[precision]="netPerformancePrecision"
|
||||
[unit]="data.baseCurrency"
|
||||
[value]="netPerformance"
|
||||
>Change</gf-value
|
||||
@ -160,6 +162,7 @@
|
||||
size="medium"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[precision]="investmentPrecision"
|
||||
[unit]="data.baseCurrency"
|
||||
[value]="investment"
|
||||
>Investment</gf-value
|
||||
@ -172,6 +175,7 @@
|
||||
size="medium"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[precision]="dividendInBaseCurrencyPrecision"
|
||||
[unit]="data.baseCurrency"
|
||||
[value]="dividendInBaseCurrency"
|
||||
>Dividend</gf-value
|
||||
|
@ -3,6 +3,7 @@ import { LayoutService } from '@ghostfolio/client/core/layout.service';
|
||||
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 { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config';
|
||||
import {
|
||||
LineChartItem,
|
||||
PortfolioPerformance,
|
||||
@ -34,6 +35,7 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||
public isAllTimeLow: boolean;
|
||||
public isLoadingPerformance = true;
|
||||
public performance: PortfolioPerformance;
|
||||
public precision = 2;
|
||||
public showDetails = false;
|
||||
public unit: string;
|
||||
public user: User;
|
||||
@ -67,6 +69,12 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||
public ngOnInit() {
|
||||
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
|
||||
|
||||
this.showDetails =
|
||||
!this.user.settings.isRestrictedView &&
|
||||
this.user.settings.viewMode !== 'ZEN';
|
||||
|
||||
this.unit = this.showDetails ? this.user.settings.baseCurrency : '%';
|
||||
|
||||
this.impersonationStorageService
|
||||
.onChangeHasImpersonation()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -81,12 +89,6 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||
.subscribe(() => {
|
||||
this.update();
|
||||
});
|
||||
|
||||
this.showDetails =
|
||||
!this.user.settings.isRestrictedView &&
|
||||
this.user.settings.viewMode !== 'ZEN';
|
||||
|
||||
this.unit = this.showDetails ? this.user.settings.baseCurrency : '%';
|
||||
}
|
||||
|
||||
public onChangeDateRange(dateRange: DateRange) {
|
||||
@ -134,6 +136,14 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
this.deviceType === 'mobile' &&
|
||||
this.performance.currentValueInBaseCurrency >=
|
||||
NUMERICAL_PRECISION_THRESHOLD
|
||||
) {
|
||||
this.precision = 0;
|
||||
}
|
||||
|
||||
this.isLoadingPerformance = false;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
|
@ -88,6 +88,7 @@
|
||||
[isLoading]="isLoadingPerformance"
|
||||
[locale]="user?.settings?.locale"
|
||||
[performance]="performance"
|
||||
[precision]="precision"
|
||||
[showDetails]="showDetails"
|
||||
[unit]="unit"
|
||||
/>
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
ElementRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { CountUp } from 'countup.js';
|
||||
@ -26,7 +25,7 @@ import { isNumber } from 'lodash';
|
||||
templateUrl: './portfolio-performance.component.html',
|
||||
styleUrls: ['./portfolio-performance.component.scss']
|
||||
})
|
||||
export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
||||
export class PortfolioPerformanceComponent implements OnChanges {
|
||||
@Input() deviceType: string;
|
||||
@Input() errors: ResponseError['errors'];
|
||||
@Input() isAllTimeHigh: boolean;
|
||||
@ -34,6 +33,7 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
||||
@Input() isLoading: boolean;
|
||||
@Input() locale = getLocale();
|
||||
@Input() performance: PortfolioPerformance;
|
||||
@Input() precision: number;
|
||||
@Input() showDetails: boolean;
|
||||
@Input() unit: string;
|
||||
|
||||
@ -41,9 +41,9 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
||||
|
||||
public constructor() {}
|
||||
|
||||
public ngOnInit() {}
|
||||
|
||||
public ngOnChanges() {
|
||||
this.precision = this.precision >= 0 ? this.precision : 2;
|
||||
|
||||
if (this.isLoading) {
|
||||
if (this.value?.nativeElement) {
|
||||
this.value.nativeElement.innerHTML = '';
|
||||
@ -52,11 +52,7 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
||||
if (isNumber(this.performance?.currentValueInBaseCurrency)) {
|
||||
new CountUp('value', this.performance?.currentValueInBaseCurrency, {
|
||||
decimal: getNumberFormatDecimal(this.locale),
|
||||
decimalPlaces:
|
||||
this.deviceType === 'mobile' &&
|
||||
this.performance?.currentValueInBaseCurrency >= 100000
|
||||
? 0
|
||||
: 2,
|
||||
decimalPlaces: this.precision,
|
||||
duration: 1,
|
||||
separator: getNumberFormatGroup(this.locale)
|
||||
}).start();
|
||||
|
@ -91,6 +91,8 @@ export const HEADER_KEY_TOKEN = 'Authorization';
|
||||
export const MAX_CHART_ITEMS = 365;
|
||||
export const MAX_TOP_HOLDINGS = 50;
|
||||
|
||||
export const NUMERICAL_PRECISION_THRESHOLD = 100000;
|
||||
|
||||
export const PROPERTY_BENCHMARKS = 'BENCHMARKS';
|
||||
export const PROPERTY_BETTER_UPTIME_MONITOR_ID = 'BETTER_UPTIME_MONITOR_ID';
|
||||
export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS';
|
||||
|
@ -29,7 +29,7 @@ export class GfValueComponent implements OnChanges {
|
||||
@Input() isPercent = false;
|
||||
@Input() locale: string;
|
||||
@Input() position = '';
|
||||
@Input() precision: number | undefined;
|
||||
@Input() precision: number;
|
||||
@Input() size: 'large' | 'medium' | 'small' = 'small';
|
||||
@Input() subLabel = '';
|
||||
@Input() unit = '';
|
||||
@ -58,8 +58,8 @@ export class GfValueComponent implements OnChanges {
|
||||
this.formattedValue = this.absoluteValue.toLocaleString(
|
||||
this.locale,
|
||||
{
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
maximumFractionDigits: this.precision,
|
||||
minimumFractionDigits: this.precision
|
||||
}
|
||||
);
|
||||
} catch {}
|
||||
@ -68,8 +68,8 @@ export class GfValueComponent implements OnChanges {
|
||||
this.formattedValue = (this.absoluteValue * 100).toLocaleString(
|
||||
this.locale,
|
||||
{
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
maximumFractionDigits: this.precision,
|
||||
minimumFractionDigits: this.precision
|
||||
}
|
||||
);
|
||||
} catch {}
|
||||
@ -77,8 +77,8 @@ export class GfValueComponent implements OnChanges {
|
||||
} else if (this.isCurrency) {
|
||||
try {
|
||||
this.formattedValue = this.value?.toLocaleString(this.locale, {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
maximumFractionDigits: this.precision,
|
||||
minimumFractionDigits: this.precision
|
||||
});
|
||||
} catch {}
|
||||
} else if (this.isPercent) {
|
||||
@ -86,18 +86,11 @@ export class GfValueComponent implements OnChanges {
|
||||
this.formattedValue = (this.value * 100).toLocaleString(
|
||||
this.locale,
|
||||
{
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
maximumFractionDigits: this.precision,
|
||||
minimumFractionDigits: this.precision
|
||||
}
|
||||
);
|
||||
} catch {}
|
||||
} else if (this.precision || this.precision === 0) {
|
||||
try {
|
||||
this.formattedValue = this.value?.toLocaleString(this.locale, {
|
||||
maximumFractionDigits: this.precision,
|
||||
minimumFractionDigits: this.precision
|
||||
});
|
||||
} catch {}
|
||||
} else {
|
||||
this.formattedValue = this.value?.toLocaleString(this.locale);
|
||||
}
|
||||
@ -136,6 +129,7 @@ export class GfValueComponent implements OnChanges {
|
||||
this.isNumber = false;
|
||||
this.isString = false;
|
||||
this.locale = this.locale || getLocale();
|
||||
this.precision = this.precision >= 0 ? this.precision : 2;
|
||||
this.useAbsoluteValue = false;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user