Browse Source

Feature/improve precision of values in holding detail dialog on mobile (#5337)

* Improve dynamic numerical precision

* Shorten date on mobile

* Extend Storybook story of value component

* Update changelog
pull/5192/merge
Thomas Kaul 2 days ago
committed by GitHub
parent
commit
fcaa4f7996
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 59
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  3. 6
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  4. 4
      apps/client/src/app/components/home-overview/home-overview.component.ts
  5. 4
      libs/common/src/lib/config.ts
  6. 7
      libs/ui/src/lib/activities-table/activities-table.component.html
  7. 5
      libs/ui/src/lib/activities-table/activities-table.component.ts
  8. 18
      libs/ui/src/lib/value/value.component.stories.ts
  9. 3
      libs/ui/src/lib/value/value.component.ts

2
CHANGELOG.md

@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Extended the import functionality by tags - Extended the import functionality by tags
- Improved the dynamic numerical precision for various values in the holding detail dialog
- Shortened the date in the activities table on mobile
- Introduced the fuzzy search for the accounts endpoint - Introduced the fuzzy search for the accounts endpoint
- Refactored the fuzzy search for the holdings of the assistant - Refactored the fuzzy search for the holdings of the assistant
- Improved the language localization for Polish (`pl`) - Improved the language localization for Polish (`pl`)

59
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts

@ -3,7 +3,11 @@ import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-foote
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config'; import {
NUMERICAL_PRECISION_THRESHOLD_3_FIGURES,
NUMERICAL_PRECISION_THRESHOLD_5_FIGURES,
NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
} from '@ghostfolio/common/config';
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper'; import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
import { import {
DataProviderInfo, DataProviderInfo,
@ -101,6 +105,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public assetClass: string; public assetClass: string;
public assetSubClass: string; public assetSubClass: string;
public averagePrice: number; public averagePrice: number;
public averagePricePrecision = 2;
public benchmarkDataItems: LineChartItem[]; public benchmarkDataItems: LineChartItem[];
public benchmarkLabel = $localize`Average Unit Price`; public benchmarkLabel = $localize`Average Unit Price`;
public countries: { public countries: {
@ -122,11 +127,15 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public marketDataItems: MarketData[] = []; public marketDataItems: MarketData[] = [];
public marketPrice: number; public marketPrice: number;
public marketPriceMax: number; public marketPriceMax: number;
public marketPriceMaxPrecision = 2;
public marketPriceMin: number; public marketPriceMin: number;
public marketPriceMinPrecision = 2;
public marketPricePrecision = 2;
public netPerformance: number; public netPerformance: number;
public netPerformancePrecision = 2; public netPerformancePrecision = 2;
public netPerformancePercent: number; public netPerformancePercent: number;
public netPerformancePercentWithCurrencyEffect: number; public netPerformancePercentWithCurrencyEffect: number;
public netPerformancePercentWithCurrencyEffectPrecision = 2;
public netPerformanceWithCurrencyEffect: number; public netPerformanceWithCurrencyEffect: number;
public netPerformanceWithCurrencyEffectPrecision = 2; public netPerformanceWithCurrencyEffectPrecision = 2;
public quantity: number; public quantity: number;
@ -274,6 +283,14 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
value value
}) => { }) => {
this.averagePrice = averagePrice; this.averagePrice = averagePrice;
if (
this.data.deviceType === 'mobile' &&
this.averagePrice >= NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) {
this.averagePricePrecision = 0;
}
this.benchmarkDataItems = []; this.benchmarkDataItems = [];
this.countries = {}; this.countries = {};
this.dataProviderInfo = dataProviderInfo; this.dataProviderInfo = dataProviderInfo;
@ -281,7 +298,8 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
if ( if (
this.data.deviceType === 'mobile' && this.data.deviceType === 'mobile' &&
this.dividendInBaseCurrency >= NUMERICAL_PRECISION_THRESHOLD this.dividendInBaseCurrency >=
NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) { ) {
this.dividendInBaseCurrencyPrecision = 0; this.dividendInBaseCurrencyPrecision = 0;
} }
@ -320,19 +338,42 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
if ( if (
this.data.deviceType === 'mobile' && this.data.deviceType === 'mobile' &&
this.investmentInBaseCurrencyWithCurrencyEffect >= this.investmentInBaseCurrencyWithCurrencyEffect >=
NUMERICAL_PRECISION_THRESHOLD NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) { ) {
this.investmentInBaseCurrencyWithCurrencyEffectPrecision = 0; this.investmentInBaseCurrencyWithCurrencyEffectPrecision = 0;
} }
this.marketPrice = marketPrice; this.marketPrice = marketPrice;
this.marketPriceMax = marketPriceMax; this.marketPriceMax = marketPriceMax;
if (
this.data.deviceType === 'mobile' &&
this.marketPriceMax >= NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) {
this.marketPriceMaxPrecision = 0;
}
this.marketPriceMin = marketPriceMin; this.marketPriceMin = marketPriceMin;
if (
this.data.deviceType === 'mobile' &&
this.marketPriceMin >= NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) {
this.marketPriceMinPrecision = 0;
}
if (
this.data.deviceType === 'mobile' &&
this.marketPrice >= NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) {
this.marketPricePrecision = 0;
}
this.netPerformance = netPerformance; this.netPerformance = netPerformance;
if ( if (
this.data.deviceType === 'mobile' && this.data.deviceType === 'mobile' &&
this.netPerformance >= NUMERICAL_PRECISION_THRESHOLD this.netPerformance >= NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) { ) {
this.netPerformancePrecision = 0; this.netPerformancePrecision = 0;
} }
@ -342,13 +383,21 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
this.netPerformancePercentWithCurrencyEffect = this.netPerformancePercentWithCurrencyEffect =
netPerformancePercentWithCurrencyEffect; netPerformancePercentWithCurrencyEffect;
if (
this.data.deviceType === 'mobile' &&
this.netPerformancePercentWithCurrencyEffect >=
NUMERICAL_PRECISION_THRESHOLD_3_FIGURES
) {
this.netPerformancePercentWithCurrencyEffectPrecision = 0;
}
this.netPerformanceWithCurrencyEffect = this.netPerformanceWithCurrencyEffect =
netPerformanceWithCurrencyEffect; netPerformanceWithCurrencyEffect;
if ( if (
this.data.deviceType === 'mobile' && this.data.deviceType === 'mobile' &&
this.netPerformanceWithCurrencyEffect >= this.netPerformanceWithCurrencyEffect >=
NUMERICAL_PRECISION_THRESHOLD NUMERICAL_PRECISION_THRESHOLD_5_FIGURES
) { ) {
this.netPerformanceWithCurrencyEffectPrecision = 0; this.netPerformanceWithCurrencyEffectPrecision = 0;
} }

6
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html

@ -77,6 +77,7 @@
[colorizeSign]="true" [colorizeSign]="true"
[isPercent]="true" [isPercent]="true"
[locale]="data.locale" [locale]="data.locale"
[precision]="netPerformancePercentWithCurrencyEffectPrecision"
[value]="netPerformancePercentWithCurrencyEffect" [value]="netPerformancePercentWithCurrencyEffect"
> >
@if ( @if (
@ -95,6 +96,7 @@
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[locale]="data.locale" [locale]="data.locale"
[precision]="averagePricePrecision"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="averagePrice" [value]="averagePrice"
>Average Unit Price</gf-value >Average Unit Price</gf-value
@ -106,6 +108,7 @@
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[locale]="data.locale" [locale]="data.locale"
[precision]="marketPricePrecision"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="marketPrice" [value]="marketPrice"
>Market Price</gf-value >Market Price</gf-value
@ -122,6 +125,7 @@
marketPriceMin?.toFixed(2) === marketPrice?.toFixed(2) && marketPriceMin?.toFixed(2) === marketPrice?.toFixed(2) &&
marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2) marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2)
}" }"
[precision]="marketPriceMinPrecision"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="marketPriceMin" [value]="marketPriceMin"
>Minimum Price</gf-value >Minimum Price</gf-value
@ -138,6 +142,7 @@
marketPriceMax?.toFixed(2) === marketPrice?.toFixed(2) && marketPriceMax?.toFixed(2) === marketPrice?.toFixed(2) &&
marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2) marketPriceMax?.toFixed(2) !== marketPriceMin?.toFixed(2)
}" }"
[precision]="marketPriceMaxPrecision"
[unit]="SymbolProfile?.currency" [unit]="SymbolProfile?.currency"
[value]="marketPriceMax" [value]="marketPriceMax"
>Maximum Price</gf-value >Maximum Price</gf-value
@ -208,6 +213,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[deviceType]="data.deviceType"
[isDate]="true" [isDate]="true"
[locale]="data.locale" [locale]="data.locale"
[value]="firstBuyDate" [value]="firstBuyDate"

4
apps/client/src/app/components/home-overview/home-overview.component.ts

@ -3,7 +3,7 @@ import { LayoutService } from '@ghostfolio/client/core/layout.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config'; import { NUMERICAL_PRECISION_THRESHOLD_6_FIGURES } from '@ghostfolio/common/config';
import { import {
AssetProfileIdentifier, AssetProfileIdentifier,
LineChartItem, LineChartItem,
@ -143,7 +143,7 @@ export class GfHomeOverviewComponent implements OnDestroy, OnInit {
if ( if (
this.deviceType === 'mobile' && this.deviceType === 'mobile' &&
this.performance.currentValueInBaseCurrency >= this.performance.currentValueInBaseCurrency >=
NUMERICAL_PRECISION_THRESHOLD NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) { ) {
this.precision = 0; this.precision = 0;
} }

4
libs/common/src/lib/config.ts

@ -136,7 +136,9 @@ export const HEADER_KEY_SKIP_INTERCEPTOR = 'X-Skip-Interceptor';
export const MAX_TOP_HOLDINGS = 50; export const MAX_TOP_HOLDINGS = 50;
export const NUMERICAL_PRECISION_THRESHOLD = 100000; export const NUMERICAL_PRECISION_THRESHOLD_3_FIGURES = 100;
export const NUMERICAL_PRECISION_THRESHOLD_5_FIGURES = 10000;
export const NUMERICAL_PRECISION_THRESHOLD_6_FIGURES = 100000;
export const PROPERTY_API_KEY_GHOSTFOLIO = 'API_KEY_GHOSTFOLIO'; export const PROPERTY_API_KEY_GHOSTFOLIO = 'API_KEY_GHOSTFOLIO';
export const PROPERTY_API_KEY_OPENROUTER = 'API_KEY_OPENROUTER'; export const PROPERTY_API_KEY_OPENROUTER = 'API_KEY_OPENROUTER';

7
libs/ui/src/lib/activities-table/activities-table.component.html

@ -171,7 +171,12 @@
</th> </th>
<td *matCellDef="let element" class="px-1" mat-cell> <td *matCellDef="let element" class="px-1" mat-cell>
<div class="d-flex"> <div class="d-flex">
{{ element.date | date: defaultDateFormat }} <gf-value
[deviceType]="deviceType"
[isDate]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.date"
/>
</div> </div>
</td> </td>
</ng-container> </ng-container>

5
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -6,7 +6,7 @@ import {
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
TAG_ID_EXCLUDE_FROM_ANALYSIS TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { getDateFormatString, getLocale } from '@ghostfolio/common/helper'; import { getLocale } from '@ghostfolio/common/helper';
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
@ -128,7 +128,6 @@ export class GfActivitiesTableComponent
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public defaultDateFormat: string;
public displayedColumns = []; public displayedColumns = [];
public endOfToday = endOfToday(); public endOfToday = endOfToday();
public hasDrafts = false; public hasDrafts = false;
@ -175,8 +174,6 @@ export class GfActivitiesTableComponent
} }
public ngOnChanges() { public ngOnChanges() {
this.defaultDateFormat = getDateFormatString(this.locale);
this.displayedColumns = [ this.displayedColumns = [
'select', 'select',
'importStatus', 'importStatus',

18
libs/ui/src/lib/value/value.component.stories.ts

@ -11,7 +11,13 @@ export default {
moduleMetadata({ moduleMetadata({
imports: [NgxSkeletonLoaderModule] imports: [NgxSkeletonLoaderModule]
}) })
] ],
argTypes: {
deviceType: {
control: 'select',
options: ['desktop', 'mobile']
}
}
} as Meta<GfValueComponent>; } as Meta<GfValueComponent>;
type Story = StoryObj<GfValueComponent>; type Story = StoryObj<GfValueComponent>;
@ -31,6 +37,16 @@ export const Currency: Story = {
} }
}; };
export const DateValue: Story = {
args: {
deviceType: 'desktop',
isDate: true,
locale: 'en-US',
value: new Date().toISOString()
},
name: 'Date'
};
export const Label: Story = { export const Label: Story = {
args: { args: {
locale: 'en-US', locale: 'en-US',

3
libs/ui/src/lib/value/value.component.ts

@ -22,6 +22,7 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
}) })
export class GfValueComponent implements OnChanges { export class GfValueComponent implements OnChanges {
@Input() colorizeSign = false; @Input() colorizeSign = false;
@Input() deviceType: string;
@Input() icon = ''; @Input() icon = '';
@Input() isAbsolute = false; @Input() isAbsolute = false;
@Input() isCurrency = false; @Input() isCurrency = false;
@ -118,7 +119,7 @@ export class GfValueComponent implements OnChanges {
{ {
day: '2-digit', day: '2-digit',
month: '2-digit', month: '2-digit',
year: 'numeric' year: this.deviceType === 'mobile' ? '2-digit' : 'numeric'
} }
); );
} else { } else {

Loading…
Cancel
Save