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
### 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 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
- Extended the value component by an `isLoading` attribute to distinguish the loading state from redacted values
### Changed
- 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 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
- Improved the language localization for Spanish (`es`)
### 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

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

@ -20,6 +20,7 @@ import {
DATA_GATHERING_QUEUE_PRIORITY_HIGH,
GATHER_ASSET_PROFILE_PROCESS_JOB_NAME,
GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS,
NON_INVESTMENT_ACTIVITY_TYPES,
TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config';
import {
@ -191,7 +192,7 @@ export class ActivitiesService {
const userId = data.userId;
if (
['FEE', 'INTEREST', 'LIABILITY'].includes(data.type) ||
NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type) ||
(data.SymbolProfile.connectOrCreate.create.dataSource === 'MANUAL' &&
data.type === 'BUY')
) {
@ -260,7 +261,7 @@ export class ActivitiesService {
const orderData: Prisma.OrderCreateInput = data;
const isDraft = ['FEE', 'INTEREST', 'LIABILITY'].includes(data.type)
const isDraft = NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type)
? false
: isAfter(data.date as Date, endOfToday());
@ -981,7 +982,7 @@ export class ActivitiesService {
let isDraft = false;
if (
['FEE', 'INTEREST', 'LIABILITY'].includes(data.type) ||
NON_INVESTMENT_ACTIVITY_TYPES.includes(data.type) ||
(data.SymbolProfile.connect.dataSource_symbol.dataSource === 'MANUAL' &&
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 {
DATA_GATHERING_QUEUE_PRIORITY_HIGH,
ghostfolioPrefix,
NON_INVESTMENT_ACTIVITY_TYPES,
TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config';
import {
@ -223,7 +224,7 @@ export class ImportService {
// createActivity() if needed.
for (const [index, activity] of activitiesDto.entries()) {
if (!activity.dataSource) {
if (['FEE', 'INTEREST', 'LIABILITY'].includes(activity.type)) {
if (NON_INVESTMENT_ACTIVITY_TYPES.includes(activity.type)) {
activity.dataSource = DataSource.MANUAL;
} else {
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>;
symbol: string;
}): Promise<Partial<SymbolProfile>> {
if (
!(
response.assetClass === 'EQUITY' &&
['ETF', 'MUTUALFUND'].includes(response.assetSubClass)
)
) {
if (!(
response.assetClass === 'EQUITY' &&
['ETF', 'MUTUALFUND'].includes(response.assetSubClass)
)) {
return response;
}
@ -214,8 +212,8 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
return undefined;
})
.catch(({ message }) => {
this.logger.error(
`Failed to search Trackinsight symbol for ${symbol} (${message})`
this.logger.warn(
`Could not search Trackinsight symbol for "${symbol}": ${message}`
);
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 {
DEFAULT_CURRENCY,
DERIVED_CURRENCIES,
NON_INVESTMENT_ACTIVITY_TYPES,
PROPERTY_API_KEY_GHOSTFOLIO,
PROPERTY_DATA_SOURCE_MAPPING
} from '@ghostfolio/common/config';
@ -268,7 +269,7 @@ export class DataProviderService implements OnModuleInit {
if (!assetProfiles[assetProfileIdentifier]) {
if (
(dataSource === DataSource.MANUAL && type === 'BUY') ||
['FEE', 'INTEREST', 'LIABILITY'].includes(type)
NON_INVESTMENT_ACTIVITY_TYPES.includes(type)
) {
const assetProfileInImport = assetProfilesWithMarketDataDto?.find(
(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,
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 interestInBaseCurrency: number;
protected interestInBaseCurrencyPrecision = 2;
protected isLoading = true;
protected isLoadingChart: boolean;
protected name: string | null;
protected pageIndex = 0;
@ -302,6 +303,8 @@ export class GfAccountDetailDialogComponent implements OnInit {
this.valueInBaseCurrency = valueInBaseCurrency;
this.isLoading = false;
this.changeDetectorRef.markForCheck();
}
);

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

@ -12,6 +12,7 @@
<gf-value
size="large"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="valueInBaseCurrency"
@ -48,6 +49,7 @@
i18n
size="medium"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[precision]="balancePrecision"
[unit]="currency"
@ -60,6 +62,7 @@
i18n
size="medium"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[precision]="equityPrecision"
[unit]="currency"
@ -72,6 +75,7 @@
i18n
size="medium"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[precision]="interestInBaseCurrencyPrecision"
[unit]="user?.settings?.baseCurrency"
@ -84,6 +88,7 @@
i18n
size="medium"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[precision]="dividendInBaseCurrencyPrecision"
[unit]="user?.settings?.baseCurrency"
@ -94,6 +99,7 @@
<div class="col-6 mb-3">
<gf-value
size="medium"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[value]="activitiesCount"
>
@ -108,6 +114,7 @@
<gf-value
i18n
size="medium"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[value]="platformName"
>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 isDataGatheringEnabled: boolean;
protected isEditAssetProfileIdentifierMode = false;
protected isLoading = true;
protected readonly isUUID = isUUID;
protected marketDataItems: MarketData[] = [];
@ -496,6 +497,8 @@ export class GfAssetProfileDialogComponent implements OnInit {
this.assetProfileForm.markAsPristine();
this.isLoading = false;
this.changeDetectorRef.markForCheck();
});
}

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

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

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

@ -7,6 +7,7 @@
i18n
size="large"
[enableCopyToClipboardButton]="true"
[isLoading]="isLoading"
[value]="version"
>Version</gf-value
>
@ -19,6 +20,7 @@
<gf-value
i18n
size="large"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[value]="userCount"
>Users</gf-value
@ -32,6 +34,7 @@
<gf-value
i18n
size="large"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[subLabel]="activitiesCountPerUser"
[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 investmentInBaseCurrencyWithCurrencyEffectPrecision = 2;
protected isLoading = true;
protected readonly isUUID = isUUID;
protected marketDataItems: MarketData[] = [];
protected marketPrice: number;
@ -571,6 +572,8 @@ export class GfHoldingDetailDialogComponent implements OnInit {
this.fetchMarketData();
}
this.isLoading = false;
this.changeDetectorRef.markForCheck();
}
);

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

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

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

@ -38,24 +38,18 @@
<gf-value
[colorizeSign]="true"
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale()"
[value]="
isLoading()
? undefined
: performance().netPerformanceWithCurrencyEffect
"
[value]="performance()?.netPerformanceWithCurrencyEffect"
/>
</div>
<div class="col">
<gf-value
[colorizeSign]="true"
[isLoading]="isLoading()"
[isPercent]="true"
[locale]="locale()"
[value]="
isLoading()
? undefined
: performance().netPerformancePercentageWithCurrencyEffect
"
[value]="performance()?.netPerformancePercentageWithCurrencyEffect"
/>
</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-grow-1 text-truncate" i18n>Time in Market</div>
<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
@ -33,10 +37,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalBuy"
[value]="summary?.totalBuy"
/>
</div>
</div>
@ -46,10 +51,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalSell"
[value]="summary?.totalSell"
/>
</div>
</div>
@ -62,14 +68,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="
isLoading
? undefined
: summary?.totalInvestmentValueWithCurrencyEffect
"
[value]="summary?.totalInvestmentValueWithCurrencyEffect"
/>
</div>
</div>
@ -79,12 +82,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="
isLoading ? undefined : summary?.grossPerformanceWithCurrencyEffect
"
[value]="summary?.grossPerformanceWithCurrencyEffect"
/>
</div>
</div>
@ -97,10 +99,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.fees"
[value]="summary?.fees"
/>
</div>
</div>
@ -113,12 +116,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="
isLoading ? undefined : summary?.netPerformanceWithCurrencyEffect
"
[value]="summary?.netPerformanceWithCurrencyEffect"
/>
</div>
</div>
@ -136,13 +138,10 @@
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading
? undefined
: summary?.netPerformancePercentageWithCurrencyEffect
"
[value]="summary?.netPerformancePercentageWithCurrencyEffect"
/>
</div>
</div>
@ -156,10 +155,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalAssetsInBaseCurrency"
[value]="summary?.totalAssetsInBaseCurrency"
/>
</div>
</div>
@ -188,10 +188,11 @@
) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : holdingsPercentage"
[value]="holdingsPercentage"
/>
}
</div>
@ -200,10 +201,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : holdingsInBaseCurrency"
[value]="holdingsInBaseCurrency"
/>
</div>
</div>
@ -215,10 +217,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : investmentsInBaseCurrency"
[value]="investmentsInBaseCurrency"
/>
</div>
</div>
@ -229,10 +232,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets"
[value]="summary?.emergencyFund?.assets"
/>
</div>
</div>
@ -262,10 +266,11 @@
) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : cashPercentage"
[value]="cashPercentage"
/>
}
</div>
@ -274,10 +279,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalCashInBaseCurrency"
[value]="summary?.totalCashInBaseCurrency"
/>
</div>
</div>
@ -289,10 +295,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash"
[value]="summary?.cash"
/>
</div>
</div>
@ -303,10 +310,11 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash"
[value]="summary?.emergencyFund?.cash"
/>
</div>
</div>
@ -321,20 +329,22 @@
) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : excludedFromAnalysisPercentage"
[value]="excludedFromAnalysisPercentage"
/>
}
</div>
<div class="align-items-center d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.excludedAccountsAndActivities"
[value]="summary?.excludedAccountsAndActivities"
/>
</div>
</div>
@ -353,10 +363,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.liabilitiesInBaseCurrency"
[value]="summary?.liabilitiesInBaseCurrency"
/>
</div>
</div>
@ -369,10 +380,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalValueInBaseCurrency"
[value]="summary?.totalValueInBaseCurrency"
/>
</div>
</div>
@ -385,13 +397,10 @@
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading
? undefined
: summary?.annualizedPerformancePercentWithCurrencyEffect
"
[value]="summary?.annualizedPerformancePercentWithCurrencyEffect"
/>
</div>
</div>
@ -408,10 +417,11 @@
) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isLoading]="isLoading"
[isPercent]="true"
[locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : emergencyFundPercentage"
[value]="emergencyFundPercentage"
/>
}
</div>
@ -443,10 +453,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.total"
[value]="summary?.emergencyFund?.total"
/>
</div>
</div>
@ -456,10 +467,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.interestInBaseCurrency"
[value]="summary?.interestInBaseCurrency"
/>
</div>
</div>
@ -469,10 +481,11 @@
<gf-value
class="justify-content-end"
[isCurrency]="true"
[isLoading]="isLoading"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.dividendInBaseCurrency"
[value]="summary?.dividendInBaseCurrency"
/>
</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 isAccessTokenHidden = true;
protected readonly isFingerprintSupported = this.doesBrowserSupportAuthn();
protected isLoading = true;
protected isWebAuthnEnabled: boolean;
protected readonly language = document.documentElement.lang;
protected locales = [
@ -178,6 +179,8 @@ export class GfUserAccountSettingsComponent implements OnInit {
this.locales = Array.from(new Set(this.locales)).sort();
this.isLoading = false;
this.changeDetectorRef.markForCheck();
}
});

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

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

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

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

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

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

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

@ -83,14 +83,11 @@
i18n
size="large"
[isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[precision]="precision"
[unit]="user?.settings?.baseCurrency"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.currentValueInBaseCurrency
"
[value]="performance?.currentValueInBaseCurrency"
>Total amount</gf-value
>
</mat-card-content>
@ -104,14 +101,11 @@
size="large"
[colorizeSign]="true"
[isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[precision]="precision"
[unit]="user?.settings?.baseCurrency"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformanceWithCurrencyEffect
"
[value]="performance?.netPerformanceWithCurrencyEffect"
>Change with currency effect</gf-value
>
</mat-card-content>
@ -124,13 +118,10 @@
i18n
size="large"
[colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true"
[locale]="user?.settings?.locale"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentageWithCurrencyEffect
"
[value]="performance?.netPerformancePercentageWithCurrencyEffect"
>Performance with currency effect</gf-value
>
</mat-card-content>
@ -173,13 +164,10 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformance
"
[value]="performance?.netPerformance"
/>
</div>
</div>
@ -192,13 +180,10 @@
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true"
[locale]="user?.settings?.locale"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentage
"
[value]="performance?.netPerformancePercentage"
/>
</div>
</div>
@ -216,15 +201,14 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformance === null
? null
: performance?.netPerformanceWithCurrencyEffect -
performance?.netPerformance
performance?.netPerformance === null
? null
: performance?.netPerformanceWithCurrencyEffect -
performance?.netPerformance
"
/>
</div>
@ -238,15 +222,14 @@
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true"
[locale]="user?.settings?.locale"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentage === null
? null
: performance?.netPerformancePercentageWithCurrencyEffect -
performance?.netPerformancePercentage
performance?.netPerformancePercentage === null
? null
: performance?.netPerformancePercentageWithCurrencyEffect -
performance?.netPerformancePercentage
"
/>
</div>
@ -261,13 +244,10 @@
class="justify-content-end"
position="end"
[isCurrency]="true"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformanceWithCurrencyEffect
"
[value]="performance?.netPerformanceWithCurrencyEffect"
/>
</div>
</div>
@ -280,12 +260,11 @@
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isLoading]="isLoadingInvestmentChart"
[isPercent]="true"
[locale]="user?.settings?.locale"
[value]="
isLoadingInvestmentChart
? undefined
: performance?.netPerformancePercentageWithCurrencyEffect
performance?.netPerformancePercentageWithCurrencyEffect
"
/>
</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 holdings: PublicPortfolioResponse['holdings'][string][];
protected info: InfoItem;
protected isLoading = true;
protected latestActivitiesDataSource: MatTableDataSource<
PublicPortfolioResponse['latestActivities'][0]
>;
@ -138,6 +139,8 @@ export class GfPublicPageComponent implements OnInit {
this.publicPortfolioDetails.latestActivities
);
this.isLoading = false;
this.changeDetectorRef.markForCheck();
});
}

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

@ -15,11 +15,11 @@
i18n
size="large"
[colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true"
[precision]="2"
[value]="
publicPortfolioDetails?.performance?.['1d']?.relativeChange ??
undefined
publicPortfolioDetails?.performance?.['1d']?.relativeChange
"
>Today</gf-value
>
@ -33,11 +33,11 @@
i18n
size="large"
[colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true"
[precision]="2"
[value]="
publicPortfolioDetails?.performance?.['ytd']?.relativeChange ??
undefined
publicPortfolioDetails?.performance?.['ytd']?.relativeChange
"
>This year</gf-value
>
@ -51,11 +51,11 @@
i18n
size="large"
[colorizeSign]="true"
[isLoading]="isLoading"
[isPercent]="true"
[precision]="2"
[value]="
publicPortfolioDetails?.performance?.['max']?.relativeChange ??
undefined
publicPortfolioDetails?.performance?.['max']?.relativeChange
"
>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 {
CreateAccountWithBalancesDto,
CreateAssetProfileWithMarketDataDto,
@ -70,7 +71,7 @@ export class ImportActivitiesService {
let dataSource = this.parseDataSource({ 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
dataSource = DataSource.MANUAL;
}

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

@ -356,7 +356,7 @@
</trans-unit>
<trans-unit id="4467323362722952678" datatype="html">
<source>Unknown</source>
<target state="new">Unknown</target>
<target state="translated">Desconocido</target>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/i18n.ts</context>
<context context-type="linenumber">88</context>
@ -440,7 +440,7 @@
</trans-unit>
<trans-unit id="2671970132878857664" datatype="html">
<source>Splits</source>
<target state="new">Splits</target>
<target state="translated">Desdoblamientos</target>
<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="linenumber">512</context>
@ -840,7 +840,7 @@
</trans-unit>
<trans-unit id="504959340312049745" datatype="html">
<source>Split Ratio</source>
<target state="new">Split Ratio</target>
<target state="translated">Relación de desdoblamiento</target>
<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="linenumber">526</context>
@ -1776,7 +1776,7 @@
</trans-unit>
<trans-unit id="659291745893337969" datatype="html">
<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 context-type="sourcefile">apps/client/src/app/components/admin-settings/admin-settings.component.html</context>
<context context-type="linenumber">112</context>
@ -3056,7 +3056,7 @@
</trans-unit>
<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>
<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 context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">68</context>
@ -4168,7 +4168,7 @@
</trans-unit>
<trans-unit id="4344577155823592293" datatype="html">
<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 context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.component.ts</context>
<context context-type="linenumber">281</context>
@ -4324,7 +4324,7 @@
</trans-unit>
<trans-unit id="6718883297275862532" datatype="html">
<source>Shares After</source>
<target state="new">Shares After</target>
<target state="translated">Acciones después</target>
<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="linenumber">584</context>
@ -5292,7 +5292,7 @@
</trans-unit>
<trans-unit id="8993940354220895245" datatype="html">
<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 context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.ts</context>
<context context-type="linenumber">334</context>
@ -5758,7 +5758,7 @@
</trans-unit>
<trans-unit id="3430061313424019324" datatype="html">
<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 context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">413</context>
@ -7367,7 +7367,7 @@
</trans-unit>
<trans-unit id="4604813705660070247" datatype="html">
<source>Shares Before</source>
<target state="new">Shares Before</target>
<target state="translated">Acciones antes</target>
<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="linenumber">597</context>
@ -8170,7 +8170,7 @@
</trans-unit>
<trans-unit id="6278975207599702712" datatype="html">
<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 context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</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
] 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_OPTIONS: JobOptions = {
removeOnComplete: true

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

@ -210,8 +210,9 @@
<gf-value
[deviceType]="deviceType"
[isDate]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.date"
[value]="element.date"
/>
</div>
</td>
@ -234,8 +235,9 @@
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.quantity"
[value]="element.quantity"
/>
</div>
</td>
@ -258,8 +260,9 @@
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.unitPrice"
[value]="element.unitPrice"
/>
</div>
</td>
@ -282,8 +285,9 @@
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.fee"
[value]="element.fee"
/>
</div>
</td>
@ -305,8 +309,9 @@
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.value"
[value]="element.value"
/>
</div>
</td>
@ -337,8 +342,9 @@
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[isLoading]="isLoading()"
[locale]="locale"
[value]="isLoading() ? undefined : element.valueInBaseCurrency"
[value]="element.valueInBaseCurrency"
/>
</div>
</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 {
public assetProfile: AdminMarketDataDetails['assetProfile'];
public historicalDataItems: LineChartItem[];
public isLoading = true;
public value: number;
public constructor(
@ -79,6 +80,8 @@ export class GfBenchmarkDetailDialogComponent implements OnInit {
}
);
this.isLoading = false;
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">
<gf-value
size="large"
[isLoading]="isLoading"
[locale]="data.locale"
[precision]="2"
[value]="value"

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

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

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

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

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

@ -27,7 +27,20 @@
}
</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
class="align-items-center d-flex"
[class.justify-content-end]="position === 'end'"
@ -75,17 +88,6 @@
</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') {
<div class="text-truncate">
<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: {
control: 'boolean'
},
isLoading: {
control: 'boolean'
},
size: {
control: 'select',
options: ['small', 'medium', 'large']
@ -41,7 +44,7 @@ type Story = StoryObj<GfValueComponent>;
export const Loading: Story = {
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 copyToClipboardTitle = $localize`Copy to clipboard`;
public readonly isLoading = input<boolean>(false);
public readonly precision = input<number>();
public constructor(
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 readonly formatOptions = computed<Intl.NumberFormatOptions>(() => {

4
package-lock.json

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

2
package.json

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

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

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

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

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

1
test/import/ok/sample.json

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

Loading…
Cancel
Save