Browse Source

Merge branch 'main' into task/improve-check-for-duplicates-in-preview-step-of-activities-import

pull/7541/head
Thomas Kaul 3 weeks ago
committed by GitHub
parent
commit
c554aa1110
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 20
      CHANGELOG.md
  2. 7
      apps/api/src/app/activities/activities.service.ts
  3. 3
      apps/api/src/app/import/import.service.ts
  4. 14
      apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts
  5. 3
      apps/api/src/services/data-provider/data-provider.service.ts
  6. 2
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  7. 3
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  8. 7
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
  9. 3
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  10. 10
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  11. 3
      apps/client/src/app/components/admin-overview/admin-overview.html
  12. 3
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  13. 14
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  14. 14
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
  15. 97
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  16. 3
      apps/client/src/app/components/user-account-settings/user-account-settings.component.ts
  17. 1
      apps/client/src/app/components/user-account-settings/user-account-settings.html
  18. 39
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
  19. 11
      apps/client/src/app/pages/portfolio/allocations/allocations-page.html
  20. 69
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  21. 3
      apps/client/src/app/pages/public/public-page.component.ts
  22. 12
      apps/client/src/app/pages/public/public-page.html
  23. 3
      apps/client/src/app/services/import-activities.service.ts
  24. 22
      apps/client/src/locales/messages.es.xlf
  25. 6
      libs/common/src/lib/config.ts
  26. 18
      libs/ui/src/lib/activities-table/activities-table.component.html
  27. 3
      libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts
  28. 1
      libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.html
  29. 21
      libs/ui/src/lib/holdings-table/holdings-table.component.html
  30. 7
      libs/ui/src/lib/shared/abstract-mat-form-field.ts
  31. 26
      libs/ui/src/lib/value/value.component.html
  32. 5
      libs/ui/src/lib/value/value.component.stories.ts
  33. 4
      libs/ui/src/lib/value/value.component.ts
  34. 4
      package-lock.json
  35. 2
      package.json
  36. 1
      test/import/ok/500-activities.json
  37. 1
      test/import/ok/derived-currency.json
  38. 1
      test/import/ok/sample.json

20
CHANGELOG.md

@ -7,21 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Changed
- Migrated the abstract _Material_ form field from a component to a directive
- Improved the check for duplicates in the preview step of the activities import (regardless of the account)
- Improved the check for duplicates in the preview step of the import dividends dialog (regardless of the account)
- Extended the activities import to reuse an existing account of the user by name and currency
### Fixed
- Fixed the check for duplicates in the preview step of the activities import for activities without a comment
## 3.43.0 - 2026-08-06
### Added ### Added
- Added the platform logo to the platform selector in the create or update account dialog - Added the platform logo to the platform selector in the create or update account dialog
- Added the platform logo to the account selector in the create or update activity dialog - Added the platform logo to the account selector in the create or update activity dialog
- Extended the value component by an `isLoading` attribute to distinguish the loading state from redacted values
### Changed ### Changed
- Guarded the system tags against deletion and renaming in the tag management of the admin control panel - Guarded the system tags against deletion and renaming in the tag management of the admin control panel
- Improved the check for duplicates in the preview step of the activities import (regardless of the account) - Improved the language localization for Spanish (`es`)
- Improved the check for duplicates in the preview step of the import dividends dialog (regardless of the account)
- Extended the activities import to reuse an existing account of the user by name and currency
### Fixed ### Fixed
- Fixed the check for duplicates in the preview step of the activities import for activities without a comment - Handled an exception in the country weightings parsing of the _Financial Modeling Prep_ service
## 3.42.0 - 2026-08-04 ## 3.42.0 - 2026-08-04

7
apps/api/src/app/activities/activities.service.ts

@ -20,6 +20,7 @@ import {
DATA_GATHERING_QUEUE_PRIORITY_HIGH, DATA_GATHERING_QUEUE_PRIORITY_HIGH,
GATHER_ASSET_PROFILE_PROCESS_JOB_NAME, GATHER_ASSET_PROFILE_PROCESS_JOB_NAME,
GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS, GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS,
NON_INVESTMENT_ACTIVITY_TYPES,
TAG_ID_EXCLUDE_FROM_ANALYSIS TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { import {
@ -191,7 +192,7 @@ export class ActivitiesService {
const userId = data.userId; const userId = data.userId;
if ( if (
['FEE', 'INTEREST', 'LIABILITY'].includes(data.type) || NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type) ||
(data.SymbolProfile.connectOrCreate.create.dataSource === 'MANUAL' && (data.SymbolProfile.connectOrCreate.create.dataSource === 'MANUAL' &&
data.type === 'BUY') data.type === 'BUY')
) { ) {
@ -260,7 +261,7 @@ export class ActivitiesService {
const orderData: Prisma.OrderCreateInput = data; const orderData: Prisma.OrderCreateInput = data;
const isDraft = ['FEE', 'INTEREST', 'LIABILITY'].includes(data.type) const isDraft = NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type)
? false ? false
: isAfter(data.date as Date, endOfToday()); : isAfter(data.date as Date, endOfToday());
@ -981,7 +982,7 @@ export class ActivitiesService {
let isDraft = false; let isDraft = false;
if ( if (
['FEE', 'INTEREST', 'LIABILITY'].includes(data.type) || NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type) ||
(data.SymbolProfile.connect.dataSource_symbol.dataSource === 'MANUAL' && (data.SymbolProfile.connect.dataSource_symbol.dataSource === 'MANUAL' &&
data.type === 'BUY') data.type === 'BUY')
) { ) {

3
apps/api/src/app/import/import.service.ts

@ -12,6 +12,7 @@ import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { import {
DATA_GATHERING_QUEUE_PRIORITY_HIGH, DATA_GATHERING_QUEUE_PRIORITY_HIGH,
ghostfolioPrefix, ghostfolioPrefix,
NON_INVESTMENT_ACTIVITY_TYPES,
TAG_ID_EXCLUDE_FROM_ANALYSIS TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { import {
@ -223,7 +224,7 @@ export class ImportService {
// createActivity() if needed. // createActivity() if needed.
for (const [index, activity] of activitiesDto.entries()) { for (const [index, activity] of activitiesDto.entries()) {
if (!activity.dataSource) { if (!activity.dataSource) {
if (['FEE', 'INTEREST', 'LIABILITY'].includes(activity.type)) { if (NON_INVESTMENT_ACTIVITY_TYPES.includes(activity.type)) {
activity.dataSource = DataSource.MANUAL; activity.dataSource = DataSource.MANUAL;
} else { } else {
activity.dataSource = activity.dataSource =

14
apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts

@ -50,12 +50,10 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
response: Partial<SymbolProfile>; response: Partial<SymbolProfile>;
symbol: string; symbol: string;
}): Promise<Partial<SymbolProfile>> { }): Promise<Partial<SymbolProfile>> {
if ( if (!(
!( response.assetClass === 'EQUITY' &&
response.assetClass === 'EQUITY' && ['ETF', 'MUTUALFUND'].includes(response.assetSubClass)
['ETF', 'MUTUALFUND'].includes(response.assetSubClass) )) {
)
) {
return response; return response;
} }
@ -214,8 +212,8 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
return undefined; return undefined;
}) })
.catch(({ message }) => { .catch(({ message }) => {
this.logger.error( this.logger.warn(
`Failed to search Trackinsight symbol for ${symbol} (${message})` `Could not search Trackinsight symbol for "${symbol}": ${message}`
); );
return undefined; return undefined;

3
apps/api/src/services/data-provider/data-provider.service.ts

@ -9,6 +9,7 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DERIVED_CURRENCIES, DERIVED_CURRENCIES,
NON_INVESTMENT_ACTIVITY_TYPES,
PROPERTY_API_KEY_GHOSTFOLIO, PROPERTY_API_KEY_GHOSTFOLIO,
PROPERTY_DATA_SOURCE_MAPPING PROPERTY_DATA_SOURCE_MAPPING
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
@ -268,7 +269,7 @@ export class DataProviderService implements OnModuleInit {
if (!assetProfiles[assetProfileIdentifier]) { if (!assetProfiles[assetProfileIdentifier]) {
if ( if (
(dataSource === DataSource.MANUAL && type === 'BUY') || (dataSource === DataSource.MANUAL && type === 'BUY') ||
['FEE', 'INTEREST', 'LIABILITY'].includes(type) NON_INVESTMENT_ACTIVITY_TYPES.includes(type)
) { ) {
const assetProfileInImport = assetProfilesWithMarketDataDto?.find( const assetProfileInImport = assetProfilesWithMarketDataDto?.find(
(assetProfile) => { (assetProfile) => {

2
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -178,7 +178,7 @@ export class FinancialModelingPrepService
aliases: FinancialModelingPrepService.countriesMapping, aliases: FinancialModelingPrepService.countriesMapping,
name: countryName name: countryName
}), }),
weight: parseFloat(weightPercentage.slice(0, -1)) / 100 weight: parseFloat(`${weightPercentage}`) / 100
}; };
}); });

3
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -102,6 +102,7 @@ export class GfAccountDetailDialogComponent implements OnInit {
protected holdings: PortfolioPosition[]; protected holdings: PortfolioPosition[];
protected interestInBaseCurrency: number; protected interestInBaseCurrency: number;
protected interestInBaseCurrencyPrecision = 2; protected interestInBaseCurrencyPrecision = 2;
protected isLoading = true;
protected isLoadingChart: boolean; protected isLoadingChart: boolean;
protected name: string | null; protected name: string | null;
protected pageIndex = 0; protected pageIndex = 0;
@ -302,6 +303,8 @@ export class GfAccountDetailDialogComponent implements OnInit {
this.valueInBaseCurrency = valueInBaseCurrency; this.valueInBaseCurrency = valueInBaseCurrency;
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
); );

7
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html

@ -12,6 +12,7 @@
<gf-value <gf-value
size="large" size="large"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]="valueInBaseCurrency" [value]="valueInBaseCurrency"
@ -48,6 +49,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="balancePrecision" [precision]="balancePrecision"
[unit]="currency" [unit]="currency"
@ -60,6 +62,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="equityPrecision" [precision]="equityPrecision"
[unit]="currency" [unit]="currency"
@ -72,6 +75,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="interestInBaseCurrencyPrecision" [precision]="interestInBaseCurrencyPrecision"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
@ -84,6 +88,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="dividendInBaseCurrencyPrecision" [precision]="dividendInBaseCurrencyPrecision"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
@ -94,6 +99,7 @@
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value <gf-value
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]="activitiesCount" [value]="activitiesCount"
> >
@ -108,6 +114,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]="platformName" [value]="platformName"
>Platform</gf-value >Platform</gf-value

3
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -283,6 +283,7 @@ export class GfAssetProfileDialogComponent implements OnInit {
protected isBenchmark = false; protected isBenchmark = false;
protected isDataGatheringEnabled: boolean; protected isDataGatheringEnabled: boolean;
protected isEditAssetProfileIdentifierMode = false; protected isEditAssetProfileIdentifierMode = false;
protected isLoading = true;
protected readonly isUUID = isUUID; protected readonly isUUID = isUUID;
protected marketDataItems: MarketData[] = []; protected marketDataItems: MarketData[] = [];
@ -496,6 +497,8 @@ export class GfAssetProfileDialogComponent implements OnInit {
this.assetProfileForm.markAsPristine(); this.assetProfileForm.markAsPristine();
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

10
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html

@ -183,6 +183,7 @@
i18n i18n
size="medium" size="medium"
[enableCopyToClipboardButton]="true" [enableCopyToClipboardButton]="true"
[isLoading]="isLoading"
[value]="assetProfile?.symbol" [value]="assetProfile?.symbol"
>Symbol</gf-value >Symbol</gf-value
> >
@ -191,6 +192,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[value]=" [value]="
assetProfile?.dataProviderInfo?.name ?? assetProfile?.dataProviderInfo?.name ??
assetProfile?.dataSource assetProfile?.dataSource
@ -213,7 +215,11 @@
</div> </div>
} }
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="assetProfile?.currency" <gf-value
i18n
size="medium"
[isLoading]="isLoading"
[value]="assetProfile?.currency"
>Currency</gf-value >Currency</gf-value
> >
</div> </div>
@ -231,6 +237,7 @@
i18n i18n
size="medium" size="medium"
[isDate]="assetProfile?.dateOfFirstActivity ? true : false" [isDate]="assetProfile?.dateOfFirstActivity ? true : false"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="assetProfile?.dateOfFirstActivity ?? '-'" [value]="assetProfile?.dateOfFirstActivity ?? '-'"
>First Activity</gf-value >First Activity</gf-value
@ -240,6 +247,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="assetProfile?.activitiesCount" [value]="assetProfile?.activitiesCount"
>Activities</gf-value >Activities</gf-value

3
apps/client/src/app/components/admin-overview/admin-overview.html

@ -7,6 +7,7 @@
i18n i18n
size="large" size="large"
[enableCopyToClipboardButton]="true" [enableCopyToClipboardButton]="true"
[isLoading]="isLoading"
[value]="version" [value]="version"
>Version</gf-value >Version</gf-value
> >
@ -19,6 +20,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]="userCount" [value]="userCount"
>Users</gf-value >Users</gf-value
@ -32,6 +34,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[subLabel]="activitiesCountPerUser" [subLabel]="activitiesCountPerUser"
[value]="activitiesCount" [value]="activitiesCount"

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

@ -157,6 +157,7 @@ export class GfHoldingDetailDialogComponent implements OnInit {
}>; }>;
protected investmentInBaseCurrencyWithCurrencyEffect: number; protected investmentInBaseCurrencyWithCurrencyEffect: number;
protected investmentInBaseCurrencyWithCurrencyEffectPrecision = 2; protected investmentInBaseCurrencyWithCurrencyEffectPrecision = 2;
protected isLoading = true;
protected readonly isUUID = isUUID; protected readonly isUUID = isUUID;
protected marketDataItems: MarketData[] = []; protected marketDataItems: MarketData[] = [];
protected marketPrice: number; protected marketPrice: number;
@ -571,6 +572,8 @@ export class GfHoldingDetailDialogComponent implements OnInit {
this.fetchMarketData(); this.fetchMarketData();
} }
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
); );

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

@ -12,6 +12,7 @@
<gf-value <gf-value
size="large" size="large"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[unit]="data.baseCurrency" [unit]="data.baseCurrency"
[value]="value" [value]="value"
@ -55,6 +56,7 @@
size="medium" size="medium"
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="netPerformanceWithCurrencyEffectPrecision" [precision]="netPerformanceWithCurrencyEffectPrecision"
[unit]="data.baseCurrency" [unit]="data.baseCurrency"
@ -75,6 +77,7 @@
i18n i18n
size="medium" size="medium"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="data.locale" [locale]="data.locale"
[precision]="netPerformancePercentWithCurrencyEffectPrecision" [precision]="netPerformancePercentWithCurrencyEffectPrecision"
@ -95,6 +98,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="averagePricePrecision" [precision]="averagePricePrecision"
[unit]="assetProfile?.currency" [unit]="assetProfile?.currency"
@ -107,6 +111,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="marketPricePrecision" [precision]="marketPricePrecision"
[unit]="assetProfile?.currency" [unit]="assetProfile?.currency"
@ -125,6 +130,7 @@
round(marketPriceMin, marketPriceMinPrecision) round(marketPriceMin, marketPriceMinPrecision)
" "
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="marketPriceMinPrecision" [precision]="marketPriceMinPrecision"
[unit]="assetProfile?.currency" [unit]="assetProfile?.currency"
@ -143,6 +149,7 @@
round(marketPriceMin, marketPriceMinPrecision) round(marketPriceMin, marketPriceMinPrecision)
" "
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="marketPriceMaxPrecision" [precision]="marketPriceMaxPrecision"
[unit]="assetProfile?.currency" [unit]="assetProfile?.currency"
@ -154,6 +161,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="quantityPrecision" [precision]="quantityPrecision"
[value]="quantity" [value]="quantity"
@ -165,6 +173,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]=" [precision]="
investmentInBaseCurrencyWithCurrencyEffectPrecision investmentInBaseCurrencyWithCurrencyEffectPrecision
@ -182,6 +191,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="dividendInBaseCurrencyPrecision" [precision]="dividendInBaseCurrencyPrecision"
[unit]="data.baseCurrency" [unit]="data.baseCurrency"
@ -193,6 +203,7 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="data.locale" [locale]="data.locale"
[value]="dividendYieldPercentWithCurrencyEffect" [value]="dividendYieldPercentWithCurrencyEffect"
@ -205,6 +216,7 @@
i18n i18n
size="medium" size="medium"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[unit]="data.baseCurrency" [unit]="data.baseCurrency"
[value]="feeInBaseCurrency" [value]="feeInBaseCurrency"
@ -217,6 +229,7 @@
size="medium" size="medium"
[deviceType]="data.deviceType" [deviceType]="data.deviceType"
[isDate]="true" [isDate]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="dateOfFirstActivity" [value]="dateOfFirstActivity"
>First Activity</gf-value >First Activity</gf-value
@ -225,6 +238,7 @@
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value <gf-value
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="activitiesCount" [value]="activitiesCount"
> >

14
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html

@ -38,24 +38,18 @@
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale()" [locale]="locale()"
[value]=" [value]="performance()?.netPerformanceWithCurrencyEffect"
isLoading()
? undefined
: performance().netPerformanceWithCurrencyEffect
"
/> />
</div> </div>
<div class="col"> <div class="col">
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading()"
[isPercent]="true" [isPercent]="true"
[locale]="locale()" [locale]="locale()"
[value]=" [value]="performance()?.netPerformancePercentageWithCurrencyEffect"
isLoading()
? undefined
: performance().netPerformancePercentageWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>

97
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -2,7 +2,11 @@
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 text-truncate" i18n>Time in Market</div> <div class="flex-grow-1 text-truncate" i18n>Time in Market</div>
<div class="justify-content-end"> <div class="justify-content-end">
<gf-value class="justify-content-end" [value]="timeInMarket" /> <gf-value
class="justify-content-end"
[isLoading]="isLoading"
[value]="timeInMarket"
/>
</div> </div>
</div> </div>
<div <div
@ -33,10 +37,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalBuy" [value]="summary?.totalBuy"
/> />
</div> </div>
</div> </div>
@ -46,10 +51,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalSell" [value]="summary?.totalSell"
/> />
</div> </div>
</div> </div>
@ -62,14 +68,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]=" [value]="summary?.totalInvestmentValueWithCurrencyEffect"
isLoading
? undefined
: summary?.totalInvestmentValueWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -79,12 +82,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]=" [value]="summary?.grossPerformanceWithCurrencyEffect"
isLoading ? undefined : summary?.grossPerformanceWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -97,10 +99,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.fees" [value]="summary?.fees"
/> />
</div> </div>
</div> </div>
@ -113,12 +116,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]=" [value]="summary?.netPerformanceWithCurrencyEffect"
isLoading ? undefined : summary?.netPerformanceWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -136,13 +138,10 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]=" [value]="summary?.netPerformancePercentageWithCurrencyEffect"
isLoading
? undefined
: summary?.netPerformancePercentageWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -156,10 +155,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalAssetsInBaseCurrency" [value]="summary?.totalAssetsInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -188,10 +188,11 @@
) { ) {
<gf-value <gf-value
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0" [precision]="0"
[value]="isLoading ? undefined : holdingsPercentage" [value]="holdingsPercentage"
/> />
} }
</div> </div>
@ -200,10 +201,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : holdingsInBaseCurrency" [value]="holdingsInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -215,10 +217,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : investmentsInBaseCurrency" [value]="investmentsInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -229,10 +232,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets" [value]="summary?.emergencyFund?.assets"
/> />
</div> </div>
</div> </div>
@ -262,10 +266,11 @@
) { ) {
<gf-value <gf-value
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0" [precision]="0"
[value]="isLoading ? undefined : cashPercentage" [value]="cashPercentage"
/> />
} }
</div> </div>
@ -274,10 +279,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalCashInBaseCurrency" [value]="summary?.totalCashInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -289,10 +295,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash" [value]="summary?.cash"
/> />
</div> </div>
</div> </div>
@ -303,10 +310,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash" [value]="summary?.emergencyFund?.cash"
/> />
</div> </div>
</div> </div>
@ -321,20 +329,22 @@
) { ) {
<gf-value <gf-value
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0" [precision]="0"
[value]="isLoading ? undefined : excludedFromAnalysisPercentage" [value]="excludedFromAnalysisPercentage"
/> />
} }
</div> </div>
<div class="align-items-center d-flex justify-content-end"> <div class="align-items-center d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.excludedAccountsAndActivities" [value]="summary?.excludedAccountsAndActivities"
/> />
</div> </div>
</div> </div>
@ -353,10 +363,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.liabilitiesInBaseCurrency" [value]="summary?.liabilitiesInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -369,10 +380,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalValueInBaseCurrency" [value]="summary?.totalValueInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -385,13 +397,10 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]=" [value]="summary?.annualizedPerformancePercentWithCurrencyEffect"
isLoading
? undefined
: summary?.annualizedPerformancePercentWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -408,10 +417,11 @@
) { ) {
<gf-value <gf-value
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0" [precision]="0"
[value]="isLoading ? undefined : emergencyFundPercentage" [value]="emergencyFundPercentage"
/> />
} }
</div> </div>
@ -443,10 +453,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.total" [value]="summary?.emergencyFund?.total"
/> />
</div> </div>
</div> </div>
@ -456,10 +467,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.interestInBaseCurrency" [value]="summary?.interestInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -469,10 +481,11 @@
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.dividendInBaseCurrency" [value]="summary?.dividendInBaseCurrency"
/> />
</div> </div>
</div> </div>

3
apps/client/src/app/components/user-account-settings/user-account-settings.component.ts

@ -87,6 +87,7 @@ export class GfUserAccountSettingsComponent implements OnInit {
protected hasPermissionToUpdateUserSettings: boolean; protected hasPermissionToUpdateUserSettings: boolean;
protected isAccessTokenHidden = true; protected isAccessTokenHidden = true;
protected readonly isFingerprintSupported = this.doesBrowserSupportAuthn(); protected readonly isFingerprintSupported = this.doesBrowserSupportAuthn();
protected isLoading = true;
protected isWebAuthnEnabled: boolean; protected isWebAuthnEnabled: boolean;
protected readonly language = document.documentElement.lang; protected readonly language = document.documentElement.lang;
protected locales = [ protected locales = [
@ -178,6 +179,8 @@ export class GfUserAccountSettingsComponent implements OnInit {
this.locales = Array.from(new Set(this.locales)).sort(); this.locales = Array.from(new Set(this.locales)).sort();
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });

1
apps/client/src/app/components/user-account-settings/user-account-settings.html

@ -273,6 +273,7 @@
<gf-value <gf-value
class="text-monospace" class="text-monospace"
[enableCopyToClipboardButton]="true" [enableCopyToClipboardButton]="true"
[isLoading]="isLoading"
[value]="user?.id" [value]="user?.id"
/> />
</div> </div>

39
apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html

@ -36,12 +36,17 @@
i18n i18n
size="medium" size="medium"
[enableCopyToClipboardButton]="true" [enableCopyToClipboardButton]="true"
[value]="isLoading ? undefined : user.id" [isLoading]="isLoading"
[value]="user?.id"
>User ID</gf-value >User ID</gf-value
> >
</div> </div>
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="isLoading ? undefined : user.role" <gf-value
i18n
size="medium"
[isLoading]="isLoading"
[value]="user?.role"
>Role</gf-value >Role</gf-value
> >
</div> </div>
@ -53,8 +58,9 @@
i18n i18n
size="medium" size="medium"
[isDate]="true" [isDate]="true"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="isLoading ? undefined : user.createdAt" [value]="user?.createdAt"
>Registration Date</gf-value >Registration Date</gf-value
> >
</div> </div>
@ -62,8 +68,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="isLoading ? undefined : user.provider" [value]="user?.provider"
>Authentication</gf-value >Authentication</gf-value
> >
</div> </div>
@ -75,10 +82,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]=" [value]="user?.subscription ? 'Premium' : 'Basic'"
isLoading ? undefined : user.subscription ? 'Premium' : 'Basic'
"
>Membership</gf-value >Membership</gf-value
> >
</div> </div>
@ -86,12 +92,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[value]=" [value]="
isLoading user?.country ? getCountryName({ code: user.country }) : '-'
? undefined
: user.country
? getCountryName({ code: user.country })
: '-'
" "
>Country</gf-value >Country</gf-value
> >
@ -104,8 +107,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="isLoading ? undefined : user.accountCount" [value]="user?.accountCount"
>Accounts</gf-value >Accounts</gf-value
> >
</div> </div>
@ -113,8 +117,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="isLoading ? undefined : user.activityCount" [value]="user?.activityCount"
>Activities</gf-value >Activities</gf-value
> >
</div> </div>
@ -126,9 +131,10 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="0" [precision]="0"
[value]="isLoading ? undefined : user.engagement" [value]="user?.engagement"
>Engagement per Day</gf-value >Engagement per Day</gf-value
> >
</div> </div>
@ -136,8 +142,9 @@
<gf-value <gf-value
i18n i18n
size="medium" size="medium"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[value]="isLoading ? undefined : user.dailyApiRequests" [value]="user?.dailyApiRequests"
>API Requests Today</gf-value >API Requests Today</gf-value
> >
</div> </div>

11
apps/client/src/app/pages/portfolio/allocations/allocations-page.html

@ -14,12 +14,9 @@
<gf-value <gf-value
class="justify-content-end l-2" class="justify-content-end l-2"
size="medium" size="medium"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[value]=" [value]="portfolioDetails?.summary?.filteredValueInPercentage"
isLoading
? undefined
: portfolioDetails?.summary?.filteredValueInPercentage
"
/> />
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
@ -213,6 +210,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[value]="markets?.developedMarkets?.valueInPercentage" [value]="markets?.developedMarkets?.valueInPercentage"
>Developed Markets</gf-value >Developed Markets</gf-value
@ -222,6 +220,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[value]="markets?.emergingMarkets?.valueInPercentage" [value]="markets?.emergingMarkets?.valueInPercentage"
>Emerging Markets</gf-value >Emerging Markets</gf-value
@ -231,6 +230,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[value]="markets?.otherMarkets?.valueInPercentage" [value]="markets?.otherMarkets?.valueInPercentage"
>Other Markets</gf-value >Other Markets</gf-value
@ -241,6 +241,7 @@
<gf-value <gf-value
i18n i18n
size="large" size="large"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[value]="markets?.[UNKNOWN_KEY]?.valueInPercentage" [value]="markets?.[UNKNOWN_KEY]?.valueInPercentage"
>No data available</gf-value >No data available</gf-value

69
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -83,14 +83,11 @@
i18n i18n
size="large" size="large"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="precision" [precision]="precision"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]=" [value]="performance?.currentValueInBaseCurrency"
isLoadingInvestmentChart
? undefined
: performance?.currentValueInBaseCurrency
"
>Total amount</gf-value >Total amount</gf-value
> >
</mat-card-content> </mat-card-content>
@ -104,14 +101,11 @@
size="large" size="large"
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[precision]="precision" [precision]="precision"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]=" [value]="performance?.netPerformanceWithCurrencyEffect"
isLoadingInvestmentChart
? undefined
: performance?.netPerformanceWithCurrencyEffect
"
>Change with currency effect</gf-value >Change with currency effect</gf-value
> >
</mat-card-content> </mat-card-content>
@ -124,13 +118,10 @@
i18n i18n
size="large" size="large"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true" [isPercent]="true"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]=" [value]="performance?.netPerformancePercentageWithCurrencyEffect"
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentageWithCurrencyEffect
"
>Performance with currency effect</gf-value >Performance with currency effect</gf-value
> >
</mat-card-content> </mat-card-content>
@ -173,13 +164,10 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]=" [value]="performance?.netPerformance"
isLoadingInvestmentChart
? undefined
: performance?.netPerformance
"
/> />
</div> </div>
</div> </div>
@ -192,13 +180,10 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true" [isPercent]="true"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]=" [value]="performance?.netPerformancePercentage"
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentage
"
/> />
</div> </div>
</div> </div>
@ -216,15 +201,14 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]=" [value]="
isLoadingInvestmentChart performance?.netPerformance === null
? undefined ? null
: performance?.netPerformance === null : performance?.netPerformanceWithCurrencyEffect -
? null performance?.netPerformance
: performance?.netPerformanceWithCurrencyEffect -
performance?.netPerformance
" "
/> />
</div> </div>
@ -238,15 +222,14 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true" [isPercent]="true"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]=" [value]="
isLoadingInvestmentChart performance?.netPerformancePercentage === null
? undefined ? null
: performance?.netPerformancePercentage === null : performance?.netPerformancePercentageWithCurrencyEffect -
? null performance?.netPerformancePercentage
: performance?.netPerformancePercentageWithCurrencyEffect -
performance?.netPerformancePercentage
" "
/> />
</div> </div>
@ -261,13 +244,10 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency" [unit]="user?.settings?.baseCurrency"
[value]=" [value]="performance?.netPerformanceWithCurrencyEffect"
isLoadingInvestmentChart
? undefined
: performance?.netPerformanceWithCurrencyEffect
"
/> />
</div> </div>
</div> </div>
@ -280,12 +260,11 @@
class="justify-content-end" class="justify-content-end"
position="end" position="end"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true" [isPercent]="true"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]=" [value]="
isLoadingInvestmentChart performance?.netPerformancePercentageWithCurrencyEffect
? undefined
: performance?.netPerformancePercentageWithCurrencyEffect
" "
/> />
</div> </div>

3
apps/client/src/app/pages/public/public-page.component.ts

@ -69,6 +69,7 @@ export class GfPublicPageComponent implements OnInit {
protected hasPermissionForSubscription: boolean; protected hasPermissionForSubscription: boolean;
protected holdings: PublicPortfolioResponse['holdings'][string][]; protected holdings: PublicPortfolioResponse['holdings'][string][];
protected info: InfoItem; protected info: InfoItem;
protected isLoading = true;
protected latestActivitiesDataSource: MatTableDataSource< protected latestActivitiesDataSource: MatTableDataSource<
PublicPortfolioResponse['latestActivities'][0] PublicPortfolioResponse['latestActivities'][0]
>; >;
@ -138,6 +139,8 @@ export class GfPublicPageComponent implements OnInit {
this.publicPortfolioDetails.latestActivities this.publicPortfolioDetails.latestActivities
); );
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

12
apps/client/src/app/pages/public/public-page.html

@ -15,11 +15,11 @@
i18n i18n
size="large" size="large"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[precision]="2" [precision]="2"
[value]=" [value]="
publicPortfolioDetails?.performance?.['1d']?.relativeChange ?? publicPortfolioDetails?.performance?.['1d']?.relativeChange
undefined
" "
>Today</gf-value >Today</gf-value
> >
@ -33,11 +33,11 @@
i18n i18n
size="large" size="large"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[precision]="2" [precision]="2"
[value]=" [value]="
publicPortfolioDetails?.performance?.['ytd']?.relativeChange ?? publicPortfolioDetails?.performance?.['ytd']?.relativeChange
undefined
" "
>This year</gf-value >This year</gf-value
> >
@ -51,11 +51,11 @@
i18n i18n
size="large" size="large"
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true" [isPercent]="true"
[precision]="2" [precision]="2"
[value]=" [value]="
publicPortfolioDetails?.performance?.['max']?.relativeChange ?? publicPortfolioDetails?.performance?.['max']?.relativeChange
undefined
" "
>From the beginning</gf-value >From the beginning</gf-value
> >

3
apps/client/src/app/services/import-activities.service.ts

@ -1,3 +1,4 @@
import { NON_INVESTMENT_ACTIVITY_TYPES } from '@ghostfolio/common/config';
import { import {
CreateAccountWithBalancesDto, CreateAccountWithBalancesDto,
CreateAssetProfileWithMarketDataDto, CreateAssetProfileWithMarketDataDto,
@ -70,7 +71,7 @@ export class ImportActivitiesService {
let dataSource = this.parseDataSource({ item }); let dataSource = this.parseDataSource({ item });
let symbol = this.parseSymbol({ content, index, item }); let symbol = this.parseSymbol({ content, index, item });
if (!dataSource && ['FEE', 'INTEREST', 'LIABILITY'].includes(type)) { if (!dataSource && NON_INVESTMENT_ACTIVITY_TYPES.includes(type)) {
// Apply the same data source as the import service // Apply the same data source as the import service
dataSource = DataSource.MANUAL; dataSource = DataSource.MANUAL;
} }

22
apps/client/src/locales/messages.es.xlf

@ -356,7 +356,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4467323362722952678" datatype="html"> <trans-unit id="4467323362722952678" datatype="html">
<source>Unknown</source> <source>Unknown</source>
<target state="new">Unknown</target> <target state="translated">Desconocido</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/i18n.ts</context> <context context-type="sourcefile">libs/ui/src/lib/i18n.ts</context>
<context context-type="linenumber">88</context> <context context-type="linenumber">88</context>
@ -440,7 +440,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2671970132878857664" datatype="html"> <trans-unit id="2671970132878857664" datatype="html">
<source>Splits</source> <source>Splits</source>
<target state="new">Splits</target> <target state="translated">Desdoblamientos</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">512</context> <context context-type="linenumber">512</context>
@ -840,7 +840,7 @@
</trans-unit> </trans-unit>
<trans-unit id="504959340312049745" datatype="html"> <trans-unit id="504959340312049745" datatype="html">
<source>Split Ratio</source> <source>Split Ratio</source>
<target state="new">Split Ratio</target> <target state="translated">Relación de desdoblamiento</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">526</context> <context context-type="linenumber">526</context>
@ -1776,7 +1776,7 @@
</trans-unit> </trans-unit>
<trans-unit id="659291745893337969" datatype="html"> <trans-unit id="659291745893337969" datatype="html">
<source>Invalid API key</source> <source>Invalid API key</source>
<target state="new">Invalid API key</target> <target state="translated">Clave de API no válida</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-settings/admin-settings.component.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-settings/admin-settings.component.html</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">112</context>
@ -3056,7 +3056,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1825717711340685842" datatype="html"> <trans-unit id="1825717711340685842" datatype="html">
<source>Export <x id="INTERPOLATION" equiv-text="{{ totalItems &gt; 1 ? totalItems : &apos;&apos; }}"/> <x id="ICU" equiv-text="{totalItems, plural, =1 {Activity} other {Activities}}"/></source> <source>Export <x id="INTERPOLATION" equiv-text="{{ totalItems &gt; 1 ? totalItems : &apos;&apos; }}"/> <x id="ICU" equiv-text="{totalItems, plural, =1 {Activity} other {Activities}}"/></source>
<target state="new">Export <x id="INTERPOLATION" equiv-text="{{ totalItems &gt; 1 ? totalItems : &apos;&apos; }}"/> <x id="ICU" equiv-text="{totalItems, plural, =1 {Activity} other {Activities}}"/></target> <target state="translated">Exportar <x id="INTERPOLATION" equiv-text="{{ totalItems &gt; 1 ? totalItems : &apos;&apos; }}"/> <x id="ICU" equiv-text="{totalItems, plural, =1 {Actividad} other {Actividades}}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">68</context> <context context-type="linenumber">68</context>
@ -4168,7 +4168,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4344577155823592293" datatype="html"> <trans-unit id="4344577155823592293" datatype="html">
<source>Cache has been flushed.</source> <source>Cache has been flushed.</source>
<target state="new">Cache has been flushed.</target> <target state="translated">Caché vaciada.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.component.ts</context>
<context context-type="linenumber">281</context> <context context-type="linenumber">281</context>
@ -4324,7 +4324,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6718883297275862532" datatype="html"> <trans-unit id="6718883297275862532" datatype="html">
<source>Shares After</source> <source>Shares After</source>
<target state="new">Shares After</target> <target state="translated">Acciones después</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">584</context> <context context-type="linenumber">584</context>
@ -5292,7 +5292,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8993940354220895245" datatype="html"> <trans-unit id="8993940354220895245" datatype="html">
<source>Do you really want to delete these <x id="count" equiv-text="this.totalItems"/> activities?</source> <source>Do you really want to delete these <x id="count" equiv-text="this.totalItems"/> activities?</source>
<target state="new">Do you really want to delete these <x id="count" equiv-text="this.totalItems"/> activities?</target> <target state="translated">¿De verdad quieres eliminar estas <x id="count" equiv-text="this.totalItems"/> actividades?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.ts</context>
<context context-type="linenumber">334</context> <context context-type="linenumber">334</context>
@ -5758,7 +5758,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3430061313424019324" datatype="html"> <trans-unit id="3430061313424019324" datatype="html">
<source>Data gathering has been started.</source> <source>Data gathering has been started.</source>
<target state="new">Data gathering has been started.</target> <target state="translated">La recopilación de datos ha comenzado.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">413</context> <context context-type="linenumber">413</context>
@ -7367,7 +7367,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4604813705660070247" datatype="html"> <trans-unit id="4604813705660070247" datatype="html">
<source>Shares Before</source> <source>Shares Before</source>
<target state="new">Shares Before</target> <target state="translated">Acciones antes</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">597</context> <context context-type="linenumber">597</context>
@ -8170,7 +8170,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6278975207599702712" datatype="html"> <trans-unit id="6278975207599702712" datatype="html">
<source>{VAR_PLURAL, plural, =1 {Activity} other {Activities}}</source> <source>{VAR_PLURAL, plural, =1 {Activity} other {Activities}}</source>
<target state="new">{VAR_PLURAL, plural, =1 {Activity} other {Activities}}</target> <target state="translated">{VAR_PLURAL, plural, =1 {Actividad} other {Actividades}}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">69</context> <context context-type="linenumber">69</context>

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

@ -231,6 +231,12 @@ export const INVESTMENT_ACTIVITY_TYPES = [
Type.SELL Type.SELL
] as Type[]; ] as Type[];
export const NON_INVESTMENT_ACTIVITY_TYPES = Object.values(Type).filter(
(type) => {
return !INVESTMENT_ACTIVITY_TYPES.includes(type);
}
);
export const PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME = 'PORTFOLIO'; export const PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME = 'PORTFOLIO';
export const PORTFOLIO_SNAPSHOT_PROCESS_JOB_OPTIONS: JobOptions = { export const PORTFOLIO_SNAPSHOT_PROCESS_JOB_OPTIONS: JobOptions = {
removeOnComplete: true removeOnComplete: true

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

@ -210,8 +210,9 @@
<gf-value <gf-value
[deviceType]="deviceType" [deviceType]="deviceType"
[isDate]="true" [isDate]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.date" [value]="element.date"
/> />
</div> </div>
</td> </td>
@ -234,8 +235,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.quantity" [value]="element.quantity"
/> />
</div> </div>
</td> </td>
@ -258,8 +260,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.unitPrice" [value]="element.unitPrice"
/> />
</div> </div>
</td> </td>
@ -282,8 +285,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.fee" [value]="element.fee"
/> />
</div> </div>
</td> </td>
@ -305,8 +309,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.value" [value]="element.value"
/> />
</div> </div>
</td> </td>
@ -337,8 +342,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale" [locale]="locale"
[value]="isLoading() ? undefined : element.valueInBaseCurrency" [value]="element.valueInBaseCurrency"
/> />
</div> </div>
</td> </td>

3
libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts

@ -46,6 +46,7 @@ import { BenchmarkDetailDialogParams } from './interfaces/interfaces';
export class GfBenchmarkDetailDialogComponent implements OnInit { export class GfBenchmarkDetailDialogComponent implements OnInit {
public assetProfile: AdminMarketDataDetails['assetProfile']; public assetProfile: AdminMarketDataDetails['assetProfile'];
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public isLoading = true;
public value: number; public value: number;
public constructor( public constructor(
@ -79,6 +80,8 @@ export class GfBenchmarkDetailDialogComponent implements OnInit {
} }
); );
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

1
libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.html

@ -11,6 +11,7 @@
<div class="col-12 d-flex justify-content-center mb-3"> <div class="col-12 d-flex justify-content-center mb-3">
<gf-value <gf-value
size="large" size="large"
[isLoading]="isLoading"
[locale]="data.locale" [locale]="data.locale"
[precision]="2" [precision]="2"
[value]="value" [value]="value"

21
libs/ui/src/lib/holdings-table/holdings-table.component.html

@ -81,8 +81,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale()" [locale]="locale()"
[value]="isLoading() ? undefined : element.quantity" [value]="element.quantity"
/> />
</div> </div>
</td> </td>
@ -105,8 +106,9 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale()" [locale]="locale()"
[value]="isLoading() ? undefined : element.valueInBaseCurrency" [value]="element.valueInBaseCurrency"
/> />
</div> </div>
</td> </td>
@ -125,9 +127,10 @@
<td *matCellDef="let element" class="px-1" mat-cell> <td *matCellDef="let element" class="px-1" mat-cell>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[isLoading]="isLoading()"
[isPercent]="true" [isPercent]="true"
[locale]="locale()" [locale]="locale()"
[value]="isLoading() ? undefined : element.allocationInPercentage" [value]="element.allocationInPercentage"
/> />
</div> </div>
</td> </td>
@ -147,10 +150,9 @@
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale()" [locale]="locale()"
[value]=" [value]="element.netPerformanceWithCurrencyEffect"
isLoading() ? undefined : element.netPerformanceWithCurrencyEffect
"
/> />
</div> </div>
</td> </td>
@ -170,13 +172,10 @@
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isLoading]="isLoading()"
[isPercent]="true" [isPercent]="true"
[locale]="locale()" [locale]="locale()"
[value]=" [value]="element.netPerformancePercentWithCurrencyEffect"
isLoading()
? undefined
: element.netPerformancePercentWithCurrencyEffect
"
/> />
</div> </div>
</td> </td>

7
libs/ui/src/lib/shared/abstract-mat-form-field.ts

@ -1,7 +1,7 @@
import { FocusMonitor } from '@angular/cdk/a11y'; import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { import {
Component, Directive,
DoCheck, DoCheck,
ElementRef, ElementRef,
HostBinding, HostBinding,
@ -13,10 +13,7 @@ import { ControlValueAccessor, NgControl, Validators } from '@angular/forms';
import { MatFormFieldControl } from '@angular/material/form-field'; import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@Component({ @Directive()
template: '',
standalone: false
})
export abstract class AbstractMatFormField<T> export abstract class AbstractMatFormField<T>
implements ControlValueAccessor, DoCheck, MatFormFieldControl<T>, OnDestroy implements ControlValueAccessor, DoCheck, MatFormFieldControl<T>, OnDestroy
{ {

26
libs/ui/src/lib/value/value.component.html

@ -27,7 +27,20 @@
} }
</ng-template> </ng-template>
@if (value || value === 0 || value === null) { <!--
@deprecated Rendering the loading state for an undefined value is kept for
backward compatibility. Use the isLoading attribute instead.
-->
@if (isLoading() || value === undefined) {
<ngx-skeleton-loader
animation="pulse"
[theme]="{
height:
size === 'large' ? '2rem' : size === 'medium' ? '1.8rem' : '1.5rem',
width: '5rem'
}"
/>
} @else if (value || value === 0 || value === null) {
<div <div
class="align-items-center d-flex" class="align-items-center d-flex"
[class.justify-content-end]="position === 'end'" [class.justify-content-end]="position === 'end'"
@ -75,17 +88,6 @@
</div> </div>
} }
@if (value === undefined) {
<ngx-skeleton-loader
animation="pulse"
[theme]="{
height:
size === 'large' ? '2rem' : size === 'medium' ? '1.8rem' : '1.5rem',
width: '5rem'
}"
/>
}
@if (size === 'large') { @if (size === 'large') {
<div class="text-truncate"> <div class="text-truncate">
<span class="h6"><ng-container *ngTemplateOutlet="label" /></span> <span class="h6"><ng-container *ngTemplateOutlet="label" /></span>

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

@ -30,6 +30,9 @@ export default {
enableCopyToClipboardButton: { enableCopyToClipboardButton: {
control: 'boolean' control: 'boolean'
}, },
isLoading: {
control: 'boolean'
},
size: { size: {
control: 'select', control: 'select',
options: ['small', 'medium', 'large'] options: ['small', 'medium', 'large']
@ -41,7 +44,7 @@ type Story = StoryObj<GfValueComponent>;
export const Loading: Story = { export const Loading: Story = {
args: { args: {
value: undefined isLoading: true
} }
}; };

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

@ -62,6 +62,8 @@ export class GfValueComponent implements AfterViewInit, OnChanges, OnDestroy {
public readonly copiedTitle = $localize`The value has been copied to the clipboard`; public readonly copiedTitle = $localize`The value has been copied to the clipboard`;
public readonly copyToClipboardTitle = $localize`Copy to clipboard`; public readonly copyToClipboardTitle = $localize`Copy to clipboard`;
public readonly isLoading = input<boolean>(false);
public readonly precision = input<number>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
@ -74,8 +76,6 @@ export class GfValueComponent implements AfterViewInit, OnChanges, OnDestroy {
}); });
} }
public readonly precision = input<number>();
private copyToClipboardTimeout: ReturnType<typeof setTimeout>; private copyToClipboardTimeout: ReturnType<typeof setTimeout>;
private readonly formatOptions = computed<Intl.NumberFormatOptions>(() => { private readonly formatOptions = computed<Intl.NumberFormatOptions>(() => {

4
package-lock.json

@ -1,12 +1,12 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "3.42.0", "version": "3.43.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ghostfolio", "name": "ghostfolio",
"version": "3.42.0", "version": "3.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "3.42.0", "version": "3.43.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",

1
test/import/ok/500-activities.json

@ -8,7 +8,6 @@
"balance": 2000, "balance": 2000,
"currency": "USD", "currency": "USD",
"id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0", "id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
"isExcluded": false,
"name": "My Online Trading Account", "name": "My Online Trading Account",
"platformId": null "platformId": null
} }

1
test/import/ok/derived-currency.json

@ -8,7 +8,6 @@
"balance": 2000, "balance": 2000,
"currency": "USD", "currency": "USD",
"id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0", "id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
"isExcluded": false,
"name": "My Online Trading Account", "name": "My Online Trading Account",
"platformId": null "platformId": null
} }

1
test/import/ok/sample.json

@ -19,7 +19,6 @@
"comment": null, "comment": null,
"currency": "USD", "currency": "USD",
"id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0", "id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
"isExcluded": false,
"name": "My Online Trading Account", "name": "My Online Trading Account",
"platformId": "9da3a8a7-4795-43e3-a6db-ccb914189737" "platformId": "9da3a8a7-4795-43e3-a6db-ccb914189737"
} }

Loading…
Cancel
Save