diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1fabe2f48..329a9a5a3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -26,7 +26,7 @@ Thank you for your understanding and cooperation! 2. 3. -**Expected behavior** +**Expected Behavior** @@ -48,6 +48,6 @@ Thank you for your understanding and cooperation! - Browser - OS -**Additional context** +**Additional Context** diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb9efe79..5f9b95dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,13 +96,123 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Improved the usability of the user account registration + +## 2.145.1 - 2025-03-10 + +### Added + +- Extended the export functionality by the account balances +- Added a _Copy portfolio data to clipboard for AI prompt_ action to the analysis page (experimental) + +### Changed + +- Improved the style of the summary on the _X-ray_ page +- Improved the language localization for German (`de`) +- Upgraded `@simplewebauthn/browser` and `@simplewebauthn/server` from version `9.0` to `13.1` + +### Fixed + +- Fixed an issue to get dividends in the _Financial Modeling Prep_ service +- Fixed an issue to get historical market data in the _Financial Modeling Prep_ service +- Fixed an issue with serving _Storybook_ + +## 2.144.0 - 2025-03-06 + +### Fixed + +- Fixed the missing import functionality on the non-empty activities page +- Fixed the functionality to delete an asset profile of a custom currency in the admin control panel + +## 2.143.0 - 2025-03-02 + +### Added + +- Added the Ghostfolio _LinkedIn_ page to the about page +- Added the Ghostfolio _LinkedIn_ page to the footer + +### Changed + +- Optimized the asynchronous operations using `Promise.all()` in the portfolio service (`getPerformance`) +- Improved the symbol lookup in the _Trackinsight_ data enhancer for asset profile data +- Removed the no transactions info component from the holdings table on the home page +- Refactored the show condition of the step by step introduction for new users using the activities count +- Upgraded `color` from version `4.2.3` to `5.0.0` +- Upgraded `prisma` from version `6.3.0` to `6.4.1` + +### Fixed + +- Handled an exception in the export functionality related to platforms +- Handled an exception in the benchmark service related to unnamed asset profiles + +## 2.142.0 - 2025-02-28 + +### Added + +- Extended the export functionality by the platforms +- Extended the portfolio snapshot in the portfolio calculator by the `createdAt` timestamp +- Extended the _Trackinsight_ data enhancer for asset profile data by `cusip` +- Added _Storybook_ to the build process + +### Changed + +- Upgraded `eslint` dependencies + +## 2.141.0 - 2025-02-25 + +### Added + +- Extended the export functionality by the tags +- Extended the portfolio snapshot in the portfolio calculator by the activities count +- Extended the user endpoint `GET api/v1/user` by the activities count +- Added `cusip` to the asset profile model + +### Changed + +- Upgraded `prettier` from version `3.4.2` to `3.5.1` + +### Fixed + +- Improved the numeric comparison of strings in the value component + +## 2.140.0 - 2025-02-20 + +### Changed + +- Reloaded the available tags after creating a custom tag in the holding detail dialog (experimental) +- Improved the validation of the currency management in the admin control panel +- Migrated the `@ghostfolio/client` components to control flow +- Migrated the `@ghostfolio/ui` components to control flow +- Improved the language localization for German (`de`) + +### Fixed + +- Improved the error handling in the `HttpResponseInterceptor` +- Fixed an issue while using symbol profile overrides in the historical market data table of the admin control panel +- Added missing assets in _Storybook_ setup + +## 2.139.1 - 2025-02-15 + +### Added + +- Extended the tooltip in the chart of the holdings tab on the home page by the allocation, change and performance +- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Asia-Pacific Markets) +- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Japan) +- Added support to create custom tags in the holding detail dialog (experimental) - Extended the tags selector component by a `readonly` attribute - Extended the tags selector component to support creating custom tags +- Extended the holding detail dialog by the historical market data editor (experimental) - Added global styles to the _Storybook_ setup ### Changed +- Improved the symbol lookup in the _Trackinsight_ data enhancer for asset profile data - Improved the language localization for German (`de`) +- Upgraded `@trivago/prettier-plugin-sort-imports` from version `5.2.1` to `5.2.2` + +### Fixed + +- Fixed the gaps in the chart of the benchmark comparator ## 2.138.0 - 2025-02-08 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 9e32b56eb..cea43095d 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -101,3 +101,12 @@ https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push Run `npm run prisma migrate dev --name added_job_title` https://www.prisma.io/docs/concepts/components/prisma-migrate#getting-started-with-prisma-migrate + +## SSL + +Generate `localhost.cert` and `localhost.pem` files. + +``` +openssl req -x509 -newkey rsa:2048 -nodes -keyout apps/client/localhost.pem -out apps/client/localhost.cert -days 365 \ + -subj "/C=CH/ST=State/L=City/O=Organization/OU=Unit/CN=localhost" +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..528fecd44 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Reporting Security Issues + +If you discover a security vulnerability in this repository, please report it to security[at]ghostfol.io. We will acknowledge your report and provide guidance on the next steps. + +To help us resolve the issue, please include the following details: + +- A description of the vulnerability +- Steps to reproduce the vulnerability +- Affected versions of the software + +We appreciate your responsible disclosure and will work to address the issue promptly. diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index 7b9b09c0c..74b612b7e 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -8,7 +8,13 @@ import { Filter } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { Account, Order, Platform, Prisma } from '@prisma/client'; +import { + Account, + AccountBalance, + Order, + Platform, + Prisma +} from '@prisma/client'; import { Big } from 'big.js'; import { format } from 'date-fns'; import { groupBy } from 'lodash'; @@ -57,13 +63,19 @@ export class AccountService { orderBy?: Prisma.AccountOrderByWithRelationInput; }): Promise< (Account & { + balances?: AccountBalance[]; Order?: Order[]; Platform?: Platform; })[] > { const { include = {}, skip, take, cursor, where, orderBy } = params; - include.balances = { orderBy: { date: 'desc' }, take: 1 }; + const isBalancesIncluded = !!include.balances; + + include.balances = { + orderBy: { date: 'desc' }, + ...(isBalancesIncluded ? {} : { take: 1 }) + }; const accounts = await this.prismaService.account.findMany({ cursor, @@ -77,7 +89,9 @@ export class AccountService { return accounts.map((account) => { account = { ...account, balance: account.balances[0]?.value ?? 0 }; - delete account.balances; + if (!isBalancesIncluded) { + delete account.balances; + } return account; }); diff --git a/apps/api/src/app/admin/admin.module.ts b/apps/api/src/app/admin/admin.module.ts index 55acd194c..b02c0203b 100644 --- a/apps/api/src/app/admin/admin.module.ts +++ b/apps/api/src/app/admin/admin.module.ts @@ -1,8 +1,8 @@ -import { BenchmarkModule } from '@ghostfolio/api/app/benchmark/benchmark.module'; import { OrderModule } from '@ghostfolio/api/app/order/order.module'; import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; +import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 8a09dbdcb..99344716c 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -1,7 +1,7 @@ -import { BenchmarkService } from '@ghostfolio/api/app/benchmark/benchmark.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { environment } from '@ghostfolio/api/environments/environment'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; @@ -30,6 +30,7 @@ import { EnhancedSymbolProfile, Filter } from '@ghostfolio/common/interfaces'; +import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { MarketDataPreset } from '@ghostfolio/common/types'; import { BadRequestException, Injectable, Logger } from '@nestjs/common'; @@ -108,7 +109,26 @@ export class AdminService { symbol }: AssetProfileIdentifier) { await this.marketDataService.deleteMany({ dataSource, symbol }); - await this.symbolProfileService.delete({ dataSource, symbol }); + + const currency = getCurrencyFromSymbol(symbol); + const customCurrencies = (await this.propertyService.getByKey( + PROPERTY_CURRENCIES + )) as string[]; + + if (customCurrencies.includes(currency)) { + const updatedCustomCurrencies = customCurrencies.filter( + (customCurrency) => { + return customCurrency !== currency; + } + ); + + await this.putSetting( + PROPERTY_CURRENCIES, + JSON.stringify(updatedCustomCurrencies) + ); + } else { + await this.symbolProfileService.delete({ dataSource, symbol }); + } } public async get(): Promise { @@ -260,6 +280,7 @@ export class AdminService { scraperConfiguration: true, sectors: true, symbol: true, + SymbolProfileOverrides: true, tags: true } }), @@ -315,11 +336,10 @@ export class AdminService { Order, sectors, symbol, + SymbolProfileOverrides, tags }) => { - const countriesCount = countries - ? Object.keys(countries).length - : 0; + let countriesCount = countries ? Object.keys(countries).length : 0; const lastMarketPrice = lastMarketPriceMap.get( getAssetProfileIdentifier({ dataSource, symbol }) @@ -333,7 +353,34 @@ export class AdminService { ); })?._count ?? 0; - const sectorsCount = sectors ? Object.keys(sectors).length : 0; + let sectorsCount = sectors ? Object.keys(sectors).length : 0; + + if (SymbolProfileOverrides) { + assetClass = SymbolProfileOverrides.assetClass ?? assetClass; + assetSubClass = + SymbolProfileOverrides.assetSubClass ?? assetSubClass; + + if ( + ( + SymbolProfileOverrides.countries as unknown as Prisma.JsonArray + )?.length > 0 + ) { + countriesCount = ( + SymbolProfileOverrides.countries as unknown as Prisma.JsonArray + ).length; + } + + name = SymbolProfileOverrides.name ?? name; + + if ( + (SymbolProfileOverrides.sectors as unknown as Sector[]) + ?.length > 0 + ) { + sectorsCount = ( + SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray + ).length; + } + } return { assetClass, diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 6d097aeff..2a515bf43 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -29,13 +29,14 @@ import { AppController } from './app.controller'; import { AssetModule } from './asset/asset.module'; import { AuthDeviceModule } from './auth-device/auth-device.module'; import { AuthModule } from './auth/auth.module'; -import { BenchmarkModule } from './benchmark/benchmark.module'; import { CacheModule } from './cache/cache.module'; import { AiModule } from './endpoints/ai/ai.module'; import { ApiKeysModule } from './endpoints/api-keys/api-keys.module'; +import { BenchmarksModule } from './endpoints/benchmarks/benchmarks.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; import { MarketDataModule } from './endpoints/market-data/market-data.module'; import { PublicModule } from './endpoints/public/public.module'; +import { TagsModule } from './endpoints/tags/tags.module'; import { ExchangeRateModule } from './exchange-rate/exchange-rate.module'; import { ExportModule } from './export/export.module'; import { HealthModule } from './health/health.module'; @@ -49,7 +50,6 @@ import { RedisCacheModule } from './redis-cache/redis-cache.module'; import { SitemapModule } from './sitemap/sitemap.module'; import { SubscriptionModule } from './subscription/subscription.module'; import { SymbolModule } from './symbol/symbol.module'; -import { TagModule } from './tag/tag.module'; import { UserModule } from './user/user.module'; @Module({ @@ -63,7 +63,7 @@ import { UserModule } from './user/user.module'; AssetModule, AuthDeviceModule, AuthModule, - BenchmarkModule, + BenchmarksModule, BullModule.forRoot({ redis: { db: parseInt(process.env.REDIS_DB ?? '0', 10), @@ -124,7 +124,7 @@ import { UserModule } from './user/user.module'; SitemapModule, SubscriptionModule, SymbolModule, - TagModule, + TagsModule, TwitterBotModule, UserModule ], diff --git a/apps/api/src/app/auth/web-auth.service.ts b/apps/api/src/app/auth/web-auth.service.ts index 2f8dd1018..5678ef7fe 100644 --- a/apps/api/src/app/auth/web-auth.service.ts +++ b/apps/api/src/app/auth/web-auth.service.ts @@ -24,6 +24,7 @@ import { verifyRegistrationResponse, VerifyRegistrationResponseOpts } from '@simplewebauthn/server'; +import { isoBase64URL, isoUint8Array } from '@simplewebauthn/server/helpers'; import { AssertionCredentialJSON, @@ -54,10 +55,9 @@ export class WebAuthService { const opts: GenerateRegistrationOptionsOpts = { rpName: 'Ghostfolio', rpID: this.rpID, - userID: user.id, + userID: isoUint8Array.fromUTF8String(user.id), userName: '', timeout: 60000, - attestationType: 'indirect', authenticatorSelection: { authenticatorAttachment: 'platform', requireResidentKey: false, @@ -111,11 +111,17 @@ export class WebAuthService { where: { userId: user.id } }); if (registrationInfo && verified) { - const { counter, credentialID, credentialPublicKey } = registrationInfo; + const { + credential: { + counter, + id: credentialId, + publicKey: credentialPublicKey + } + } = registrationInfo; - let existingDevice = devices.find( - (device) => device.credentialId === credentialID - ); + let existingDevice = devices.find((device) => { + return isoBase64URL.fromBuffer(device.credentialId) === credentialId; + }); if (!existingDevice) { /** @@ -123,7 +129,7 @@ export class WebAuthService { */ existingDevice = await this.deviceService.createAuthDevice({ counter, - credentialId: Buffer.from(credentialID), + credentialId: Buffer.from(credentialId), credentialPublicKey: Buffer.from(credentialPublicKey), User: { connect: { id: user.id } } }); @@ -148,9 +154,8 @@ export class WebAuthService { const opts: GenerateAuthenticationOptionsOpts = { allowCredentials: [ { - id: device.credentialId, - transports: ['internal'], - type: 'public-key' + id: isoBase64URL.fromBuffer(device.credentialId), + transports: ['internal'] } ], rpID: this.rpID, @@ -187,10 +192,10 @@ export class WebAuthService { let verification: VerifiedAuthenticationResponse; try { const opts: VerifyAuthenticationResponseOpts = { - authenticator: { - credentialID: device.credentialId, - credentialPublicKey: device.credentialPublicKey, - counter: device.counter + credential: { + counter: device.counter, + id: isoBase64URL.fromBuffer(device.credentialId), + publicKey: device.credentialPublicKey }, expectedChallenge: `${user.authChallenge}`, expectedOrigin: this.expectedOrigin, diff --git a/apps/api/src/app/benchmark/benchmark.module.ts b/apps/api/src/app/benchmark/benchmark.module.ts deleted file mode 100644 index 4c5f4d58e..000000000 --- a/apps/api/src/app/benchmark/benchmark.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; -import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; -import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; -import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; -import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; -import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; -import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; -import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; -import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; -import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; -import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; - -import { Module } from '@nestjs/common'; - -import { BenchmarkController } from './benchmark.controller'; -import { BenchmarkService } from './benchmark.service'; - -@Module({ - controllers: [BenchmarkController], - exports: [BenchmarkService], - imports: [ - ConfigurationModule, - DataProviderModule, - ExchangeRateDataModule, - MarketDataModule, - PrismaModule, - PropertyModule, - RedisCacheModule, - SymbolModule, - SymbolProfileModule, - TransformDataSourceInRequestModule, - TransformDataSourceInResponseModule - ], - providers: [BenchmarkService] -}) -export class BenchmarkModule {} diff --git a/apps/api/src/app/endpoints/ai/ai.controller.ts b/apps/api/src/app/endpoints/ai/ai.controller.ts index 981b26aa2..910abbf96 100644 --- a/apps/api/src/app/endpoints/ai/ai.controller.ts +++ b/apps/api/src/app/endpoints/ai/ai.controller.ts @@ -6,9 +6,9 @@ import { } from '@ghostfolio/common/config'; import { AiPromptResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; -import type { RequestWithUser } from '@ghostfolio/common/types'; +import type { AiPromptMode, RequestWithUser } from '@ghostfolio/common/types'; -import { Controller, Get, Inject, UseGuards } from '@nestjs/common'; +import { Controller, Get, Inject, Param, UseGuards } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; @@ -21,11 +21,14 @@ export class AiController { @Inject(REQUEST) private readonly request: RequestWithUser ) {} - @Get('prompt') + @Get('prompt/:mode') @HasPermission(permissions.readAiPrompt) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async getPrompt(): Promise { + public async getPrompt( + @Param('mode') mode: AiPromptMode + ): Promise { const prompt = await this.aiService.getPrompt({ + mode, impersonationId: undefined, languageCode: this.request.user.Settings.settings.language ?? DEFAULT_LANGUAGE_CODE, diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts index 59dec6add..d9090d77c 100644 --- a/apps/api/src/app/endpoints/ai/ai.service.ts +++ b/apps/api/src/app/endpoints/ai/ai.service.ts @@ -1,4 +1,5 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import type { AiPromptMode } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; @@ -9,11 +10,13 @@ export class AiService { public async getPrompt({ impersonationId, languageCode, + mode, userCurrency, userId }: { impersonationId: string; languageCode: string; + mode: AiPromptMode; userCurrency: string; userId: string; }) { @@ -43,6 +46,10 @@ export class AiService { ) ]; + if (mode === 'portfolio') { + return holdingsTable.join('\n'); + } + return [ `You are a neutral financial assistant. Please analyze the following investment portfolio (base currency being ${userCurrency}) in simple words.`, ...holdingsTable, diff --git a/apps/api/src/app/benchmark/benchmark.controller.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts similarity index 72% rename from apps/api/src/app/benchmark/benchmark.controller.ts rename to apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts index 66c268b9b..69383a30d 100644 --- a/apps/api/src/app/benchmark/benchmark.controller.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts @@ -2,7 +2,10 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; +import { ApiService } from '@ghostfolio/api/services/api/api.service'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; +import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import type { AssetProfileIdentifier, BenchmarkMarketDataDetails, @@ -16,6 +19,7 @@ import { Controller, Delete, Get, + Headers, HttpException, Inject, Param, @@ -29,12 +33,14 @@ import { AuthGuard } from '@nestjs/passport'; import { DataSource } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { BenchmarkService } from './benchmark.service'; +import { BenchmarksService } from './benchmarks.service'; -@Controller('benchmark') -export class BenchmarkController { +@Controller('benchmarks') +export class BenchmarksController { public constructor( + private readonly apiService: ApiService, private readonly benchmarkService: BenchmarkService, + private readonly benchmarksService: BenchmarksService, @Inject(REQUEST) private readonly request: RequestWithUser ) {} @@ -108,23 +114,43 @@ export class BenchmarkController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInRequestInterceptor) public async getBenchmarkMarketDataForUser( + @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Param('dataSource') dataSource: DataSource, @Param('startDateString') startDateString: string, @Param('symbol') symbol: string, - @Query('range') dateRange: DateRange = 'max' + @Query('range') dateRange: DateRange = 'max', + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, + @Query('symbol') filterBySymbol?: string, + @Query('tags') filterByTags?: string, + @Query('withExcludedAccounts') withExcludedAccountsParam = 'false' ): Promise { const { endDate, startDate } = getIntervalFromDateRange( dateRange, new Date(startDateString) ); - const userCurrency = this.request.user.Settings.settings.baseCurrency; - return this.benchmarkService.getMarketDataForUser({ + const filters = this.apiService.buildFiltersFromQueryParams({ + filterByAccounts, + filterByAssetClasses, + filterByDataSource, + filterBySymbol, + filterByTags + }); + + const withExcludedAccounts = withExcludedAccountsParam === 'true'; + + return this.benchmarksService.getMarketDataForUser({ dataSource, + dateRange, endDate, + filters, + impersonationId, startDate, symbol, - userCurrency + withExcludedAccounts, + user: this.request.user }); } } diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts new file mode 100644 index 000000000..a0f443621 --- /dev/null +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts @@ -0,0 +1,63 @@ +import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; +import { AccountService } from '@ghostfolio/api/app/account/account.service'; +import { OrderModule } from '@ghostfolio/api/app/order/order.module'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; +import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; +import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; +import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; +import { UserModule } from '@ghostfolio/api/app/user/user.module'; +import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; +import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; +import { ApiModule } from '@ghostfolio/api/services/api/api.module'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; +import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; +import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; +import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; + +import { Module } from '@nestjs/common'; + +import { BenchmarksController } from './benchmarks.controller'; +import { BenchmarksService } from './benchmarks.service'; + +@Module({ + controllers: [BenchmarksController], + imports: [ + ApiModule, + ConfigurationModule, + DataProviderModule, + ExchangeRateDataModule, + ImpersonationModule, + MarketDataModule, + OrderModule, + PortfolioSnapshotQueueModule, + PrismaModule, + PropertyModule, + RedisCacheModule, + SymbolModule, + SymbolProfileModule, + TransformDataSourceInRequestModule, + TransformDataSourceInResponseModule, + UserModule + ], + providers: [ + AccountBalanceService, + AccountService, + BenchmarkService, + BenchmarksService, + CurrentRateService, + MarketDataService, + PortfolioCalculatorFactory, + PortfolioService, + RulesService + ] +}) +export class BenchmarksModule {} diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts new file mode 100644 index 000000000..237f0d153 --- /dev/null +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -0,0 +1,163 @@ +import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; +import { + AssetProfileIdentifier, + BenchmarkMarketDataDetails, + Filter +} from '@ghostfolio/common/interfaces'; +import { DateRange, UserWithSettings } from '@ghostfolio/common/types'; + +import { Injectable, Logger } from '@nestjs/common'; +import { format, isSameDay } from 'date-fns'; +import { isNumber } from 'lodash'; + +@Injectable() +export class BenchmarksService { + public constructor( + private readonly benchmarkService: BenchmarkService, + private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly marketDataService: MarketDataService, + private readonly portfolioService: PortfolioService, + private readonly symbolService: SymbolService + ) {} + + public async getMarketDataForUser({ + dataSource, + dateRange, + endDate = new Date(), + filters, + impersonationId, + startDate, + symbol, + user, + withExcludedAccounts + }: { + dateRange: DateRange; + endDate?: Date; + filters?: Filter[]; + impersonationId: string; + startDate: Date; + user: UserWithSettings; + withExcludedAccounts?: boolean; + } & AssetProfileIdentifier): Promise { + const marketData: { date: string; value: number }[] = []; + const userCurrency = user.Settings.settings.baseCurrency; + const userId = user.id; + + const { chart } = await this.portfolioService.getPerformance({ + dateRange, + filters, + impersonationId, + userId, + withExcludedAccounts + }); + + const [currentSymbolItem, marketDataItems] = await Promise.all([ + this.symbolService.get({ + dataGatheringItem: { + dataSource, + symbol + } + }), + this.marketDataService.marketDataItems({ + orderBy: { + date: 'asc' + }, + where: { + dataSource, + symbol, + date: { + in: chart.map(({ date }) => { + return resetHours(parseDate(date)); + }) + } + } + }) + ]); + + const exchangeRates = + await this.exchangeRateDataService.getExchangeRatesByCurrency({ + startDate, + currencies: [currentSymbolItem.currency], + targetCurrency: userCurrency + }); + + const exchangeRateAtStartDate = + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ + format(startDate, DATE_FORMAT) + ]; + + const marketPriceAtStartDate = marketDataItems?.find(({ date }) => { + return isSameDay(date, startDate); + })?.marketPrice; + + if (!marketPriceAtStartDate) { + Logger.error( + `No historical market data has been found for ${symbol} (${dataSource}) at ${format( + startDate, + DATE_FORMAT + )}`, + 'BenchmarkService' + ); + + return { marketData }; + } + + for (const marketDataItem of marketDataItems) { + const exchangeRate = + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ + format(marketDataItem.date, DATE_FORMAT) + ]; + + const exchangeRateFactor = + isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) + ? exchangeRate / exchangeRateAtStartDate + : 1; + + marketData.push({ + date: format(marketDataItem.date, DATE_FORMAT), + value: + marketPriceAtStartDate === 0 + ? 0 + : this.benchmarkService.calculateChangeInPercentage( + marketPriceAtStartDate, + marketDataItem.marketPrice * exchangeRateFactor + ) * 100 + }); + } + + const includesEndDate = isSameDay( + parseDate(marketData.at(-1).date), + endDate + ); + + if (currentSymbolItem?.marketPrice && !includesEndDate) { + const exchangeRate = + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ + format(endDate, DATE_FORMAT) + ]; + + const exchangeRateFactor = + isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) + ? exchangeRate / exchangeRateAtStartDate + : 1; + + marketData.push({ + date: format(endDate, DATE_FORMAT), + value: + this.benchmarkService.calculateChangeInPercentage( + marketPriceAtStartDate, + currentSymbolItem.marketPrice * exchangeRateFactor + ) * 100 + }); + } + + return { + marketData + }; + } +} diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts index f3386f8a7..83c7317f0 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts @@ -2,6 +2,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { parseDate } from '@ghostfolio/common/helper'; import { + DataProviderGhostfolioAssetProfileResponse, DataProviderGhostfolioStatusResponse, DividendsResponse, HistoricalResponse, @@ -37,6 +38,41 @@ export class GhostfolioController { @Inject(REQUEST) private readonly request: RequestWithUser ) {} + @Get('asset-profile/:symbol') + @HasPermission(permissions.enableDataProviderGhostfolio) + @UseGuards(AuthGuard('api-key'), HasPermissionGuard) + public async getAssetProfile( + @Param('symbol') symbol: string + ): Promise { + const maxDailyRequests = await this.ghostfolioService.getMaxDailyRequests(); + + if ( + this.request.user.dataProviderGhostfolioDailyRequests > maxDailyRequests + ) { + throw new HttpException( + getReasonPhrase(StatusCodes.TOO_MANY_REQUESTS), + StatusCodes.TOO_MANY_REQUESTS + ); + } + + try { + const assetProfile = await this.ghostfolioService.getAssetProfile({ + symbol + }); + + await this.ghostfolioService.incrementDailyRequests({ + userId: this.request.user.id + }); + + return assetProfile; + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), + StatusCodes.INTERNAL_SERVER_ERROR + ); + } + } + /** * @deprecated */ diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts index 78685a61b..7281697bd 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -15,6 +16,7 @@ import { } from '@ghostfolio/common/config'; import { PROPERTY_DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER_MAX_REQUESTS } from '@ghostfolio/common/config'; import { + DataProviderGhostfolioAssetProfileResponse, DataProviderInfo, DividendsResponse, HistoricalResponse, @@ -25,7 +27,7 @@ import { import { UserWithSettings } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; -import { DataSource } from '@prisma/client'; +import { DataSource, SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; @Injectable() @@ -37,6 +39,44 @@ export class GhostfolioService { private readonly propertyService: PropertyService ) {} + public async getAssetProfile({ + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), + symbol + }: GetAssetProfileParams) { + let result: DataProviderGhostfolioAssetProfileResponse = {}; + + try { + const promises: Promise>[] = []; + + for (const dataProviderService of this.getDataProviderServices()) { + promises.push( + dataProviderService + .getAssetProfile({ + requestTimeout, + symbol + }) + .then((assetProfile) => { + result = { + ...result, + ...assetProfile, + dataSource: DataSource.GHOSTFOLIO + }; + + return assetProfile; + }) + ); + } + + await Promise.all(promises); + + return result; + } catch (error) { + Logger.error(error, 'GhostfolioService'); + + throw error; + } + } + public async getDividends({ from, granularity, @@ -277,6 +317,7 @@ export class GhostfolioService { }); results.items = filteredItems; + return results; } catch (error) { Logger.error(error, 'GhostfolioService'); diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts index b4aef807a..933e70e9d 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -1,6 +1,7 @@ import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -42,7 +43,7 @@ export class MarketDataController { { dataSource, symbol } ]); - if (!assetProfile) { + if (!assetProfile && !isCurrency(getCurrencyFromSymbol(symbol))) { throw new HttpException( getReasonPhrase(StatusCodes.NOT_FOUND), StatusCodes.NOT_FOUND @@ -55,7 +56,7 @@ export class MarketDataController { ); const canReadOwnAssetProfile = - assetProfile.userId === this.request.user.id && + assetProfile?.userId === this.request.user.id && hasPermission( this.request.user.permissions, permissions.readMarketDataOfOwnAssetProfile diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index 7488e4201..bdcd86afb 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -57,7 +57,7 @@ export class PublicController { } const [ - { holdings, markets }, + { createdAt, holdings, markets }, { performance: performance1d }, { performance: performanceMax }, { performance: performanceYtd } @@ -81,6 +81,7 @@ export class PublicController { }); const publicPortfolioResponse: PublicPortfolioResponse = { + createdAt, hasDetails, markets, alias: access.alias, diff --git a/apps/api/src/app/endpoints/tags/create-tag.dto.ts b/apps/api/src/app/endpoints/tags/create-tag.dto.ts new file mode 100644 index 000000000..09c29d25a --- /dev/null +++ b/apps/api/src/app/endpoints/tags/create-tag.dto.ts @@ -0,0 +1,10 @@ +import { IsOptional, IsString } from 'class-validator'; + +export class CreateTagDto { + @IsString() + name: string; + + @IsOptional() + @IsString() + userId?: string; +} diff --git a/apps/api/src/app/tag/tag.controller.ts b/apps/api/src/app/endpoints/tags/tags.controller.ts similarity index 62% rename from apps/api/src/app/tag/tag.controller.ts rename to apps/api/src/app/endpoints/tags/tags.controller.ts index 6198a0bf5..bf216bb21 100644 --- a/apps/api/src/app/tag/tag.controller.ts +++ b/apps/api/src/app/endpoints/tags/tags.controller.ts @@ -1,6 +1,8 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; -import { permissions } from '@ghostfolio/common/permissions'; +import { TagService } from '@ghostfolio/api/services/tag/tag.service'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -8,41 +10,63 @@ import { Delete, Get, HttpException, + Inject, Param, Post, Put, UseGuards } from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { Tag } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { CreateTagDto } from './create-tag.dto'; -import { TagService } from './tag.service'; import { UpdateTagDto } from './update-tag.dto'; -@Controller('tag') -export class TagController { - public constructor(private readonly tagService: TagService) {} - - @Get() - @HasPermission(permissions.readTags) - @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async getTags() { - return this.tagService.getTagsWithActivityCount(); - } +@Controller('tags') +export class TagsController { + public constructor( + @Inject(REQUEST) private readonly request: RequestWithUser, + private readonly tagService: TagService + ) {} @Post() - @HasPermission(permissions.createTag) - @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + @UseGuards(AuthGuard('jwt')) public async createTag(@Body() data: CreateTagDto): Promise { + const canCreateOwnTag = hasPermission( + this.request.user.permissions, + permissions.createOwnTag + ); + + const canCreateTag = hasPermission( + this.request.user.permissions, + permissions.createTag + ); + + if (!canCreateOwnTag && !canCreateTag) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + if (canCreateOwnTag && !canCreateTag) { + if (data.userId !== this.request.user.id) { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + } + return this.tagService.createTag(data); } - @HasPermission(permissions.updateTag) - @Put(':id') + @Delete(':id') + @HasPermission(permissions.deleteTag) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async updateTag(@Param('id') id: string, @Body() data: UpdateTagDto) { + public async deleteTag(@Param('id') id: string) { const originalTag = await this.tagService.getTag({ id }); @@ -54,20 +78,20 @@ export class TagController { ); } - return this.tagService.updateTag({ - data: { - ...data - }, - where: { - id - } - }); + return this.tagService.deleteTag({ id }); } - @Delete(':id') - @HasPermission(permissions.deleteTag) + @Get() + @HasPermission(permissions.readTags) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async deleteTag(@Param('id') id: string) { + public async getTags() { + return this.tagService.getTagsWithActivityCount(); + } + + @HasPermission(permissions.updateTag) + @Put(':id') + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async updateTag(@Param('id') id: string, @Body() data: UpdateTagDto) { const originalTag = await this.tagService.getTag({ id }); @@ -79,6 +103,13 @@ export class TagController { ); } - return this.tagService.deleteTag({ id }); + return this.tagService.updateTag({ + data: { + ...data + }, + where: { + id + } + }); } } diff --git a/apps/api/src/app/endpoints/tags/tags.module.ts b/apps/api/src/app/endpoints/tags/tags.module.ts new file mode 100644 index 000000000..a8a2f1c51 --- /dev/null +++ b/apps/api/src/app/endpoints/tags/tags.module.ts @@ -0,0 +1,12 @@ +import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; +import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; + +import { Module } from '@nestjs/common'; + +import { TagsController } from './tags.controller'; + +@Module({ + controllers: [TagsController], + imports: [PrismaModule, TagModule] +}) +export class TagsModule {} diff --git a/apps/api/src/app/endpoints/tags/update-tag.dto.ts b/apps/api/src/app/endpoints/tags/update-tag.dto.ts new file mode 100644 index 000000000..5ae42dcc6 --- /dev/null +++ b/apps/api/src/app/endpoints/tags/update-tag.dto.ts @@ -0,0 +1,13 @@ +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateTagDto { + @IsString() + id: string; + + @IsString() + name: string; + + @IsOptional() + @IsString() + userId?: string; +} diff --git a/apps/api/src/app/export/export.module.ts b/apps/api/src/app/export/export.module.ts index 048c60359..892a761cc 100644 --- a/apps/api/src/app/export/export.module.ts +++ b/apps/api/src/app/export/export.module.ts @@ -1,6 +1,7 @@ import { AccountModule } from '@ghostfolio/api/app/account/account.module'; import { OrderModule } from '@ghostfolio/api/app/order/order.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; +import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; import { Module } from '@nestjs/common'; @@ -8,7 +9,7 @@ import { ExportController } from './export.controller'; import { ExportService } from './export.service'; @Module({ - imports: [AccountModule, ApiModule, OrderModule], + imports: [AccountModule, ApiModule, OrderModule, TagModule], controllers: [ExportController], providers: [ExportService] }) diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 1ff18ce9c..8b9d2c56c 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -1,15 +1,18 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { environment } from '@ghostfolio/api/environments/environment'; +import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { Filter, Export } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; +import { Platform } from '@prisma/client'; @Injectable() export class ExportService { public constructor( private readonly accountService: AccountService, - private readonly orderService: OrderService + private readonly orderService: OrderService, + private readonly tagService: TagService ) {} public async export({ @@ -23,17 +26,40 @@ export class ExportService { userCurrency: string; userId: string; }): Promise { + const platformsMap: { [platformId: string]: Platform } = {}; + const accounts = ( await this.accountService.accounts({ + include: { + balances: true, + Platform: true + }, orderBy: { name: 'asc' }, where: { userId } }) ).map( - ({ balance, comment, currency, id, isExcluded, name, platformId }) => { + ({ + balance, + balances, + comment, + currency, + id, + isExcluded, + name, + Platform: platform, + platformId + }) => { + if (platformId) { + platformsMap[platformId] = platform; + } + return { balance, + balances: balances.map(({ date, value }) => { + return { date: date.toISOString(), value }; + }), comment, currency, id, @@ -60,9 +86,22 @@ export class ExportService { }); } + const tags = (await this.tagService.getTagsForUser(userId)) + .filter(({ isUsed }) => { + return isUsed; + }) + .map(({ id, name }) => { + return { + id, + name + }; + }); + return { meta: { date: new Date().toISOString(), version: environment.version }, accounts, + platforms: Object.values(platformsMap), + tags, activities: activities.map( ({ accountId, @@ -72,6 +111,7 @@ export class ExportService { id, quantity, SymbolProfile, + tags: currentTags, type, unitPrice }) => { @@ -86,13 +126,12 @@ export class ExportService { currency: SymbolProfile.currency, dataSource: SymbolProfile.dataSource, date: date.toISOString(), - symbol: - type === 'FEE' || - type === 'INTEREST' || - type === 'ITEM' || - type === 'LIABILITY' - ? SymbolProfile.name - : SymbolProfile.symbol + symbol: ['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes(type) + ? SymbolProfile.name + : SymbolProfile.symbol, + tags: currentTags.map(({ id: tagId }) => { + return tagId; + }) }; } ), diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 0d393bfeb..926372504 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -293,6 +293,7 @@ export class ImportService { assetSubClass, countries, createdAt, + cusip, dataSource, figi, figiComposite, @@ -367,6 +368,7 @@ export class ImportService { assetSubClass, countries, createdAt, + cusip, dataSource, figi, figiComposite, diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 7903ac397..d7a5ed641 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -1,8 +1,8 @@ -import { BenchmarkModule } from '@ghostfolio/api/app/benchmark/benchmark.module'; import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; +import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 1af41520d..780860232 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -1,7 +1,7 @@ -import { BenchmarkService } from '@ghostfolio/api/app/benchmark/benchmark.service'; import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 4cfe0e27a..fe1962995 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -172,6 +172,8 @@ export abstract class PortfolioCalculator { if (!transactionPoints.length) { return { + activitiesCount: 0, + createdAt: new Date(), currentValueInBaseCurrency: new Big(0), errors: [], hasErrors: false, diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts index d25e219b4..36a1eda12 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts @@ -101,6 +101,10 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { totalInterestWithCurrencyEffect, totalInvestment, totalInvestmentWithCurrencyEffect, + activitiesCount: this.activities.filter(({ type }) => { + return ['BUY', 'SELL'].includes(type); + }).length, + createdAt: new Date(), errors: [], historicalData: [], totalLiabilitiesWithCurrencyEffect: new Big(0), diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index ff7a3e1db..c00db4b40 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -108,6 +108,7 @@ export class PortfolioController { const { accounts, + createdAt, hasErrors, holdings, markets, @@ -254,6 +255,7 @@ export class PortfolioController { return { accounts, + createdAt, hasError, holdings, platforms, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index c2c1b4c5a..548140aa9 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -16,8 +16,10 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskAsiaPacific } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/asia-pacific'; import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; +import { RegionalMarketClusterRiskJapan } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/japan'; import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; @@ -381,7 +383,7 @@ export class PortfolioService { currency: userCurrency }); - const { currentValueInBaseCurrency, hasErrors, positions } = + const { createdAt, currentValueInBaseCurrency, hasErrors, positions } = await portfolioCalculator.getSnapshot(); const cashDetails = await this.accountService.getCashDetails({ @@ -626,6 +628,7 @@ export class PortfolioService { return { accounts, + createdAt, hasErrors, holdings, markets, @@ -1108,19 +1111,18 @@ export class PortfolioService { const user = await this.userService.user({ id: userId }); const userCurrency = this.getUserCurrency(user); - const accountBalanceItems = - await this.accountBalanceService.getAccountBalanceItems({ + const [accountBalanceItems, { activities }] = await Promise.all([ + this.accountBalanceService.getAccountBalanceItems({ filters, userId, userCurrency - }); - - const { activities } = - await this.orderService.getOrdersForPortfolioCalculator({ + }), + this.orderService.getOrdersForPortfolioCalculator({ filters, userCurrency, userId - }); + }) + ]); if (accountBalanceItems.length === 0 && activities.length === 0) { return { @@ -1309,6 +1311,11 @@ export class PortfolioService { summary.ordersCount > 0 ? await this.rulesService.evaluate( [ + new RegionalMarketClusterRiskAsiaPacific( + this.exchangeRateDataService, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.asiaPacific.valueInBaseCurrency + ), new RegionalMarketClusterRiskEmergingMarkets( this.exchangeRateDataService, marketsAdvancedTotalInBaseCurrency, @@ -1319,6 +1326,11 @@ export class PortfolioService { marketsAdvancedTotalInBaseCurrency, marketsAdvanced.europe.valueInBaseCurrency ), + new RegionalMarketClusterRiskJapan( + this.exchangeRateDataService, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.japan.valueInBaseCurrency + ), new RegionalMarketClusterRiskNorthAmerica( this.exchangeRateDataService, marketsAdvancedTotalInBaseCurrency, diff --git a/apps/api/src/app/tag/create-tag.dto.ts b/apps/api/src/app/tag/create-tag.dto.ts deleted file mode 100644 index 650a0ce12..000000000 --- a/apps/api/src/app/tag/create-tag.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsString } from 'class-validator'; - -export class CreateTagDto { - @IsString() - name: string; -} diff --git a/apps/api/src/app/tag/tag.module.ts b/apps/api/src/app/tag/tag.module.ts deleted file mode 100644 index 48587c54e..000000000 --- a/apps/api/src/app/tag/tag.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; - -import { Module } from '@nestjs/common'; - -import { TagController } from './tag.controller'; -import { TagService } from './tag.service'; - -@Module({ - controllers: [TagController], - exports: [TagService], - imports: [PrismaModule], - providers: [TagService] -}) -export class TagModule {} diff --git a/apps/api/src/app/tag/update-tag.dto.ts b/apps/api/src/app/tag/update-tag.dto.ts deleted file mode 100644 index b26ffde11..000000000 --- a/apps/api/src/app/tag/update-tag.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IsString } from 'class-validator'; - -export class UpdateTagDto { - @IsString() - id: string; - - @IsString() - name: string; -} diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 1dae9d45b..40bc1b2b5 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -13,8 +13,10 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskAsiaPacific } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/asia-pacific'; import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; +import { RegionalMarketClusterRiskJapan } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/japan'; import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; @@ -84,6 +86,9 @@ export class UserService { orderBy: { alias: 'asc' }, where: { GranteeUser: { id } } }), + this.prismaService.order.count({ + where: { userId: id } + }), this.prismaService.order.findFirst({ orderBy: { date: 'asc' @@ -94,8 +99,9 @@ export class UserService { ]); const access = userData[0]; - const firstActivity = userData[1]; - let tags = userData[2]; + const activitiesCount = userData[1]; + const firstActivity = userData[2]; + let tags = userData[3]; let systemMessage: SystemMessage; @@ -115,6 +121,7 @@ export class UserService { } return { + activitiesCount, id, permissions, subscription, @@ -272,6 +279,12 @@ export class UserService { undefined, undefined ).getSettings(user.Settings.settings), + RegionalMarketClusterRiskAsiaPacific: + new RegionalMarketClusterRiskAsiaPacific( + undefined, + undefined, + undefined + ).getSettings(user.Settings.settings), RegionalMarketClusterRiskEmergingMarkets: new RegionalMarketClusterRiskEmergingMarkets( undefined, @@ -283,6 +296,11 @@ export class UserService { undefined, undefined ).getSettings(user.Settings.settings), + RegionalMarketClusterRiskJapan: new RegionalMarketClusterRiskJapan( + undefined, + undefined, + undefined + ).getSettings(user.Settings.settings), RegionalMarketClusterRiskNorthAmerica: new RegionalMarketClusterRiskNorthAmerica( undefined, @@ -333,7 +351,11 @@ export class UserService { currentPermissions, permissions.accessHoldingsChart, permissions.createAccess, - permissions.readAiPrompt + permissions.createMarketDataOfOwnAssetProfile, + permissions.createOwnTag, + permissions.readAiPrompt, + permissions.readMarketDataOfOwnAssetProfile, + permissions.updateMarketDataOfOwnAssetProfile ); // Reset benchmark diff --git a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts index f5034927c..fcbf3e76e 100644 --- a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts @@ -33,9 +33,12 @@ export class TransformDataSourceInResponseInterceptor attribute: 'dataSource', valueMap: Object.keys(DataSource).reduce( (valueMap, dataSource) => { - valueMap[dataSource] = encodeDataSource( - DataSource[dataSource] - ); + if (!['MANUAL'].includes(dataSource)) { + valueMap[dataSource] = encodeDataSource( + DataSource[dataSource] + ); + } + return valueMap; }, {} diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index 6c929c388..b6c8f2e54 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -129,6 +129,7 @@ export const HtmlTemplateMiddleware = async ( if ( path.startsWith('/api/') || + path.startsWith('/development/storybook') || isFileRequest(path) || !environment.production ) { diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts b/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts new file mode 100644 index 000000000..d49849d54 --- /dev/null +++ b/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts @@ -0,0 +1,77 @@ +import { Rule } from '@ghostfolio/api/models/rule'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { UserSettings } from '@ghostfolio/common/interfaces'; + +import { Settings } from './interfaces/rule-settings.interface'; + +export class RegionalMarketClusterRiskAsiaPacific extends Rule { + private asiaPacificValueInBaseCurrency: number; + private currentValueInBaseCurrency: number; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + currentValueInBaseCurrency: number, + asiaPacificValueInBaseCurrency: number + ) { + super(exchangeRateDataService, { + key: RegionalMarketClusterRiskAsiaPacific.name, + name: 'Asia-Pacific' + }); + + this.asiaPacificValueInBaseCurrency = asiaPacificValueInBaseCurrency; + this.currentValueInBaseCurrency = currentValueInBaseCurrency; + } + + public evaluate(ruleSettings: Settings) { + const asiaPacificMarketValueRatio = this.currentValueInBaseCurrency + ? this.asiaPacificValueInBaseCurrency / this.currentValueInBaseCurrency + : 0; + + if (asiaPacificMarketValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The Asia-Pacific market contribution of your current investment (${(asiaPacificMarketValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (asiaPacificMarketValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The Asia-Pacific market contribution of your current investment (${(asiaPacificMarketValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The Asia-Pacific market contribution of your current investment (${(asiaPacificMarketValueRatio * 100).toPrecision(3)}%) is within the range of ${( + ruleSettings.thresholdMin * 100 + ).toPrecision( + 3 + )}% and ${(ruleSettings.thresholdMax * 100).toPrecision(3)}%`, + value: true + }; + } + + public getConfiguration() { + return { + threshold: { + max: 1, + min: 0, + step: 0.01, + unit: '%' + }, + thresholdMax: true, + thresholdMin: true + }; + } + + public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + return { + baseCurrency, + isActive: xRayRules?.[this.getKey()]?.isActive ?? true, + thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.03, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.02 + }; + } +} diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts new file mode 100644 index 000000000..4694b0006 --- /dev/null +++ b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts @@ -0,0 +1,77 @@ +import { Rule } from '@ghostfolio/api/models/rule'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { UserSettings } from '@ghostfolio/common/interfaces'; + +import { Settings } from './interfaces/rule-settings.interface'; + +export class RegionalMarketClusterRiskJapan extends Rule { + private currentValueInBaseCurrency: number; + private japanValueInBaseCurrency: number; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + currentValueInBaseCurrency: number, + japanValueInBaseCurrency: number + ) { + super(exchangeRateDataService, { + key: RegionalMarketClusterRiskJapan.name, + name: 'Japan' + }); + + this.currentValueInBaseCurrency = currentValueInBaseCurrency; + this.japanValueInBaseCurrency = japanValueInBaseCurrency; + } + + public evaluate(ruleSettings: Settings) { + const japanMarketValueRatio = this.currentValueInBaseCurrency + ? this.japanValueInBaseCurrency / this.currentValueInBaseCurrency + : 0; + + if (japanMarketValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (japanMarketValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 100).toPrecision(3)}%) is within the range of ${( + ruleSettings.thresholdMin * 100 + ).toPrecision( + 3 + )}% and ${(ruleSettings.thresholdMax * 100).toPrecision(3)}%`, + value: true + }; + } + + public getConfiguration() { + return { + threshold: { + max: 1, + min: 0, + step: 0.01, + unit: '%' + }, + thresholdMax: true, + thresholdMin: true + }; + } + + public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + return { + baseCurrency, + isActive: xRayRules?.[this.getKey()]?.isActive ?? true, + thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.06, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.04 + }; + } +} diff --git a/apps/api/src/services/benchmark/benchmark.module.ts b/apps/api/src/services/benchmark/benchmark.module.ts new file mode 100644 index 000000000..870ef244f --- /dev/null +++ b/apps/api/src/services/benchmark/benchmark.module.ts @@ -0,0 +1,24 @@ +import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; +import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; +import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; +import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; + +import { Module } from '@nestjs/common'; + +import { BenchmarkService } from './benchmark.service'; + +@Module({ + exports: [BenchmarkService], + imports: [ + DataProviderModule, + MarketDataModule, + PrismaModule, + PropertyModule, + RedisCacheModule, + SymbolProfileModule + ], + providers: [BenchmarkService] +}) +export class BenchmarkModule {} diff --git a/apps/api/src/app/benchmark/benchmark.service.spec.ts b/apps/api/src/services/benchmark/benchmark.service.spec.ts similarity index 74% rename from apps/api/src/app/benchmark/benchmark.service.spec.ts rename to apps/api/src/services/benchmark/benchmark.service.spec.ts index 5371fcdc0..833dbcdfc 100644 --- a/apps/api/src/app/benchmark/benchmark.service.spec.ts +++ b/apps/api/src/services/benchmark/benchmark.service.spec.ts @@ -4,17 +4,7 @@ describe('BenchmarkService', () => { let benchmarkService: BenchmarkService; beforeAll(async () => { - benchmarkService = new BenchmarkService( - null, - null, - null, - null, - null, - null, - null, - null, - null - ); + benchmarkService = new BenchmarkService(null, null, null, null, null, null); }); it('calculateChangeInPercentage', async () => { diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/services/benchmark/benchmark.service.ts similarity index 66% rename from apps/api/src/app/benchmark/benchmark.service.ts rename to apps/api/src/services/benchmark/benchmark.service.ts index 4e466668c..95cb9e5d2 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/services/benchmark/benchmark.service.ts @@ -1,8 +1,5 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; -import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; @@ -11,16 +8,10 @@ import { CACHE_TTL_INFINITE, PROPERTY_BENCHMARKS } from '@ghostfolio/common/config'; -import { - DATE_FORMAT, - calculateBenchmarkTrend, - parseDate, - resetHours -} from '@ghostfolio/common/helper'; +import { calculateBenchmarkTrend } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, Benchmark, - BenchmarkMarketDataDetails, BenchmarkProperty, BenchmarkResponse } from '@ghostfolio/common/interfaces'; @@ -29,16 +20,8 @@ import { BenchmarkTrend } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; -import { - addHours, - differenceInDays, - eachDayOfInterval, - format, - isAfter, - isSameDay, - subDays -} from 'date-fns'; -import { isNumber, uniqBy } from 'lodash'; +import { addHours, isAfter, subDays } from 'date-fns'; +import { uniqBy } from 'lodash'; import ms from 'ms'; import { BenchmarkValue } from './interfaces/benchmark-value.interface'; @@ -48,15 +31,12 @@ export class BenchmarkService { private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS'; public constructor( - private readonly configurationService: ConfigurationService, private readonly dataProviderService: DataProviderService, - private readonly exchangeRateDataService: ExchangeRateDataService, private readonly marketDataService: MarketDataService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, private readonly redisCacheService: RedisCacheService, - private readonly symbolProfileService: SymbolProfileService, - private readonly symbolService: SymbolService + private readonly symbolProfileService: SymbolProfileService ) {} public calculateChangeInPercentage(baseValue: number, currentValue: number) { @@ -153,139 +133,9 @@ export class BenchmarkService { symbol }; }) - .sort((a, b) => a.name.localeCompare(b.name)); - } - - public async getMarketDataForUser({ - dataSource, - endDate = new Date(), - startDate, - symbol, - userCurrency - }: { - endDate?: Date; - startDate: Date; - userCurrency: string; - } & AssetProfileIdentifier): Promise { - const marketData: { date: string; value: number }[] = []; - - const days = differenceInDays(endDate, startDate) + 1; - const dates = eachDayOfInterval( - { - start: startDate, - end: endDate - }, - { - step: Math.round( - days / - Math.min(days, this.configurationService.get('MAX_CHART_ITEMS')) - ) - } - ).map((date) => { - return resetHours(date); - }); - - const [currentSymbolItem, marketDataItems] = await Promise.all([ - this.symbolService.get({ - dataGatheringItem: { - dataSource, - symbol - } - }), - this.marketDataService.marketDataItems({ - orderBy: { - date: 'asc' - }, - where: { - dataSource, - symbol, - date: { - in: dates - } - } - }) - ]); - - const exchangeRates = - await this.exchangeRateDataService.getExchangeRatesByCurrency({ - startDate, - currencies: [currentSymbolItem.currency], - targetCurrency: userCurrency - }); - - const exchangeRateAtStartDate = - exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ - format(startDate, DATE_FORMAT) - ]; - - const marketPriceAtStartDate = marketDataItems?.find(({ date }) => { - return isSameDay(date, startDate); - })?.marketPrice; - - if (!marketPriceAtStartDate) { - Logger.error( - `No historical market data has been found for ${symbol} (${dataSource}) at ${format( - startDate, - DATE_FORMAT - )}`, - 'BenchmarkService' - ); - - return { marketData }; - } - - for (const marketDataItem of marketDataItems) { - const exchangeRate = - exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ - format(marketDataItem.date, DATE_FORMAT) - ]; - - const exchangeRateFactor = - isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) - ? exchangeRate / exchangeRateAtStartDate - : 1; - - marketData.push({ - date: format(marketDataItem.date, DATE_FORMAT), - value: - marketPriceAtStartDate === 0 - ? 0 - : this.calculateChangeInPercentage( - marketPriceAtStartDate, - marketDataItem.marketPrice * exchangeRateFactor - ) * 100 - }); - } - - const includesEndDate = isSameDay( - parseDate(marketData.at(-1).date), - endDate - ); - - if (currentSymbolItem?.marketPrice && !includesEndDate) { - const exchangeRate = - exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ - format(endDate, DATE_FORMAT) - ]; - - const exchangeRateFactor = - isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) - ? exchangeRate / exchangeRateAtStartDate - : 1; - - marketData.push({ - date: format(endDate, DATE_FORMAT), - value: - this.calculateChangeInPercentage( - marketPriceAtStartDate, - currentSymbolItem.marketPrice * exchangeRateFactor - ) * 100 + .sort((a, b) => { + return a.name?.localeCompare(b?.name) ?? 0; }); - } - - return { - marketData - }; } public async addBenchmark({ diff --git a/apps/api/src/app/benchmark/interfaces/benchmark-value.interface.ts b/apps/api/src/services/benchmark/interfaces/benchmark-value.interface.ts similarity index 100% rename from apps/api/src/app/benchmark/interfaces/benchmark-value.interface.ts rename to apps/api/src/services/benchmark/interfaces/benchmark-value.interface.ts diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 5c9eee127..606e6b7fd 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -41,9 +42,7 @@ export class AlphaVantageService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { return { symbol, dataSource: this.getName() diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index fb1fa9b63..d53355b9c 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -56,9 +57,7 @@ export class CoinGeckoService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { const response: Partial = { symbol, assetClass: AssetClass.LIQUIDITY, diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index 53a163596..652897d9f 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -43,60 +43,54 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return response; } + let trackinsightSymbol = await this.searchTrackinsightSymbol({ + requestTimeout, + symbol + }); + + if (!trackinsightSymbol) { + trackinsightSymbol = await this.searchTrackinsightSymbol({ + requestTimeout, + symbol: symbol.split('.')?.[0] + }); + } + + if (!trackinsightSymbol) { + return response; + } + const profile = await fetch( - `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`, + `${TrackinsightDataEnhancerService.baseUrl}/funds/${trackinsightSymbol}.json`, { signal: AbortSignal.timeout(requestTimeout) } ) .then((res) => res.json()) .catch(() => { - return fetch( - `${TrackinsightDataEnhancerService.baseUrl}/funds/${ - symbol.split('.')?.[0] - }.json`, - { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) - } - ) - .then((res) => res.json()) - .catch(() => { - return {}; - }); + return {}; }); - const isin = profile?.isin?.split(';')?.[0]; + const cusip = profile?.cusip; + + if (cusip) { + response.cusip = cusip; + } + + const isin = profile?.isins?.[0]; if (isin) { response.isin = isin; } const holdings = await fetch( - `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`, + `${TrackinsightDataEnhancerService.baseUrl}/holdings/${trackinsightSymbol}.json`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ) .then((res) => res.json()) .catch(() => { - return fetch( - `${TrackinsightDataEnhancerService.baseUrl}/holdings/${ - symbol.split('.')?.[0] - }.json`, - { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) - } - ) - .then((res) => res.json()) - .catch(() => { - return {}; - }); + return {}; }); if (holdings?.weight < 1 - Math.min(holdings?.count * 0.000015, 0.95)) { @@ -177,4 +171,36 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { public getTestSymbol() { return 'QQQ'; } + + private async searchTrackinsightSymbol({ + requestTimeout, + symbol + }: { + requestTimeout: number; + symbol: string; + }) { + return fetch( + `https://www.trackinsight.com/search-api/search_v2/${symbol}/_/ticker/default/0/3`, + { + signal: AbortSignal.timeout(requestTimeout) + } + ) + .then((res) => res.json()) + .then((jsonRes) => { + if ( + jsonRes['results']?.['count'] === 1 || + // Allow exact match + jsonRes['results']?.['docs']?.[0]?.['ticker'] === symbol || + // Allow EXCHANGE:SYMBOL + jsonRes['results']?.['docs']?.[0]?.['ticker']?.endsWith(`:${symbol}`) + ) { + return jsonRes['results']['docs'][0]['ticker']; + } + + return undefined; + }) + .catch(() => { + return undefined; + }); + } } diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index e427a830a..376b8f159 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -51,9 +52,7 @@ export class EodHistoricalDataService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { const [searchResult] = await this.getSearchResult(symbol); return { diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index 2d42fe21c..5216ed214 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -2,6 +2,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -56,10 +57,9 @@ export class FinancialModelingPrepService implements DataProviderInterface { } public async getAssetProfile({ + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { const response: Partial = { symbol, dataSource: this.getName() @@ -70,9 +70,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const [quote] = await fetch( `${this.URL}/quote/${symbol}?apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -84,9 +82,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const [assetProfile] = await fetch( `${this.URL}/profile/${symbol}?apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -100,9 +96,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const etfCountryWeightings = await fetch( `${this.URL}/etf-country-weightings/${symbol}?apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -127,9 +121,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const [etfInformation] = await fetch( `${this.getUrl({ version: 4 })}/etf-info?symbol=${symbol}&apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -140,9 +132,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const [portfolioDate] = await fetch( `${this.getUrl({ version: 4 })}/etf-holdings/portfolio-date?symbol=${symbol}&apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -150,9 +140,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const etfHoldings = await fetch( `${this.getUrl({ version: 4 })}/etf-holdings?date=${portfolioDate.date}&symbol=${symbol}&apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -170,9 +158,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const etfSectorWeightings = await fetch( `${this.URL}/etf-sector-weightings/${symbol}?apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -211,7 +197,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { if (error?.name === 'AbortError') { message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${( - this.configurationService.get('REQUEST_TIMEOUT') / 1000 + requestTimeout / 1000 ).toFixed(3)} seconds`; } @@ -244,7 +230,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { [date: string]: IDataProviderHistoricalResponse; } = {}; - const { historical } = await fetch( + const { historical = [] } = await fetch( `${this.URL}/historical-price-full/stock_dividend/${symbol}?apikey=${this.apiKey}`, { signal: AbortSignal.timeout(requestTimeout) @@ -305,7 +291,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { ? addYears(currentFrom, MAX_YEARS_PER_REQUEST) : to; - const { historical } = await fetch( + const { historical = [] } = await fetch( `${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`, { signal: AbortSignal.timeout(requestTimeout) @@ -376,7 +362,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { if (error?.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( - this.configurationService.get('REQUEST_TIMEOUT') / 1000 + requestTimeout / 1000 ).toFixed(3)} seconds`; } diff --git a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts index a674d479a..097464e2f 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -2,6 +2,7 @@ import { environment } from '@ghostfolio/api/environments/environment'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -18,6 +19,7 @@ import { } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + DataProviderGhostfolioAssetProfileResponse, DataProviderInfo, DividendsResponse, HistoricalResponse, @@ -46,21 +48,46 @@ export class GhostfolioService implements DataProviderInterface { } public async getAssetProfile({ + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbol - }: { - symbol: string; - }): Promise> { - const { items } = await this.search({ query: symbol }); - const searchResult = items?.[0]; + }: GetAssetProfileParams): Promise> { + let response: DataProviderGhostfolioAssetProfileResponse = {}; - return { - symbol, - assetClass: searchResult?.assetClass, - assetSubClass: searchResult?.assetSubClass, - currency: searchResult?.currency, - dataSource: this.getName(), - name: searchResult?.name - }; + try { + const assetProfile = (await fetch( + `${this.URL}/v1/data-providers/ghostfolio/asset-profile/${symbol}`, + { + headers: await this.getRequestHeaders(), + signal: AbortSignal.timeout(requestTimeout) + } + ).then((res) => + res.json() + )) as DataProviderGhostfolioAssetProfileResponse; + + response = assetProfile; + } catch (error) { + let message = error; + + if (error.name === 'AbortError') { + message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + requestTimeout / 1000 + ).toFixed(3)} seconds`; + } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { + message = 'RequestError: The daily request limit has been exceeded'; + } else if (error.response?.statusCode === StatusCodes.UNAUTHORIZED) { + if (!error.request?.options?.headers?.authorization?.includes('-')) { + message = + 'RequestError: The provided API key is invalid. Please update it in the Settings section of the Admin Control panel.'; + } else { + message = + 'RequestError: The provided API key has expired. Please request a new one and update it in the Settings section of the Admin Control panel.'; + } + } + + Logger.error(message, 'GhostfolioService'); + } + + return response; } public getDataProviderInfo(): DataProviderInfo { @@ -203,7 +230,7 @@ export class GhostfolioService implements DataProviderInterface { if (error.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( - this.configurationService.get('REQUEST_TIMEOUT') / 1000 + requestTimeout / 1000 ).toFixed(3)} seconds`; } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { message = 'RequestError: The daily request limit has been exceeded'; @@ -224,10 +251,13 @@ export class GhostfolioService implements DataProviderInterface { } public getTestSymbol() { - return 'AAPL.US'; + return 'AAPL'; } - public async search({ query }: GetSearchParams): Promise { + public async search({ + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), + query + }: GetSearchParams): Promise { let searchResult: LookupResponse = { items: [] }; try { @@ -235,9 +265,7 @@ export class GhostfolioService implements DataProviderInterface { `${this.URL}/v2/data-providers/ghostfolio/lookup?query=${query}`, { headers: await this.getRequestHeaders(), - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json())) as LookupResponse; } catch (error) { @@ -245,7 +273,7 @@ export class GhostfolioService implements DataProviderInterface { if (error.name === 'AbortError') { message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( - this.configurationService.get('REQUEST_TIMEOUT') / 1000 + requestTimeout / 1000 ).toFixed(3)} seconds`; } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { message = 'RequestError: The daily request limit has been exceeded'; diff --git a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts index f18d670d1..0c466972d 100644 --- a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts +++ b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -37,9 +38,7 @@ export class GoogleSheetsService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { return { symbol, dataSource: this.getName() diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index 5c316aac2..475205a01 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts @@ -15,9 +15,7 @@ export interface DataProviderInterface { getAssetProfile({ symbol - }: { - symbol: string; - }): Promise>; + }: GetAssetProfileParams): Promise>; getDataProviderInfo(): DataProviderInfo; @@ -55,6 +53,11 @@ export interface DataProviderInterface { search({ includeIndices, query }: GetSearchParams): Promise; } +export interface GetAssetProfileParams { + requestTimeout?: number; + symbol: string; +} + export interface GetDividendsParams { from: Date; granularity?: Granularity; @@ -79,5 +82,6 @@ export interface GetQuotesParams { export interface GetSearchParams { includeIndices?: boolean; query: string; + requestTimeout?: number; userId?: string; } diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 63d7dbfb4..f12966991 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -44,9 +45,7 @@ export class ManualService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { const assetProfile: Partial = { symbol, dataSource: this.getName() diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index 4c9bb2717..7762be426 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -1,6 +1,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -33,9 +34,7 @@ export class RapidApiService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { return { symbol, dataSource: this.getName() diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 27da18ab0..72ae1ff97 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -2,6 +2,7 @@ import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/c import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; import { DataProviderInterface, + GetAssetProfileParams, GetDividendsParams, GetHistoricalParams, GetQuotesParams, @@ -43,9 +44,7 @@ export class YahooFinanceService implements DataProviderInterface { public async getAssetProfile({ symbol - }: { - symbol: string; - }): Promise> { + }: GetAssetProfileParams): Promise> { return this.yahooFinanceDataEnhancerService.getAssetProfile(symbol); } diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index 45b429d48..fa189c462 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -220,6 +220,7 @@ export class DataGatheringService { assetSubClass, countries, currency, + cusip, dataSource, figi, figiComposite, @@ -238,6 +239,7 @@ export class DataGatheringService { assetSubClass, countries, currency, + cusip, dataSource, figi, figiComposite, @@ -254,6 +256,7 @@ export class DataGatheringService { assetSubClass, countries, currency, + cusip, figi, figiComposite, figiShareClass, diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 62fac27d4..1ae726508 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -217,8 +217,7 @@ export class SymbolProfileService { ?.length > 0 ) { item.countries = this.getCountries( - item.SymbolProfileOverrides - ?.countries as unknown as Prisma.JsonArray + item.SymbolProfileOverrides.countries as unknown as Prisma.JsonArray ); } @@ -227,22 +226,22 @@ export class SymbolProfileService { ?.length > 0 ) { item.holdings = this.getHoldings( - item.SymbolProfileOverrides?.holdings as unknown as Prisma.JsonArray + item.SymbolProfileOverrides.holdings as unknown as Prisma.JsonArray ); } - item.name = item.SymbolProfileOverrides?.name ?? item.name; + item.name = item.SymbolProfileOverrides.name ?? item.name; if ( (item.SymbolProfileOverrides.sectors as unknown as Sector[])?.length > 0 ) { item.sectors = this.getSectors( - item.SymbolProfileOverrides?.sectors as unknown as Prisma.JsonArray + item.SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray ); } - item.url = item.SymbolProfileOverrides?.url ?? item.url; + item.url = item.SymbolProfileOverrides.url ?? item.url; delete item.SymbolProfileOverrides; } diff --git a/apps/api/src/services/tag/tag.service.ts b/apps/api/src/services/tag/tag.service.ts index 341ebe8fb..e410f9260 100644 --- a/apps/api/src/services/tag/tag.service.ts +++ b/apps/api/src/services/tag/tag.service.ts @@ -1,11 +1,52 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { Injectable } from '@nestjs/common'; +import { Prisma, Tag } from '@prisma/client'; @Injectable() export class TagService { public constructor(private readonly prismaService: PrismaService) {} + public async createTag(data: Prisma.TagCreateInput) { + return this.prismaService.tag.create({ + data + }); + } + + public async deleteTag(where: Prisma.TagWhereUniqueInput): Promise { + return this.prismaService.tag.delete({ where }); + } + + public async getTag( + tagWhereUniqueInput: Prisma.TagWhereUniqueInput + ): Promise { + return this.prismaService.tag.findUnique({ + where: tagWhereUniqueInput + }); + } + + public async getTags({ + cursor, + orderBy, + skip, + take, + where + }: { + cursor?: Prisma.TagWhereUniqueInput; + orderBy?: Prisma.TagOrderByWithRelationInput; + skip?: number; + take?: number; + where?: Prisma.TagWhereInput; + } = {}) { + return this.prismaService.tag.findMany({ + cursor, + orderBy, + skip, + take, + where + }); + } + public async getTagsForUser(userId: string) { const tags = await this.prismaService.tag.findMany({ include: { @@ -42,4 +83,36 @@ export class TagService { isUsed: true })); } + + public async getTagsWithActivityCount() { + const tagsWithOrderCount = await this.prismaService.tag.findMany({ + include: { + _count: { + select: { orders: true } + } + } + }); + + return tagsWithOrderCount.map(({ _count, id, name, userId }) => { + return { + id, + name, + userId, + activityCount: _count.orders + }; + }); + } + + public async updateTag({ + data, + where + }: { + data: Prisma.TagUpdateInput; + where: Prisma.TagWhereUniqueInput; + }): Promise { + return this.prismaService.tag.update({ + data, + where + }); + } } diff --git a/apps/api/src/services/twitter-bot/twitter-bot.module.ts b/apps/api/src/services/twitter-bot/twitter-bot.module.ts index 4a2b1589a..80d53169c 100644 --- a/apps/api/src/services/twitter-bot/twitter-bot.module.ts +++ b/apps/api/src/services/twitter-bot/twitter-bot.module.ts @@ -1,5 +1,5 @@ -import { BenchmarkModule } from '@ghostfolio/api/app/benchmark/benchmark.module'; import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; +import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { TwitterBotService } from '@ghostfolio/api/services/twitter-bot/twitter-bot.service'; diff --git a/apps/api/src/services/twitter-bot/twitter-bot.service.ts b/apps/api/src/services/twitter-bot/twitter-bot.service.ts index a32882aed..a17585c5b 100644 --- a/apps/api/src/services/twitter-bot/twitter-bot.service.ts +++ b/apps/api/src/services/twitter-bot/twitter-bot.service.ts @@ -1,5 +1,5 @@ -import { BenchmarkService } from '@ghostfolio/api/app/benchmark/benchmark.service'; import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ghostfolioFearAndGreedIndexDataSource, diff --git a/apps/client/localhost.cert b/apps/client/localhost.cert index 12f31dd27..882ddd2a4 100644 --- a/apps/client/localhost.cert +++ b/apps/client/localhost.cert @@ -1,18 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIC5TCCAc2gAwIBAgIJAJAMHOFnJ6oyMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAeFw0yNDAyMjcxNTQ2MzFaFw0yNDAzMjgxNTQ2MzFaMBQx -EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAJ0hRjViikEKVIyukXR4CfuYVvFEFzB6AwAQ9Jrz2MseqpLacLTXFFAS54mp -iDuqPBzs9ta40mlSrqSBuAWKikpW5kTNnmqUnDZ6iSJezbYWx9YyULGqqb1q3C4/ -5pH9m6NHJ+2uaUNKlDxYNKbntqs3drQEdxH9yv672Z53nvogTcf9jz6zjutEQGSV -HgVkCTTQmzf3+3st+VJ5D8JeYFR+tpZ6yleqgXFaTMtPZRfKLvTkQ+KeyCJLnsUJ -BQvdCKI0PGsG6d6ygXFmSePolD9KW3VTKKDPCsndID89vAnRWDj9UhzvycxmKpcF -GrUPH5+Pis1PM1R7OnAvnFygnyECAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo -b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B -AQsFAAOCAQEAOdzrY3RTAPnBVAd3/GKIEkielYyOgKPnvd+RcOB+je8cXZY+vaxX -uEFP0526G00+kRZ2Tx9t0SmjoSpwg3lHUPMko0dIxgRlqISDAohdrEptHpcVujsD -ak88LLnAurr60JNjWX2wbEoJ18KLtqGSnATdmCgKwDPIN5a7+ssp44BGyzH6VYCg -wV3VjJl0pp4C5M0Jfu0p1FrQjzIVhgqR7JFYmvqIogWrGwYAQK/3XRXq8t48J5X3 -IsfWiLAA2ZdCoWAnZ6PAGBOoGivtkJm967pHjd/28qYY6mQo4sN2ksEOjx6/YslF -2mOJdLV/DzqoggUsTpPEG0dRhzQLTGHKDQ== +MIIDSDCCAjACCQCQ2ForVhz+uDANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJD +SDEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkxFTATBgNVBAoMDE9yZ2Fu +aXphdGlvbjENMAsGA1UECwwEVW5pdDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1 +MDMwOTE2MzQxM1oXDTI2MDMwOTE2MzQxM1owZjELMAkGA1UEBhMCQ0gxDjAMBgNV +BAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24x +DTALBgNVBAsMBFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMkJRKPgV8NDcoIakPc7sZVXQ9VK2PGb8+lF/1Lv +NcIZpD40+p4DzuEw0bjRn17IDClyLMaLbZNtIyTPSkFaffL+rJ0JvnKdG50s+HId +YNuCwKkgHg4hTXFzOPpT3HMG3UxyEwFOm25GMFiikfT96ukMAAkanMqYKZQOClRU +Cw4LP3g0Oks58obbRy4Wltp88K8LJrR+j81+AjElTIGXHhChXzV/NjJ14TMNy5hZ +lwV4xUSwvNqOvWGMIR7J77fINF130ghTSnvzCS52dCeom2I4Lvncz3m37lDttCOs +Wm/i651ro7pwFEs/lJmrnFHPtph2ayPcHBmrQCgLc5xMUMcCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAhRA1/+Gl2VH34yN/FvrE5cY0W4ghSCuTdK9pGeo8AcN+TScU +7O+hVsEwZDrYKuDvG8Ab//A+uv5gbfGbYPJVIdJ3Q8HKijNZmbwAgANJU/c0WwOx +XBQ9mCzWRcJxQeUUgh4DT4lZCOfR5pIvAJpKScTcF/yp5gOgrgJH1GHFEYYPoXWO +ezPPMwCNbfamUPlZZnHu74fUrFrDPI9c/YSu8Ex/LegZXJAEzA+8I0g64rjGtzJp +fkRDyQcBuT5SVa+USBlALQmdIuT/fN6R729DcGzvV8JqdoG9sLra4hrRCn3+A3c9 +izZguW1BQNQ2N7II6QCDnWkdUFSQCiQunX/xsg== -----END CERTIFICATE----- diff --git a/apps/client/localhost.pem b/apps/client/localhost.pem index dc2a7ff5a..05a480779 100644 --- a/apps/client/localhost.pem +++ b/apps/client/localhost.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdIUY1YopBClSM -rpF0eAn7mFbxRBcwegMAEPSa89jLHqqS2nC01xRQEueJqYg7qjwc7PbWuNJpUq6k -gbgFiopKVuZEzZ5qlJw2eokiXs22FsfWMlCxqqm9atwuP+aR/ZujRyftrmlDSpQ8 -WDSm57arN3a0BHcR/cr+u9med576IE3H/Y8+s47rREBklR4FZAk00Js39/t7LflS -eQ/CXmBUfraWespXqoFxWkzLT2UXyi705EPinsgiS57FCQUL3QiiNDxrBunesoFx -Zknj6JQ/Slt1UyigzwrJ3SA/PbwJ0Vg4/VIc78nMZiqXBRq1Dx+fj4rNTzNUezpw -L5xcoJ8hAgMBAAECggEAPU5YOEgELTA8oM8TjV+wdWuQsH2ilpVkSkhTR4nQkh+a -6cU0qDoqgLt/fySYNL9MyPRjso9V+SX7YdAC3paZMjwJh9q57lehQ1g33SMkG+Fz -gs0K0ucFZxQkaB8idN+ANAp1N7UO+ORGRe0cTeqmSNNRCxea5XgiFZVxaPS/IFOR -vVdXS1DulgwHh4H0ljKmkj7jc9zPBSc9ccW5Ml2q4a26Atu4IC/Mlp/DF4GNELbD -ebi9ZOZG33ip2bdhj3WX7NW9GJaaViKtVUpcpR6u+6BfvTXQ6xrqdoxXk9fnPzzf -sSoLPTt8yO4RavP1zQU22PhgIcWudhCiy/2Nv+uLqQKBgQDMPh1/xwdFl8yES8dy -f0xJyCDWPiB+xzGwcOAC90FrNZaqG0WWs3VHE4cilaWjCflvdR6aAEDEY68sZJhl -h+ChT/g61QLMOI+VIDQ1kJXKYgfS/B+BE1PZ0EkuymKFOvbNO8agHodB8CqnZaQh -bLuZaDnZ0JLK4im3KPt70+aKYwKBgQDE8s6xl0SLu+yJLo3VklCRD67Z8/jlvx2t -h3DF6NG8pB3QmvKdJWFKuLAWLGflJwbUW9Pt3hXkc0649KQrtiIYC0ZMh9KMaVCk -WmjS/n2eIUQZ7ZUlwFesi4p4iGynVBavIY8TJ6Y+K3TvsJgXP3IZ96r689PQJo8E -KbSeyYzFqwKBgGQTS4EAlJ+U8bEhMGj51veQCAbyChoUoFRD+n95h6RwbZKMKlzd -MenRt7VKfg6VJJNoX8Y1uYaBEaQ+5i1Zlsdz1718AhLu4+u+C9bzMXIo9ox63TTx -s3RWioVSxVNiwOtvDrQGQWAdvcioFPQLwyA34aDIgiTHDImimxbhjWThAoGAWOqW -Tq9QjxWk0Lpn5ohMP3GpK1VuhasnJvUDARb/uf8ORuPtrOz3Y9jGBvy9W0OnXbCn -mbiugZldbTtl8yYjdl+AuYSIlkPl2I3IzZl/9Shnqp0MvSJ9crT9KzXMeC8Knr6z -7Z30/BR6ksxTngtS5E5grzPt6Qe/gc2ich3kpEkCgYBfBHUhVIKVrDW/8a7U2679 -Wj4i9us/M3iPMxcTv7/ZAg08TEvNBQYtvVQLu0NfDKXx8iGKJJ6evIYgNXVm2sPq -VzSgwGCALi1X0443amZU+mnX+/9RnBqyM+PJU8570mM83jqKlY/BEsi0aqxTioFG -an3xbjjN+Rq9iKLzmPxIMg== +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJCUSj4FfDQ3KC +GpD3O7GVV0PVStjxm/PpRf9S7zXCGaQ+NPqeA87hMNG40Z9eyAwpcizGi22TbSMk +z0pBWn3y/qydCb5ynRudLPhyHWDbgsCpIB4OIU1xczj6U9xzBt1MchMBTptuRjBY +opH0/erpDAAJGpzKmCmUDgpUVAsOCz94NDpLOfKG20cuFpbafPCvCya0fo/NfgIx +JUyBlx4QoV81fzYydeEzDcuYWZcFeMVEsLzajr1hjCEeye+3yDRdd9IIU0p78wku +dnQnqJtiOC753M95t+5Q7bQjrFpv4uuda6O6cBRLP5SZq5xRz7aYdmsj3BwZq0Ao +C3OcTFDHAgMBAAECggEBALJQqDN7QB0QbDb+fWrt5bwDJUXBF+BmZdiZn7jeOJ6r +w8TxlQIneo6/kKYQOP4HDtKMVS7eaRkFCtERlFmXfHPWdSDtjaF3vRCS3OPLLyhF +N8JLnJ0H6PsiKn3PeJAGnK+71yOnp7IOS7+yoyfdOUnwvO9WTZBdmzOZqIvX595R +g7R5yjSYjzFMmaCpyab6kiD7b4bHzDIrB0XuouT2W/fS/i1srwc3eDk78ZyioyiB +g6GDuOwqDfPmkUqKo2oXSoSR8yCwSSdlClc7aOowoNxbsksTDjXf1k02n0lG5MHU +ldCX02WdA0JFW8Os0Ig+YBq7wSkB5oNVt7gEek/MB0ECgYEA+ATkyfX9/5X2kUMY +MatUqKOvLUtyIfHeYUy/Dm+9JlZlrxD0dRAKWhnhRR16v5cwN2RVtEvMDsV1AGHN +e/fh315aAq+I6/eY6syXfkeHHs5UIRPrOlIcp1Ogfg99xpOT0/TZy9bB7lKvtYes +GmKO8n7md1TptdxilSNORI60KvECgYEAz4FX6vH76HgV/seY+vePrj5nFCZnDru7 +16w5LYoHaX0hABJ0qZCqZozdPf9mqM5Ldc8PUbvVsFqXyaHwBAKUsH44a3aLxcXU +JMsQanU5I87SWP/S8Xu2Yxc10L66Oc5VdAeraZvb+wJqTkYKhDYOJVMjyuI/vkAw +fqMPI6wShzcCgYEAndYnb6uf4Eakap9jR0C8mLHKaq3nzVhqaEt6DwrnOf2jqnzE +xbbWj66GoQB4vHLP2YB91kaibwgURJD5PxpqYUdfSvRA08J3S322L0P/5ofyHDbb +7PqSh539thvPtE74tdvNux5Jvoxai9Dyorv0Mri1nF2qefTtu/GC/rg+SlECgYB2 +FaYhhomTVls1/QIat6zlPI/OULhPExineFOljaoAJvwTnW0UXcYKy9jPgjs6jwM0 +TJvsKFdHn5ZHYUdEEO/qrDmRNgn+h0Ddm02BN6pHrVfY2+SAFaXKKBgw7YjugnPw +rrimRdLeuhYi6wrrCBPuu6xftXcO3lp6hnKEG1UD6wKBgEh/C7HQ6cjb7Rr15eRq +2VOgeuz7o2v/OC+jO6yFGRrs2VKoBuJpw/6jx806Cbi2jLEwim21iNYW/2McOWP3 +YUvni7qHXfll8d4sSAuCTA4K/N0MJ/3XbGBPDm/83J2o7uz2GFkQRruruaERvDMF +x26H2i3DOUFzdgbkoNB0ifHd -----END PRIVATE KEY----- diff --git a/apps/client/src/app/app.component.html b/apps/client/src/app/app.component.html index c6734d1b4..ab188dfcb 100644 --- a/apps/client/src/app/app.component.html +++ b/apps/client/src/app/app.component.html @@ -129,6 +129,15 @@ >GitHub +
  • + LinkedIn +
  • { diff --git a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts index fa5e33f10..b0f69fa5c 100644 --- a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts @@ -15,9 +15,11 @@ import { FormControl, FormGroup, ValidationErrors, + ValidatorFn, Validators } from '@angular/forms'; import { MatDialogRef } from '@angular/material/dialog'; +import { isISO4217CurrencyCode } from 'class-validator'; import { uniq } from 'lodash'; import { Subject, takeUntil } from 'rxjs'; @@ -52,9 +54,7 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy { this.createAssetProfileForm = this.formBuilder.group( { addCurrency: new FormControl(null, [ - Validators.maxLength(3), - Validators.minLength(3), - Validators.required + this.iso4217CurrencyCodeValidator() ]), addSymbol: new FormControl(null, [Validators.required]), searchSymbol: new FormControl(null, [Validators.required]) @@ -83,11 +83,11 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy { symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol }); } else if (this.mode === 'currency') { - const currency = this.createAssetProfileForm - .get('addCurrency') - .value.toUpperCase(); + const currency = ( + this.createAssetProfileForm.get('addCurrency').value as string + ).toUpperCase(); - const currencies = uniq([...this.customCurrencies, currency]); + const currencies = uniq([...this.customCurrencies, currency]).sort(); this.dataService .putAdminSetting(PROPERTY_CURRENCIES, { @@ -109,10 +109,7 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy { const addCurrencyFormControl = this.createAssetProfileForm.get('addCurrency'); - if ( - addCurrencyFormControl.hasError('maxlength') || - addCurrencyFormControl.hasError('minlength') - ) { + if (addCurrencyFormControl.hasError('invalidCurrency')) { return true; } @@ -161,4 +158,14 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy { this.changeDetectorRef.markForCheck(); }); } + + private iso4217CurrencyCodeValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (!isISO4217CurrencyCode(control.value?.toUpperCase())) { + return { invalidCurrency: true }; + } + + return null; + }; + } } diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index d217f871d..f54af4174 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -28,7 +28,6 @@ import { formatDistanceToNowStrict, parseISO } from 'date-fns'; -import { uniq } from 'lodash'; import { StringValue } from 'ms'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -122,24 +121,6 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { this.putAdminSetting({ key: PROPERTY_COUPONS, value: coupons }); } - public onAddCurrency() { - const currency = prompt($localize`Please add a currency:`); - - if (currency) { - if (currency.length === 3) { - const currencies = uniq([ - ...this.customCurrencies, - currency.toUpperCase() - ]); - this.putAdminSetting({ key: PROPERTY_CURRENCIES, value: currencies }); - } else { - this.notificationService.alert({ - title: $localize`${currency} is an invalid currency!` - }); - } - } - } - public onChangeCouponDuration(aCouponDuration: StringValue) { this.couponDuration = aCouponDuration; } diff --git a/apps/client/src/app/components/admin-overview/admin-overview.html b/apps/client/src/app/components/admin-overview/admin-overview.html index ba8545d16..a85c32d43 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.html +++ b/apps/client/src/app/components/admin-overview/admin-overview.html @@ -95,16 +95,6 @@ } -
    - -
    diff --git a/apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts b/apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts index 8c2907064..e42e6259e 100644 --- a/apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts +++ b/apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts @@ -3,7 +3,6 @@ import { DataService } from '@ghostfolio/client/services/data.service'; import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; -import { CommonModule } from '@angular/common'; import { Component, Inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { @@ -18,7 +17,6 @@ import { GhostfolioPremiumApiDialogParams } from './interfaces/interfaces'; @Component({ imports: [ - CommonModule, GfDialogFooterModule, GfDialogHeaderModule, GfPremiumIndicatorComponent, diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.ts b/apps/client/src/app/components/admin-tag/admin-tag.component.ts index d59a68f8b..780b01fc1 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.ts +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.ts @@ -1,8 +1,7 @@ -import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto'; -import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto'; +import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; +import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto'; import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; -import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; @@ -49,7 +48,6 @@ export class AdminTagComponent implements OnInit, OnDestroy { private unsubscribeSubject = new Subject(); public constructor( - private adminService: AdminService, private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, private deviceService: DeviceDetectorService, @@ -106,7 +104,7 @@ export class AdminTagComponent implements OnInit, OnDestroy { } private deleteTag(aId: string) { - this.adminService + this.dataService .deleteTag(aId) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe({ @@ -122,7 +120,7 @@ export class AdminTagComponent implements OnInit, OnDestroy { } private fetchTags() { - this.adminService + this.dataService .fetchTags() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((tags) => { @@ -154,7 +152,7 @@ export class AdminTagComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((tag: CreateTagDto | null) => { if (tag) { - this.adminService + this.dataService .postTag(tag) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe({ @@ -190,7 +188,7 @@ export class AdminTagComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((tag: UpdateTagDto | null) => { if (tag) { - this.adminService + this.dataService .putTag(tag) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe({ diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts index 9b7194cc6..1bbda8e13 100644 --- a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts @@ -1,5 +1,5 @@ -import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto'; -import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto'; +import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; +import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto'; import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; import { diff --git a/apps/client/src/app/components/asset-profile-icon/asset-profile-icon.component.ts b/apps/client/src/app/components/asset-profile-icon/asset-profile-icon.component.ts index 5db862969..179193da0 100644 --- a/apps/client/src/app/components/asset-profile-icon/asset-profile-icon.component.ts +++ b/apps/client/src/app/components/asset-profile-icon/asset-profile-icon.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, @@ -10,7 +9,6 @@ import { DataSource } from '@prisma/client'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-asset-profile-icon', styleUrls: ['./asset-profile-icon.component.scss'], diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index e39be083b..dc0d3fe4f 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -13,8 +13,10 @@ import { LineChartItem, User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits'; +import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor'; import { translate } from '@ghostfolio/ui/i18n'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart'; @@ -44,11 +46,11 @@ import { SortDirection } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { Router } from '@angular/router'; -import { Account, Tag } from '@prisma/client'; +import { Account, MarketData, Tag } from '@prisma/client'; import { format, isSameMonth, isToday, parseISO } from 'date-fns'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { switchMap, takeUntil } from 'rxjs/operators'; import { HoldingDetailDialogParams } from './interfaces/interfaces'; @@ -62,6 +64,7 @@ import { HoldingDetailDialogParams } from './interfaces/interfaces'; GfDataProviderCreditsComponent, GfDialogFooterModule, GfDialogHeaderModule, + GfHistoricalMarketDataEditorComponent, GfLineChartComponent, GfPortfolioProportionChartComponent, GfTagsSelectorComponent, @@ -96,9 +99,12 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public dividendYieldPercentWithCurrencyEffect: number; public feeInBaseCurrency: number; public firstBuyDate: string; + public hasPermissionToCreateOwnTag: boolean; + public hasPermissionToReadMarketDataOfOwnAssetProfile: boolean; public historicalDataItems: LineChartItem[]; public investment: number; public investmentPrecision = 2; + public marketDataItems: MarketData[] = []; public marketPrice: number; public maxPrice: number; public minPrice: number; @@ -150,15 +156,43 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.activityForm .get('tags') .valueChanges.pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((tags) => { - this.dataService - .putHoldingTags({ - tags, - dataSource: this.data.dataSource, - symbol: this.data.symbol - }) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(); + .subscribe((tags: Tag[]) => { + const newTag = tags.find(({ id }) => { + return id === undefined; + }); + + if (newTag && this.hasPermissionToCreateOwnTag) { + this.dataService + .postTag({ ...newTag, userId: this.user.id }) + .pipe( + switchMap((createdTag) => { + return this.dataService.putHoldingTags({ + dataSource: this.data.dataSource, + symbol: this.data.symbol, + tags: [ + ...tags.filter(({ id }) => { + return id !== undefined; + }), + createdTag + ] + }); + }), + switchMap(() => { + return this.userService.get(true); + }), + takeUntil(this.unsubscribeSubject) + ) + .subscribe(); + } else { + this.dataService + .putHoldingTags({ + tags, + dataSource: this.data.dataSource, + symbol: this.data.symbol + }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + } }); this.dataService @@ -235,6 +269,14 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.feeInBaseCurrency = feeInBaseCurrency; this.firstBuyDate = firstBuyDate; + this.hasPermissionToReadMarketDataOfOwnAssetProfile = + hasPermission( + this.user?.permissions, + permissions.readMarketDataOfOwnAssetProfile + ) && + SymbolProfile?.dataSource === 'MANUAL' && + SymbolProfile?.userId === this.user?.id; + this.historicalDataItems = historicalData.map( ({ averagePrice, date, marketPrice }) => { this.benchmarkDataItems.push({ @@ -398,6 +440,10 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { } ); + if (this.hasPermissionToReadMarketDataOfOwnAssetProfile) { + this.fetchMarketData(); + } + if (Number.isInteger(this.quantity)) { this.quantityPrecision = 0; } else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') { @@ -421,6 +467,11 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; + this.hasPermissionToCreateOwnTag = hasPermission( + this.user.permissions, + permissions.createOwnTag + ); + this.tagsAvailable = this.user?.tags?.map((tag) => { return { @@ -466,6 +517,12 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { }); } + public onMarketDataChanged(withRefresh = false) { + if (withRefresh) { + this.fetchMarketData(); + } + } + public onTagsChanged(tags: Tag[]) { this.activityForm.get('tags').setValue(tags); } @@ -482,4 +539,27 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + private fetchMarketData() { + this.dataService + .fetchMarketDataBySymbol({ + dataSource: this.data.dataSource, + symbol: this.data.symbol + }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ marketData }) => { + this.marketDataItems = marketData; + + this.historicalDataItems = this.marketDataItems.map( + ({ date, marketPrice }) => { + return { + date: format(date, DATE_FORMAT), + value: marketPrice + }; + } + ); + + this.changeDetectorRef.markForCheck(); + }); + } } diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index 871c9f635..0e0b8584e 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -368,9 +368,33 @@ [showValueInBaseCurrency]="false" /> + @if ( + hasPermissionToReadMarketDataOfOwnAssetProfile && + user?.settings?.isExperimentalFeatures + ) { + + + +
    Market Data
    +
    + +
    + } - @if (hasPermissionToCreateOrder && historicalDataItems?.length === 0) { + @if (hasPermissionToCreateOrder && user?.activitiesCount === 0) {

    Welcome to Ghostfolio

    diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts index 7ee9c66cf..b57bcb0fd 100644 --- a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts +++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts @@ -1,6 +1,5 @@ import { XRayRulesSettings } from '@ghostfolio/common/interfaces'; -import { CommonModule } from '@angular/common'; import { Component, Inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -14,13 +13,7 @@ import { MatSliderModule } from '@angular/material/slider'; import { IRuleSettingsDialogParams } from './interfaces/interfaces'; @Component({ - imports: [ - CommonModule, - FormsModule, - MatButtonModule, - MatDialogModule, - MatSliderModule - ], + imports: [FormsModule, MatButtonModule, MatDialogModule, MatSliderModule], selector: 'gf-rule-settings-dialog', styleUrls: ['./rule-settings-dialog.scss'], templateUrl: './rule-settings-dialog.html' diff --git a/apps/client/src/app/components/rule/rule.component.scss b/apps/client/src/app/components/rule/rule.component.scss index 54ddce823..adb081abc 100644 --- a/apps/client/src/app/components/rule/rule.component.scss +++ b/apps/client/src/app/components/rule/rule.component.scss @@ -2,7 +2,7 @@ display: block; .icon-container { - background-color: rgba(var(--dark-primary-text), 0.05); + background-color: rgba(var(--palette-foreground-base), 0.02); border-radius: 0.25rem; height: 2rem; diff --git a/apps/client/src/app/core/http-response.interceptor.ts b/apps/client/src/app/core/http-response.interceptor.ts index 018e441fc..62c3540f7 100644 --- a/apps/client/src/app/core/http-response.interceptor.ts +++ b/apps/client/src/app/core/http-response.interceptor.ts @@ -108,10 +108,12 @@ export class HttpResponseInterceptor implements HttpInterceptor { }); } } else if (error.status === StatusCodes.UNAUTHORIZED) { - if (this.webAuthnService.isEnabled()) { - this.router.navigate(['/webauthn']); - } else if (!error.url.includes('/data-providers/ghostfolio/status')) { - this.tokenStorageService.signOut(); + if (!error.url.includes('/data-providers/ghostfolio/status')) { + if (this.webAuthnService.isEnabled()) { + this.router.navigate(['/webauthn']); + } else { + this.tokenStorageService.signOut(); + } } } diff --git a/apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts b/apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts index 8aefe84cf..98b6043eb 100644 --- a/apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts +++ b/apps/client/src/app/core/notification/alert-dialog/alert-dialog.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; @@ -6,7 +5,7 @@ import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { IAlertDialogParams } from './interfaces/interfaces'; @Component({ - imports: [CommonModule, MatButtonModule, MatDialogModule], + imports: [MatButtonModule, MatDialogModule], selector: 'gf-alert-dialog', styleUrls: ['./alert-dialog.scss'], templateUrl: './alert-dialog.html' diff --git a/apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts b/apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts index 9aced99cc..88e5113d7 100644 --- a/apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts +++ b/apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { Component, HostListener } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; @@ -7,7 +6,7 @@ import { ConfirmationDialogType } from './confirmation-dialog.type'; import { IConfirmDialogParams } from './interfaces/interfaces'; @Component({ - imports: [CommonModule, MatButtonModule, MatDialogModule], + imports: [MatButtonModule, MatDialogModule], selector: 'gf-confirmation-dialog', styleUrls: ['./confirmation-dialog.scss'], templateUrl: './confirmation-dialog.html' diff --git a/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts index 4ec634da9..6c8af4197 100644 --- a/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts +++ b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -8,7 +7,6 @@ import { MatInputModule } from '@angular/material/input'; @Component({ imports: [ - CommonModule, FormsModule, MatButtonModule, MatDialogModule, diff --git a/apps/client/src/app/pages/about/overview/about-overview-page.html b/apps/client/src/app/pages/about/overview/about-overview-page.html index 4d8ba9aa0..3891f929d 100644 --- a/apps/client/src/app/pages/about/overview/about-overview-page.html +++ b/apps/client/src/app/pages/about/overview/about-overview-page.html @@ -73,6 +73,14 @@ >.

    + + + - + - +

    @if (hasPermissionForSubscription) { diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index 039bf8691..350650060 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -40,8 +40,8 @@ export class GfApiPageComponent implements OnInit { this.apiKey = prompt($localize`Please enter your Ghostfolio API key:`); this.dividends$ = this.fetchDividends({ symbol: 'KO' }); - this.historicalData$ = this.fetchHistoricalData({ symbol: 'AAPL.US' }); - this.quotes$ = this.fetchQuotes({ symbols: ['AAPL.US', 'VOO.US'] }); + this.historicalData$ = this.fetchHistoricalData({ symbol: 'AAPL' }); + this.quotes$ = this.fetchQuotes({ symbols: ['AAPL', 'VOO.US'] }); this.status$ = this.fetchStatus(); this.symbols$ = this.fetchSymbols({ query: 'apple' }); } diff --git a/apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html b/apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html index 7538678c3..3b2f6f605 100644 --- a/apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html +++ b/apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html @@ -56,6 +56,11 @@
  • Click on the + button
  • Switch to Add Currency
  • Insert e.g. EUR for Euro
  • +
  • Select Filter by Currencies
  • +
  • Find the entry USDEUR
  • +
  • + Click the menu item Gather Historical Data in the dialog +
  • diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index 91254e002..5f5f7cea9 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -125,7 +125,10 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { this.dataSource = new MatTableDataSource(activities); this.totalItems = count; - if (this.hasPermissionToCreateActivity && this.totalItems <= 0) { + if ( + this.hasPermissionToCreateActivity && + this.user?.activitiesCount === 0 + ) { this.router.navigate([], { queryParams: { createDialog: true } }); } @@ -160,6 +163,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + this.fetchActivities(); }); } @@ -169,6 +177,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { .deleteActivity(aId) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + this.fetchActivities(); }); } @@ -230,6 +243,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + this.fetchActivities(); }); } @@ -248,6 +266,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + this.fetchActivities(); }); } @@ -333,6 +356,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { if (transaction) { this.dataService.postOrder(transaction).subscribe({ next: () => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + this.fetchActivities(); } }); diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.html b/apps/client/src/app/pages/portfolio/activities/activities-page.html index c06f7dd75..80ad71b79 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.html +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.html @@ -6,6 +6,7 @@ [baseCurrency]="user?.settings?.baseCurrency" [dataSource]="dataSource" [deviceType]="deviceType" + [hasActivities]="user?.activitiesCount > 0" [hasPermissionToCreateActivity]="hasPermissionToCreateActivity" [hasPermissionToDeleteActivity]="hasPermissionToDeleteActivity" [hasPermissionToExportActivities]="!hasImpersonationId" diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index f253aecfa..6a25ebaeb 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -268,6 +268,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { this.platforms = {}; this.portfolioDetails = { accounts: {}, + createdAt: undefined, holdings: {}, platforms: {}, summary: undefined diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 4b9e3ccd6..729982ce5 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -12,7 +12,11 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { DateRange, GroupBy } from '@ghostfolio/common/types'; +import type { + AiPromptMode, + DateRange, + GroupBy +} from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { Clipboard } from '@angular/cdk/clipboard'; @@ -169,9 +173,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { this.fetchDividendsAndInvestments(); } - public onCopyPromptToClipboard() { + public onCopyPromptToClipboard(mode: AiPromptMode) { this.dataService - .fetchPrompt() + .fetchPrompt(mode) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ prompt }) => { this.clipboard.copy(prompt); @@ -349,6 +353,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { .fetchBenchmarkForUser({ dataSource, symbol, + filters: this.userService.getFilters(), range: this.user?.settings?.dateRange, startDate: this.firstOrderDate }) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 051334a7f..a1a68d201 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -16,7 +16,7 @@ + diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html index 6ec5722b7..24ca0f474 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html @@ -7,23 +7,58 @@ risks in your portfolio. Adjust the rules below and set custom thresholds to align with your personal investment strategy.

    -

    +

    @if (isLoading) { - +
    + + +
    } @else { - {{ statistics?.rulesFulfilledCount }} - out of - {{ statistics?.rulesActiveCount }} - rules align with your portfolio. +
    + @if ( + statistics?.rulesActiveCount === 0 || + statistics?.rulesFulfilledCount === 0 + ) { + + } @else if ( + statistics?.rulesFulfilledCount === statistics?.rulesActiveCount + ) { + + } @else { + + } +
    +
    + {{ statistics?.rulesFulfilledCount }} + out of + {{ statistics?.rulesActiveCount }} + rules align with your portfolio. +
    } -

    +
    Create Account diff --git a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts index 5aacbd457..c6535bf48 100644 --- a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts +++ b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts @@ -1,19 +1,58 @@ -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { DataService } from '@ghostfolio/client/services/data.service'; + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ViewChild +} from '@angular/core'; +import { MatStepper } from '@angular/material/stepper'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ - selector: 'gf-show-access-token-dialog', changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'gf-show-access-token-dialog', + standalone: false, styleUrls: ['./show-access-token-dialog.scss'], - templateUrl: 'show-access-token-dialog.html', - standalone: false + templateUrl: 'show-access-token-dialog.html' }) export class ShowAccessTokenDialog { - public isAgreeButtonDisabled = true; + @ViewChild(MatStepper) stepper!: MatStepper; + + public accessToken: string; + public authToken: string; + public isCreateAccountButtonDisabled = true; + public isDisclaimerChecked = false; + public role: string; + + private unsubscribeSubject = new Subject(); + + public constructor( + private changeDetectorRef: ChangeDetectorRef, + private dataService: DataService + ) {} - public constructor(@Inject(MAT_DIALOG_DATA) public data: any) {} + public createAccount() { + this.dataService + .postUser() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ accessToken, authToken, role }) => { + this.accessToken = accessToken; + this.authToken = authToken; + this.role = role; + + this.stepper.next(); + + this.changeDetectorRef.markForCheck(); + }); + } + + public enableCreateAccountButton() { + this.isCreateAccountButtonDisabled = false; + } - public enableAgreeButton() { - this.isAgreeButtonDisabled = false; + public onChangeDislaimerChecked() { + this.isDisclaimerChecked = !this.isDisclaimerChecked; } } diff --git a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html index 0a6a2e5e3..737d32679 100644 --- a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html +++ b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html @@ -1,48 +1,93 @@

    Create Account - @if (data.role === 'ADMIN') { - {{ data.role }} + @if (role === 'ADMIN') { + {{ role }} }

    -
    -
    - - Security Token - -
    - +
    +
    + + Continue + +
    - -
    -

    - I agree to have stored my Security Token from above in a secure - place. If I lose it, I cannot get my account back. -

    -
    -
    - - + + + Security Token +
    + Here is your security token. It is only visible once, please store + and keep it in a safe place. +
    + + Security Token + +
    + +
    +
    +
    +
    + +
    +
    +
    + + + + + +
    diff --git a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.module.ts b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.module.ts index d537fbe7f..117c1da00 100644 --- a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.module.ts +++ b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.module.ts @@ -4,9 +4,11 @@ import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { MatStepperModule } from '@angular/material/stepper'; import { ShowAccessTokenDialog } from './show-access-token-dialog.component'; @@ -17,9 +19,11 @@ import { ShowAccessTokenDialog } from './show-access-token-dialog.component'; CommonModule, FormsModule, MatButtonModule, + MatCheckboxModule, MatDialogModule, MatFormFieldModule, MatInputModule, + MatStepperModule, ReactiveFormsModule, TextFieldModule ], diff --git a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.scss b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.scss index dc9093b45..777c8c854 100644 --- a/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.scss +++ b/apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.scss @@ -1,2 +1,6 @@ :host { + .mat-mdc-dialog-actions { + padding-left: 0 !important; + padding-right: 0 !important; + } } diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts index 3a0ec4ffb..6a8543e71 100644 --- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts +++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -3,14 +3,13 @@ import { Product } from '@ghostfolio/common/interfaces'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { translate } from '@ghostfolio/ui/i18n'; -import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { ActivatedRoute, RouterModule } from '@angular/router'; @Component({ host: { class: 'page' }, - imports: [CommonModule, MatButtonModule, RouterModule], + imports: [MatButtonModule, RouterModule], selector: 'gf-product-page', styleUrls: ['./product-page.scss'], templateUrl: './product-page.html' diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 715ec0f78..153b16a59 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -1,8 +1,6 @@ import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto'; -import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto'; -import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto'; import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { HEADER_KEY_SKIP_INTERCEPTOR, @@ -25,7 +23,7 @@ import { import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { SortDirection } from '@angular/material/sort'; -import { DataSource, MarketData, Platform, Tag } from '@prisma/client'; +import { DataSource, MarketData, Platform } from '@prisma/client'; import { JobStatus } from 'bull'; import { format } from 'date-fns'; import { switchMap } from 'rxjs'; @@ -75,10 +73,6 @@ export class AdminService { ); } - public deleteTag(aId: string) { - return this.http.delete(`/api/v1/tag/${aId}`); - } - public executeJob(aId: string) { return this.http.get(`/api/v1/admin/queue/job/${aId}/execute`); } @@ -155,10 +149,6 @@ export class AdminService { return this.http.get('/api/v1/platform'); } - public fetchTags() { - return this.http.get('/api/v1/tag'); - } - public fetchUsers({ skip, take = DEFAULT_PAGE_SIZE @@ -285,10 +275,6 @@ export class AdminService { return this.http.post(`/api/v1/platform`, aPlatform); } - public postTag(aTag: CreateTagDto) { - return this.http.post(`/api/v1/tag`, aTag); - } - public putPlatform(aPlatform: UpdatePlatformDto) { return this.http.put( `/api/v1/platform/${aPlatform.id}`, @@ -296,10 +282,6 @@ export class AdminService { ); } - public putTag(aTag: UpdateTagDto) { - return this.http.put(`/api/v1/tag/${aTag.id}`, aTag); - } - public testMarketData({ dataSource, scraperConfiguration, diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index d72c02715..c35bdb16d 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -4,6 +4,8 @@ import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto'; import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto'; +import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; +import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Activities, @@ -44,7 +46,12 @@ import { User } from '@ghostfolio/common/interfaces'; import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; -import { AccountWithValue, DateRange, GroupBy } from '@ghostfolio/common/types'; +import type { + AccountWithValue, + AiPromptMode, + DateRange, + GroupBy +} from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { HttpClient, HttpParams } from '@angular/common/http'; @@ -301,13 +308,17 @@ export class DataService { } public deleteBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { - return this.http.delete(`/api/v1/benchmark/${dataSource}/${symbol}`); + return this.http.delete(`/api/v1/benchmarks/${dataSource}/${symbol}`); } public deleteOwnUser(aData: DeleteOwnUserDto) { return this.http.delete(`/api/v1/user`, { body: aData }); } + public deleteTag(aId: string) { + return this.http.delete(`/api/v1/tags/${aId}`); + } + public deleteUser(aId: string) { return this.http.delete(`/api/v1/user/${aId}`); } @@ -332,21 +343,27 @@ export class DataService { public fetchBenchmarkForUser({ dataSource, + filters, range, startDate, - symbol + symbol, + withExcludedAccounts }: { + filters?: Filter[]; range: DateRange; startDate: Date; + withExcludedAccounts?: boolean; } & AssetProfileIdentifier): Observable { - let params = new HttpParams(); + let params = this.buildFiltersAsQueryParams({ filters }); - if (range) { - params = params.append('range', range); + params = params.append('range', range); + + if (withExcludedAccounts) { + params = params.append('withExcludedAccounts', withExcludedAccounts); } return this.http.get( - `/api/v1/benchmark/${dataSource}/${symbol}/${format( + `/api/v1/benchmarks/${dataSource}/${symbol}/${format( startDate, DATE_FORMAT )}`, @@ -355,7 +372,7 @@ export class DataService { } public fetchBenchmarks() { - return this.http.get('/api/v1/benchmark'); + return this.http.get('/api/v1/benchmarks'); } public fetchExport({ @@ -649,8 +666,8 @@ export class DataService { return this.http.get('/api/v1/portfolio/report'); } - public fetchPrompt() { - return this.http.get('/api/v1/ai/prompt'); + public fetchPrompt(mode: AiPromptMode) { + return this.http.get(`/api/v1/ai/prompt/${mode}`); } public fetchPublicPortfolio(aAccessId: string) { @@ -673,6 +690,10 @@ export class DataService { ); } + public fetchTags() { + return this.http.get('/api/v1/tags'); + } + public loginAnonymous(accessToken: string) { return this.http.post('/api/v1/auth/anonymous', { accessToken @@ -699,7 +720,7 @@ export class DataService { } public postBenchmark(benchmark: AssetProfileIdentifier) { - return this.http.post('/api/v1/benchmark', benchmark); + return this.http.post('/api/v1/benchmarks', benchmark); } public postMarketData({ @@ -720,6 +741,10 @@ export class DataService { return this.http.post('/api/v1/order', aOrder); } + public postTag(aTag: CreateTagDto) { + return this.http.post(`/api/v1/tags`, aTag); + } + public postUser() { return this.http.post('/api/v1/user', {}); } @@ -747,6 +772,10 @@ export class DataService { return this.http.put(`/api/v1/order/${aOrder.id}`, aOrder); } + public putTag(aTag: UpdateTagDto) { + return this.http.put(`/api/v1/tags/${aTag.id}`, aTag); + } + public putUserSetting(aData: UpdateUserSettingDto) { return this.http.put('/api/v1/user/setting', aData); } diff --git a/apps/client/src/app/services/web-authn.service.ts b/apps/client/src/app/services/web-authn.service.ts index c5e186362..76352cb7b 100644 --- a/apps/client/src/app/services/web-authn.service.ts +++ b/apps/client/src/app/services/web-authn.service.ts @@ -45,7 +45,7 @@ export class WebAuthnService { return of(null); }), switchMap((attOps) => { - return startRegistration(attOps); + return startRegistration({ optionsJSON: attOps }); }), switchMap((credential) => { return this.http.post( @@ -89,8 +89,8 @@ export class WebAuthnService { { deviceId } ) .pipe( - switchMap((requestOptionsJSON) => { - return startAuthentication(requestOptionsJSON); + switchMap((optionsJSON) => { + return startAuthentication({ optionsJSON }); }), switchMap((credential) => { return this.http.post<{ authToken: string }>( diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 82f3f96bb..24df1cef4 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -250,7 +250,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -358,7 +358,7 @@ El risc d’assumir pèrdues en les inversions és substancial. No és recomanable invertir diners que pugui necessitar a curt termini. apps/client/src/app/app.component.html - 205 + 214 @@ -447,7 +447,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -583,7 +583,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -803,7 +803,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -1219,7 +1219,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1723,7 +1723,7 @@ Realment vol eliminar el perfil d’aquest actiu? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -1731,7 +1731,7 @@ Realment vol eliminar aquests perfils? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -1739,7 +1739,7 @@ Oooh! No s’han pogut eliminar els perfils apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -1787,7 +1787,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -1958,28 +1958,12 @@ 124 - - Please add a currency: - Si us plau, afegiu una divisa: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - - - is an invalid currency! - no és una divisa vàlida! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - Do you really want to delete this coupon? Està segur qeu vol eliminar aquest cupó? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -1987,7 +1971,7 @@ Està segur que vol eliminar aquesta divisa? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -1995,7 +1979,7 @@ Està segur que vol eliminar aquest missatge del sistema? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -2003,7 +1987,7 @@ Està segur que vol depurar el cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -2011,7 +1995,7 @@ Si us plau, afegeixi el seu missatge del sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -2061,17 +2045,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - User Signup Registrar Usuari apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -2079,7 +2059,7 @@ Mode Només Lecutra apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -2087,7 +2067,7 @@ Recollida de Dades apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -2095,7 +2075,7 @@ Missatge del Sistema apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -2103,7 +2083,7 @@ Estableix el Missatge apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -2111,7 +2091,7 @@ Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -2119,7 +2099,7 @@ Afegir apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -2131,7 +2111,7 @@ Ordre apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -2139,7 +2119,7 @@ Depurar el Cache apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -2239,7 +2219,7 @@ Està segur que vol eliminar aquesta etiqueta? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -2355,7 +2335,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2623,7 +2603,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2639,7 +2619,7 @@ Informar d’un Problema amb les Dades apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2679,7 +2659,7 @@ Gestionar Activitats apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2691,7 +2671,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2703,7 +2683,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2995,7 +2975,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -5095,7 +5075,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -5127,7 +5107,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -5135,7 +5115,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -5143,7 +5123,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -5151,7 +5131,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -5159,7 +5139,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -5167,7 +5147,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -5175,7 +5155,7 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -5183,7 +5163,7 @@ Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -5191,7 +5171,7 @@ Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -5199,7 +5179,7 @@ Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -5207,7 +5187,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -5215,7 +5195,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -5223,7 +5203,7 @@ Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -5263,7 +5243,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5271,7 +5251,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -5611,11 +5591,11 @@ Switzerland apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5623,7 +5603,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -6043,7 +6023,7 @@ Do you really want to delete these activities? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -6051,7 +6031,7 @@ Do you really want to delete this activity? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -6411,7 +6391,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -6703,7 +6683,7 @@ Extreme Fear libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6711,7 +6691,7 @@ Extreme Greed libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6719,7 +6699,7 @@ Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 0040f8cb6..5f3f0b8e5 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -22,7 +22,7 @@ Das Ausfallrisiko beim Börsenhandel kann erheblich sein. Es ist nicht ratsam, Geld zu investieren, welches du kurzfristig benötigst. apps/client/src/app/app.component.html - 205 + 214 @@ -298,7 +298,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -617,20 +617,12 @@ 44 - - Please add a currency: - Bitte Währung hinzufügen: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Möchtest du diesen Gutscheincode wirklich löschen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -638,7 +630,7 @@ Möchtest du diese Währung wirklich löschen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -646,7 +638,7 @@ Möchtest du den Cache wirklich leeren? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -654,7 +646,7 @@ Bitte gebe deine Systemmeldung ein: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -716,17 +708,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - System Message Systemmeldung apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -734,7 +722,7 @@ Systemmeldung setzen apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -742,7 +730,7 @@ Lese-Modus apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -750,7 +738,7 @@ Gutscheincodes apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -758,7 +746,7 @@ Hinzufügen apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -770,7 +758,7 @@ Verwaltung apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -778,7 +766,7 @@ Cache leeren apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1062,7 +1050,7 @@ Aktivitäten verwalten apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1254,7 +1242,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1370,7 +1358,7 @@ Datenfehler melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2146,7 +2134,7 @@ Zeitstrahl der Investitionen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2154,7 +2142,7 @@ Gewinner apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2162,7 +2150,7 @@ Verlierer apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2410,7 +2398,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2578,7 +2566,7 @@ Möchtest du diese Aktivität wirklich löschen? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -2862,7 +2850,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2874,7 +2862,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2998,7 +2986,7 @@ Portfolio Wertentwicklung apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -3318,7 +3306,7 @@ Zeitstrahl der Dividenden apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3346,7 +3334,7 @@ Benutzer Registrierung apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -3370,7 +3358,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -3770,7 +3758,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3954,7 +3942,7 @@ Möchtest du diese Aktivitäten wirklich löschen? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4174,7 +4162,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4182,7 +4170,7 @@ Aktueller Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4190,7 +4178,7 @@ Längster Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4670,7 +4658,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5316,7 +5304,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5405,7 +5393,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5659,7 +5647,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5767,11 +5755,11 @@ Schweiz apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5779,7 +5767,7 @@ Weltweit apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5891,7 +5879,7 @@ Möchtest du diesen Tag wirklich löschen? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5915,7 +5903,7 @@ Währungsklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5923,7 +5911,7 @@ Kontoklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6043,7 +6031,7 @@ Möchtest du dieses Anlageprofil wirklich löschen? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6123,7 +6111,7 @@ Extreme Angst libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6131,7 +6119,7 @@ Extreme Gier libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6139,7 +6127,7 @@ Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6155,7 +6143,7 @@ Möchtest du diese Systemmeldung wirklich löschen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - ist eine ungültige Währung! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. Wenn eine Übersetzung fehlt, unterstütze uns bitte dabei, sie hier zu ergänzen. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Absolute Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Absolute Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Absolute Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Daten einholen apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Möchtest du diese Profile wirklich löschen? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Ups! Die Profile konnten nicht gelöscht werden. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budgetierung apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Persönliche Finanzen apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Datenschutz apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Vermögen apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Vermögensverwaltung apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australien libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Österreich libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgien libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgarien libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Kanada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Tschechien libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finnland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ Frankreich libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Deutschland libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ Indien libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italien libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Niederlande libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ Neuseeland libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Polen libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Rumänien libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ Südafrika libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ USA libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inaktiv apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Wirtschaftsraumklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ von apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ Regeln werden von Ihrem Portfolio erfüllt. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Anlageklasseklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Bitte gebe deinen Ghostfolio API-Schlüssel ein. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Kopiere KI-Anweisung die Zwischenablage - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link wurde in die Zwischenablage kopiert @@ -7673,7 +7645,7 @@ Regionale Marktklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Marktdaten + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Änderung + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Kopiere Portfolio-Daten in die Zwischenablage für KI-Anweisung + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Kopiere KI-Anweisung in die Zwischenablage für Analyse + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenien + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + Britische Jungferninseln + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapur + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index bc6a62411..15254d8f2 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -23,7 +23,7 @@ El riesgo de pérdida en trading puede ser sustancial. No es aconsejable invertir dinero que puedas necesitar a corto plazo. apps/client/src/app/app.component.html - 205 + 214 @@ -299,7 +299,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -618,20 +618,12 @@ 44 - - Please add a currency: - Por favor, añade una divisa: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? ¿Estás seguro de eliminar este cupón? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -639,7 +631,7 @@ ¿Estás seguro de eliminar esta divisa? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -647,7 +639,7 @@ ¿Estás seguro de limpiar la caché? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -655,7 +647,7 @@ Por favor, establece tu mensaje del sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -717,17 +709,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - System Message Mensaje del sistema apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -735,7 +723,7 @@ Establecer mensaje apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -743,7 +731,7 @@ Modo de solo lectura apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -751,7 +739,7 @@ Cupones apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -759,7 +747,7 @@ Añadir apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -771,7 +759,7 @@ Tareas domésticas apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -779,7 +767,7 @@ Limpiar caché apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1063,7 +1051,7 @@ Gestión de las operaciones apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1255,7 +1243,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1371,7 +1359,7 @@ Reporta un anomalía de los datos apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2147,7 +2135,7 @@ Cronología de la inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2155,7 +2143,7 @@ Lo mejor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2163,7 +2151,7 @@ Lo peor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2411,7 +2399,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2579,7 +2567,7 @@ ¿Estás seguro de eliminar esta operación? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -2863,7 +2851,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2875,7 +2863,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2999,7 +2987,7 @@ Evolución cartera apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -3331,7 +3319,7 @@ Calendario de dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3347,7 +3335,7 @@ Registro de usuario apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -3371,7 +3359,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -3771,7 +3759,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3955,7 +3943,7 @@ Do you really want to delete these activities? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4175,7 +4163,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4183,7 +4171,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4191,7 +4179,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4671,7 +4659,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5317,7 +5305,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5406,7 +5394,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5660,7 +5648,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5768,11 +5756,11 @@ Switzerland apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5780,7 +5768,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5892,7 +5880,7 @@ Do you really want to delete this tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5916,7 +5904,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5924,7 +5912,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6044,7 +6032,7 @@ Do you really want to delete this asset profile? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6124,7 +6112,7 @@ Extreme Fear libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6132,7 +6120,7 @@ Extreme Greed libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6140,7 +6128,7 @@ Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6156,7 +6144,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6215,14 +6203,6 @@ 109 - - is an invalid currency! - is an invalid currency! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. If a translation is missing, kindly support us in extending it here. @@ -6328,7 +6308,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6336,7 +6316,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6344,7 +6324,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6352,7 +6332,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6360,7 +6340,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6368,7 +6348,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6376,7 +6356,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6484,7 +6464,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6736,7 +6716,7 @@ Estas seguro de borrar estos perfiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6744,7 +6724,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6776,7 +6756,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6784,7 +6764,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6792,7 +6772,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6800,7 +6780,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6808,7 +6788,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6816,7 +6796,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6824,7 +6804,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6832,7 +6812,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6840,7 +6820,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6848,7 +6828,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6856,7 +6836,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6864,7 +6844,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6872,7 +6852,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6880,7 +6860,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6888,7 +6868,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6896,7 +6876,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6904,7 +6884,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6912,7 +6892,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6920,7 +6900,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6928,7 +6908,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6936,7 +6916,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6944,7 +6924,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6952,7 +6932,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6960,7 +6940,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6968,7 +6948,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6976,7 +6956,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6984,7 +6964,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6992,7 +6972,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -7000,7 +6980,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7008,7 +6988,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7016,7 +6996,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7024,7 +7004,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7056,7 +7036,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7368,7 +7348,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7458,7 +7438,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7586,7 +7566,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7594,7 +7574,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7610,7 +7590,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7634,7 +7614,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7645,14 +7625,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7674,7 +7646,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7757,6 +7729,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 353a2a3f6..e249579de 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -6,7 +6,7 @@ Le risque de perte en investissant peut être important. Il est déconseillé d’investir de l’argent dont vous pourriez avoir besoin à court terme. apps/client/src/app/app.component.html - 205 + 214 @@ -358,7 +358,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -877,20 +877,12 @@ 339 - - Please add a currency: - Veuillez ajouter une devise : - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Voulez-vous vraiment supprimer ce code promotionnel ? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -898,7 +890,7 @@ Voulez-vous vraiment supprimer cette devise ? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -906,7 +898,7 @@ Voulez-vous vraiment vider le cache ? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -914,7 +906,7 @@ Veuillez définir votre message système : apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -956,10 +948,6 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - Tags @@ -982,7 +970,7 @@ Inscription de Nouveaux Utilisateurs apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -990,7 +978,7 @@ Mode Lecture Seule apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -998,7 +986,7 @@ Message Système apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -1006,7 +994,7 @@ Définir Message apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -1014,7 +1002,7 @@ Codes promotionnels apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -1022,7 +1010,7 @@ Ajouter apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1034,7 +1022,7 @@ Maintenance apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -1042,7 +1030,7 @@ Vider le Cache apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1150,7 +1138,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -1378,7 +1366,7 @@ Gérer les Activités apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1390,7 +1378,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -1402,7 +1390,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -1598,7 +1586,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1718,7 +1706,7 @@ Signaler une Erreur de Données apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2598,7 +2586,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -2786,7 +2774,7 @@ Haut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2794,7 +2782,7 @@ Bas apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2802,7 +2790,7 @@ Évolution du Portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -2810,7 +2798,7 @@ Historique des Investissements apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2818,7 +2806,7 @@ Historique des Dividendes apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3098,7 +3086,7 @@ Voulez-vous vraiment supprimer cette activité ? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -3770,7 +3758,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3954,7 +3942,7 @@ Voulez-vous vraiment supprimer toutes vos activités ? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4174,7 +4162,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4182,7 +4170,7 @@ Série en cours apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4190,7 +4178,7 @@ Série la plus longue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4670,7 +4658,7 @@ Japon libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5316,7 +5304,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5405,7 +5393,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5659,7 +5647,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5767,11 +5755,11 @@ Suisse apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5779,7 +5767,7 @@ Mondial apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5891,7 +5879,7 @@ Confirmez la suppression de ce tag ? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5915,7 +5903,7 @@ Risques de change apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5923,7 +5911,7 @@ Risques liés aux regroupements de comptes apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6043,7 +6031,7 @@ Confirmez la suppressoion de ce profil d'actif? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6123,7 +6111,7 @@ Extreme Peur libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6131,7 +6119,7 @@ Extreme Cupidité libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6139,7 +6127,7 @@ Neutre libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6155,7 +6143,7 @@ Confirmer la suppresion de ce message système? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - est une devise non valide ! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. Si une traduction est manquante, veuillez nous aider à compléter la traduction here. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Performance des Actifs en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Performance des Actifs apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Performance des devises en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Performance des devises apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Performance nette absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Performance nette apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Collecter les données apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Confirmer la suppression de ces Profils? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Oops! Echec de la suppression de Profils. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budget apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Communauté apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investisseur apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Gestion de Patrimoine apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Confidentialité apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Logiciels apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Outils apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ Expérience Utilisateur apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Patrimoine apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Gestion de Patrimoine apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australie libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Autriche libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgique libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgarie libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ République Tchèque libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finlande libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Allemagne libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ Inde libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italie libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Pays-Bas libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ Nouvelle-Zélande libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Pologne libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Roumanie libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ Afrique du Sud libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thaïlande libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ Etats-Unis libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inactif apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index b31996ab7..3b500d852 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -23,7 +23,7 @@ Il rischio di perdita nel trading può essere notevole. Non è consigliabile investire denaro di cui potresti avere bisogno a breve termine. apps/client/src/app/app.component.html - 205 + 214 @@ -299,7 +299,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -618,20 +618,12 @@ 44 - - Please add a currency: - Aggiungi una valuta: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Vuoi davvero eliminare questo buono? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -639,7 +631,7 @@ Vuoi davvero eliminare questa valuta? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -647,7 +639,7 @@ Vuoi davvero svuotare la cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -655,7 +647,7 @@ Imposta il messaggio di sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -717,17 +709,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - System Message Messaggio di sistema apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -735,7 +723,7 @@ Imposta messaggio apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -743,7 +731,7 @@ Modalità di sola lettura apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -751,7 +739,7 @@ Buoni sconto apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -759,7 +747,7 @@ Aggiungi apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -771,7 +759,7 @@ Bilancio domestico apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -779,7 +767,7 @@ Svuota la cache apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1063,7 +1051,7 @@ Gestione delle attività apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1255,7 +1243,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1371,7 +1359,7 @@ Segnala un’anomalia dei dati apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2147,7 +2135,7 @@ Cronologia degli investimenti apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2155,7 +2143,7 @@ In alto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2163,7 +2151,7 @@ In basso apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2411,7 +2399,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2579,7 +2567,7 @@ Vuoi davvero eliminare questa attività? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -2863,7 +2851,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2875,7 +2863,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2999,7 +2987,7 @@ Evoluzione del portafoglio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -3331,7 +3319,7 @@ Cronologia dei dividendi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3347,7 +3335,7 @@ Registrazione utente apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -3371,7 +3359,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -3771,7 +3759,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3955,7 +3943,7 @@ Vuoi davvero eliminare tutte le tue attività? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4175,7 +4163,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4183,7 +4171,7 @@ Serie attuale apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4191,7 +4179,7 @@ Serie più lunga apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4671,7 +4659,7 @@ Giappone libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5317,7 +5305,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5406,7 +5394,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5660,7 +5648,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5768,11 +5756,11 @@ Svizzera apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5780,7 +5768,7 @@ Globale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5892,7 +5880,7 @@ Sei sicuro di voler eliminare questo tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5916,7 +5904,7 @@ Rischio di Concentrazione Valutario apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5924,7 +5912,7 @@ Rischi di Concentrazione dei Conti apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6044,7 +6032,7 @@ Vuoi veramente eliminare il profilo di questo asset? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6124,7 +6112,7 @@ Paura estrema libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6132,7 +6120,7 @@ Avidità estrema libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6140,7 +6128,7 @@ Neutrale libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6156,7 +6144,7 @@ Confermi di voler cancellare questo messaggio di sistema? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6215,14 +6203,6 @@ 109 - - is an invalid currency! - non è una valuta valida! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. Se manca una traduzione, puoi aiutarci modificando questo file: here. @@ -6328,7 +6308,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6336,7 +6316,7 @@ Rendimento assoluto dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6344,7 +6324,7 @@ Rendimento dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6352,7 +6332,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6360,7 +6340,7 @@ Rendimento della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6368,7 +6348,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6376,7 +6356,7 @@ Rendimento Netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6484,7 +6464,7 @@ Raccolta Dati apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6736,7 +6716,7 @@ Confermi di voler eliminare questi profili? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6744,7 +6724,7 @@ Ops! Impossibile eliminare i profili. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6776,7 +6756,7 @@ Alternativa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6784,7 +6764,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6792,7 +6772,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6800,7 +6780,7 @@ Comunità apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6808,7 +6788,7 @@ Ufficio familiare apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6816,7 +6796,7 @@ Investitore apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6824,7 +6804,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6832,7 +6812,7 @@ Finanza Personale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6840,7 +6820,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6848,7 +6828,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6856,7 +6836,7 @@ Strumento apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6864,7 +6844,7 @@ Esperienza Utente apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6872,7 +6852,7 @@ Ricchezza apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6880,7 +6860,7 @@ Gestione Patrimoniale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6888,7 +6868,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6896,7 +6876,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6904,7 +6884,7 @@ Belgio libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6912,7 +6892,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6920,7 +6900,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6928,7 +6908,7 @@ Repubblica Ceca libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6936,7 +6916,7 @@ Finlandia libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6944,7 +6924,7 @@ Francia libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6952,7 +6932,7 @@ Germania libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6960,7 +6940,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6968,7 +6948,7 @@ Italia libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6976,7 +6956,7 @@ Olanda libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6984,7 +6964,7 @@ Nuova Zelanda libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6992,7 +6972,7 @@ Polonia libs/ui/src/lib/i18n.ts - 87 + 89 @@ -7000,7 +6980,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7008,7 +6988,7 @@ Sud Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7016,7 +6996,7 @@ Tailandia libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7024,7 +7004,7 @@ Stati Uniti libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7056,7 +7036,7 @@ Inattivo apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7368,7 +7348,7 @@ Ucraina libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7458,7 +7438,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7586,7 +7566,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7594,7 +7574,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7610,7 +7590,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7634,7 +7614,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7645,14 +7625,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7674,7 +7646,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7757,6 +7729,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + \ No newline at end of file diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 53efeab95..d6678c99a 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -22,7 +22,7 @@ Het risico op verlies bij handelen kan aanzienlijk zijn. Het is niet aan te raden om geld te investeren dat je misschien op korte termijn nodig heeft. apps/client/src/app/app.component.html - 205 + 214 @@ -298,7 +298,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -617,20 +617,12 @@ 44 - - Please add a currency: - Voeg een valuta toe: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Wil je deze coupon echt verwijderen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -638,7 +630,7 @@ Wil je deze valuta echt verwijderen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -646,7 +638,7 @@ Wil je echt de cache legen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -654,7 +646,7 @@ Stel je systeemboodschap in: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -716,17 +708,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - System Message Systeembericht apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -734,7 +722,7 @@ Bericht instellen apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -742,7 +730,7 @@ Alleen lezen apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -750,7 +738,7 @@ Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -758,7 +746,7 @@ Toevoegen apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -770,7 +758,7 @@ Huishouding apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -778,7 +766,7 @@ Cache legen apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1062,7 +1050,7 @@ Activiteiten beheren apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1254,7 +1242,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1370,7 +1358,7 @@ Gegevensstoring melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2146,7 +2134,7 @@ Tijdlijn investeringen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2154,7 +2142,7 @@ Winnaars apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2162,7 +2150,7 @@ Verliezers apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2410,7 +2398,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2578,7 +2566,7 @@ Wil je deze activiteit echt verwijderen? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -2862,7 +2850,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2874,7 +2862,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2998,7 +2986,7 @@ Waardeontwikkeling van portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -3330,7 +3318,7 @@ Tijdlijn dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3346,7 +3334,7 @@ Account aanmaken apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -3370,7 +3358,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -3770,7 +3758,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3954,7 +3942,7 @@ Wil je echt al je activiteiten verwijderen? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4174,7 +4162,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4182,7 +4170,7 @@ Huidige reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4190,7 +4178,7 @@ Langste reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4670,7 +4658,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5316,7 +5304,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5405,7 +5393,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5659,7 +5647,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5767,11 +5755,11 @@ Zwitserland apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5779,7 +5767,7 @@ Wereldwijd apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5891,7 +5879,7 @@ Do you really want to delete this tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5915,7 +5903,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5923,7 +5911,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6043,7 +6031,7 @@ Do you really want to delete this asset profile? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6123,7 +6111,7 @@ Extreme Fear libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6131,7 +6119,7 @@ Extreme Greed libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6139,7 +6127,7 @@ Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6155,7 +6143,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - is an invalid currency! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. If a translation is missing, kindly support us in extending it here. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Do you really want to delete these profiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index 2942a4898..485c86d7c 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -87,7 +87,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -189,7 +189,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -443,7 +443,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -667,7 +667,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -775,7 +775,7 @@ Ryzyko strat na rynku może być znaczne. Nie jest zalecane inwestowanie pieniędzy, które mogą być potrzebne w krótkim okresie. apps/client/src/app/app.component.html - 205 + 214 @@ -1147,7 +1147,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1455,7 +1455,7 @@ Czy na pewno chcesz usunąć ten profil aktywów? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -1639,7 +1639,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -1782,20 +1782,12 @@ 124 - - Please add a currency: - Proszę dodać walutę: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Czy naprawdę chcesz usunąć ten kupon? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -1803,7 +1795,7 @@ Czy naprawdę chcesz usunąć tę walutę? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -1811,7 +1803,7 @@ Czy naprawdę chcesz usunąć tę wiadomość systemową? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -1819,7 +1811,7 @@ Czy naprawdę chcesz wyczyścić pamięć podręczną? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -1827,7 +1819,7 @@ Proszę ustawić swoją wiadomość systemową: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -1877,17 +1869,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - User Signup Rejestracja Użytkownika apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -1895,7 +1883,7 @@ Tryb Tylko do Odczytu apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -1903,7 +1891,7 @@ Wiadomość Systemowa apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -1911,7 +1899,7 @@ Ustaw Wiadomość apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -1919,7 +1907,7 @@ Kupony apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -1927,7 +1915,7 @@ Dodaj apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1939,7 +1927,7 @@ Konserwacja apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -1947,7 +1935,7 @@ Wyczyszczenie pamięci podręcznej apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -2067,7 +2055,7 @@ Czy naprawdę chcesz usunąć ten tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -2183,7 +2171,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2327,7 +2315,7 @@ Zarządzaj Aktywnościami apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2339,7 +2327,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2351,7 +2339,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2599,7 +2587,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2647,7 +2635,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -2803,7 +2791,7 @@ Zgłoś Błąd Danych apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -4219,7 +4207,7 @@ Czy na pewno chcesz usunąć te aktywności? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4711,7 +4699,7 @@ Największe wzrosty apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -4719,7 +4707,7 @@ Największy spadek apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -4727,7 +4715,7 @@ Rozwój portfela apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -4735,7 +4723,7 @@ Oś czasu inwestycji apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -4743,7 +4731,7 @@ Obecna passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4751,7 +4739,7 @@ Najdłuższa passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4759,7 +4747,7 @@ Oś czasu dywidend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -4791,7 +4779,7 @@ Ryzyko związane z klastrem walutowym apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -4799,7 +4787,7 @@ Ryzyko związane z klastrem kont apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -5391,11 +5379,11 @@ Szwajcaria apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5403,7 +5391,7 @@ Globalny apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5567,7 +5555,7 @@ Czy na pewno chcesz usunąć tę działalność? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -5815,7 +5803,7 @@ Japonia libs/ui/src/lib/i18n.ts - 84 + 86 @@ -6099,7 +6087,7 @@ Skrajny Strach libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6107,7 +6095,7 @@ Skrajna Zachłanność libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6115,7 +6103,7 @@ Neutralny libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - to nieprawidłowa waluta! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. Jeżeli brakuje jakiegoś tłumaczenia, uprzejmie prosimy o wsparcie w jego uzupełnieniu tutaj. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Łączny wynik aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Wyniki aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Łączny wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Łączna wartość netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Wynik netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Gromadzenie Danych apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Czy na pewno chcesz usunąć te profile? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Ups! Nie udało się usunąć profili. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternatywa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ Aplikacja apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budżetowanie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Społeczność apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Biuro Rodzinne apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Inwestor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Otwarty Kod Źródłowy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Finanse Osobiste apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Prywatność apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Oprogramowanie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Narzędzie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ Doświadczenie Użytkownika apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Majątek apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Zarządzanie Majątkiem apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgia libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bułgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Kanada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Czechy libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finlandia libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ Francja libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Niemcy libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ Indie libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Włochy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Holandia libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ Nowa Zelandia libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Polska libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Rumunia libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ Południowa Afryka libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Tajlandia libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ Stany Zjednoczone libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Nieaktywny apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraina libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Ryzyko związane z klastrem rynków gospodarczych apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ poza apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ zasady zgodne z Twoim portfelem. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Ryzyko klastra klasy aktywów apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index 8b9b7ac03..4c4a763fb 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -6,7 +6,7 @@ O risco de perda em investimentos pode ser substancial. Não é aconselhável investir dinheiro que possa vir a precisar a curto prazo. apps/client/src/app/app.component.html - 205 + 214 @@ -358,7 +358,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -781,20 +781,12 @@ 45 - - Please add a currency: - Por favor, adicione uma moeda: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Deseja realmente eliminar este cupão? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -802,7 +794,7 @@ Deseja realmente excluir esta moeda? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -810,7 +802,7 @@ Deseja realmente limpar a cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -818,7 +810,7 @@ Por favor, defina a sua mensagem do sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -852,17 +844,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - System Message Mensagem de Sistema apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -870,7 +858,7 @@ Definir Mensagem apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -878,7 +866,7 @@ Modo Somente Leitura apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -886,7 +874,7 @@ Cupões apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -894,7 +882,7 @@ Adicionar apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -906,7 +894,7 @@ Manutenção apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -914,7 +902,7 @@ Limpar Cache apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1022,7 +1010,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -1250,7 +1238,7 @@ Gerir Atividades apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -1262,7 +1250,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -1274,7 +1262,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -1478,7 +1466,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -1678,7 +1666,7 @@ Dados do Relatório com Problema apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2670,7 +2658,7 @@ Topo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -2678,7 +2666,7 @@ Fundo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -2686,7 +2674,7 @@ Evolução do Portefólio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -2694,7 +2682,7 @@ Cronograma de Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -2970,7 +2958,7 @@ Deseja realmente eliminar esta atividade? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -3278,7 +3266,7 @@ Registo do Utilizador apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -3378,7 +3366,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -3398,7 +3386,7 @@ Cronograma de Dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -3770,7 +3758,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -3954,7 +3942,7 @@ Deseja mesmo eliminar estas atividades? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4174,7 +4162,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -4182,7 +4170,7 @@ Série Atual apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4190,7 +4178,7 @@ Série mais Longa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4670,7 +4658,7 @@ Japão libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5316,7 +5304,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -5405,7 +5393,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -5659,7 +5647,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -5767,11 +5755,11 @@ Switzerland apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5779,7 +5767,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5891,7 +5879,7 @@ Do you really want to delete this tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5915,7 +5903,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5923,7 +5911,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6043,7 +6031,7 @@ Do you really want to delete this asset profile? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6123,7 +6111,7 @@ Extreme Fear libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6131,7 +6119,7 @@ Extreme Greed libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6139,7 +6127,7 @@ Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6155,7 +6143,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - is an invalid currency! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. If a translation is missing, kindly support us in extending it here. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Do you really want to delete these profiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 3c7d3f102..c84ddb5f1 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -87,7 +87,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -189,7 +189,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -443,7 +443,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -643,7 +643,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -751,7 +751,7 @@ Alım satımda kayıp riski büyük boyutta olabilir. Kısa vadede ihtiyaç duyabileceğiniz parayla yatırım yapmak tavsiye edilmez. apps/client/src/app/app.component.html - 205 + 214 @@ -1111,7 +1111,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1690,20 +1690,12 @@ 124 - - Please add a currency: - Lütfen bir para birimi giriniz: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - Do you really want to delete this coupon? Önbelleği temizlemeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -1711,7 +1703,7 @@ Bu para birimini silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -1719,7 +1711,7 @@ Önbelleği temizlemeyi gerçekten istiyor musunuz apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -1727,7 +1719,7 @@ Lütfen sistem mesajınızı belirleyin: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -1769,10 +1761,6 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - Tags @@ -1795,7 +1783,7 @@ Kullanıcı Kaydı apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -1803,7 +1791,7 @@ Salt okunur mod apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -1811,7 +1799,7 @@ Sistem Mesajı apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -1819,7 +1807,7 @@ Mesaj Belirle apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -1827,7 +1815,7 @@ Kupon apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -1835,7 +1823,7 @@ Ekle apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1847,7 +1835,7 @@ Genel Ayarlar apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -1855,7 +1843,7 @@ Önbelleği temizle apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -2047,7 +2035,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2179,7 +2167,7 @@ İşlemleri Yönet apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2191,7 +2179,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2203,7 +2191,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2483,7 +2471,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -2639,7 +2627,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2647,7 +2635,7 @@ Rapor Veri Sorunu apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -3727,7 +3715,7 @@ Tüm işlemlerinizi silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -3955,7 +3943,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -4195,7 +4183,7 @@ Üst apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -4203,7 +4191,7 @@ Alt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -4211,7 +4199,7 @@ Portföyün Gelişimi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -4219,7 +4207,7 @@ Yatırım Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -4227,7 +4215,7 @@ Güncel Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4235,7 +4223,7 @@ En Uzun Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4243,7 +4231,7 @@ Temettü Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -4867,11 +4855,11 @@ İsviçre apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -4879,7 +4867,7 @@ Küresel apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5283,7 +5271,7 @@ TBu işlemi silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -5495,7 +5483,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5891,7 +5879,7 @@ Bu etiketi silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -5915,7 +5903,7 @@ Kur Kümelenme Riskleri (Currency Cluster Risks) apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5923,7 +5911,7 @@ Hesap Kümelenme Riski (Account Cluster Risks) apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -6043,7 +6031,7 @@ Bu varlık profilini silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -6123,7 +6111,7 @@ Aşırı Korku libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6131,7 +6119,7 @@ Aşırı Açgözlülük libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6139,7 +6127,7 @@ Nötr libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6155,7 +6143,7 @@ Bu sistem mesajını silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -6214,14 +6202,6 @@ 109 - - is an invalid currency! - is an invalid currency! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - If a translation is missing, kindly support us in extending it here. If a translation is missing, kindly support us in extending it here. @@ -6327,7 +6307,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6335,7 +6315,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6343,7 +6323,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6351,7 +6331,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6359,7 +6339,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6367,7 +6347,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6375,7 +6355,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6483,7 +6463,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6735,7 +6715,7 @@ Do you really want to delete these profiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6743,7 +6723,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6775,7 +6755,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6783,7 +6763,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6791,7 +6771,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6799,7 +6779,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6807,7 +6787,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6815,7 +6795,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6823,7 +6803,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6831,7 +6811,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6839,7 +6819,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6847,7 +6827,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6855,7 +6835,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6863,7 +6843,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6871,7 +6851,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6879,7 +6859,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6887,7 +6867,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6895,7 +6875,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6903,7 +6883,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6911,7 +6891,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6919,7 +6899,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6927,7 +6907,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6935,7 +6915,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6943,7 +6923,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6951,7 +6931,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6959,7 +6939,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6967,7 +6947,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6975,7 +6955,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6983,7 +6963,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6991,7 +6971,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -6999,7 +6979,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7007,7 +6987,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7015,7 +6995,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7023,7 +7003,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7055,7 +7035,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7367,7 +7347,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7457,7 +7437,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7585,7 +7565,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7593,7 +7573,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7609,7 +7589,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7633,7 +7613,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7644,14 +7624,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 26690077b..0ac3b347b 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -250,7 +250,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -358,7 +358,7 @@ Ризик втрат у торгівлі може бути суттєвим. Не рекомендується інвестувати гроші, які можуть знадобитися в короткостроковій перспективі. apps/client/src/app/app.component.html - 205 + 214 @@ -447,7 +447,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -583,7 +583,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -803,7 +803,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -1235,7 +1235,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1635,7 +1635,7 @@ Ви дійсно хочете видалити цей профіль активу? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -1643,7 +1643,7 @@ Упс! Не вдалося видалити профілі. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -1651,7 +1651,7 @@ Ви дійсно хочете видалити ці профілі? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -1933,10 +1933,6 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - Name, symbol or ISIN @@ -1958,28 +1954,12 @@ 49 - - Please add a currency: - Будь ласка, додайте валюту: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - - - is an invalid currency! - є недопустимою валютою! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - Do you really want to delete this coupon? Ви дійсно хочете видалити цей купон? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -1987,7 +1967,7 @@ Ви дійсно хочете видалити цю валюту? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -1995,7 +1975,7 @@ Ви дійсно хочете видалити це системне повідомлення? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -2003,7 +1983,7 @@ Ви дійсно хочете очистити кеш? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -2011,7 +1991,7 @@ Будь ласка, встановіть ваше системне повідомлення: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -2059,7 +2039,7 @@ Реєстрація користувача apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -2067,7 +2047,7 @@ Режим лише для читання apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -2075,7 +2055,7 @@ Збір даних apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -2083,7 +2063,7 @@ Системне повідомлення apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -2091,7 +2071,7 @@ Встановити повідомлення apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -2099,7 +2079,7 @@ Купони apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -2107,7 +2087,7 @@ Додати apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -2119,7 +2099,7 @@ Прибирання apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -2127,7 +2107,7 @@ Очистити кеш apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -2279,7 +2259,7 @@ Будь ласка, введіть ваш ключ API Ghostfolio. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -2363,7 +2343,7 @@ Ви дійсно хочете видалити цей тег? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -2487,7 +2467,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2751,7 +2731,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2767,7 +2747,7 @@ Повідомити про збій даних apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -2807,7 +2787,7 @@ Керування діяльністю apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2819,7 +2799,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2831,7 +2811,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -3091,7 +3071,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -5139,7 +5119,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -5371,7 +5351,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -5403,7 +5383,7 @@ Абсолютна прибутковість активів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -5411,7 +5391,7 @@ Прибутковість активів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -5419,7 +5399,7 @@ Абсолютна прибутковість валюти apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -5427,7 +5407,7 @@ Прибутковість валюти apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -5435,7 +5415,7 @@ Абсолютна чиста прибутковість apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -5443,7 +5423,7 @@ Чиста прибутковість apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -5451,7 +5431,7 @@ Топ apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -5459,7 +5439,7 @@ Низ apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -5467,7 +5447,7 @@ Еволюція портфеля apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -5475,7 +5455,7 @@ Інвестиційний графік apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -5483,7 +5463,7 @@ Поточна серія apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -5491,7 +5471,7 @@ Найдовша серія apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -5499,7 +5479,7 @@ Графік дивідендів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -5547,7 +5527,7 @@ з apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -5555,7 +5535,7 @@ правила узгоджуються з вашим портфелем. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -5563,7 +5543,7 @@ Ризики зосередження валюти apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -5571,7 +5551,7 @@ Ризики зосередження класу активів apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -5579,7 +5559,7 @@ Ризики зосередження рахунків apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -5587,7 +5567,7 @@ Ризики зосередження економічного ринку apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -5595,7 +5575,7 @@ Неактивний apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -6025,11 +6005,11 @@ Швейцарія apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -6037,7 +6017,7 @@ Глобальний apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -6049,7 +6029,7 @@ Альтернатива apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6057,7 +6037,7 @@ Додаток apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6065,7 +6045,7 @@ Бюджетування apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6073,7 +6053,7 @@ Спільнота apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6081,7 +6061,7 @@ Сімейний офіс apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6089,7 +6069,7 @@ Інвестор apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6097,7 +6077,7 @@ Відкритий код apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6105,7 +6085,7 @@ Особисті фінанси apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6113,7 +6093,7 @@ Конфіденційність apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6121,7 +6101,7 @@ Програмне забезпечення apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6129,7 +6109,7 @@ Інструмент apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6137,7 +6117,7 @@ Користувацький досвід apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6145,7 +6125,7 @@ Багатство apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6153,7 +6133,7 @@ Управління багатством apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6689,7 +6669,7 @@ Ви дійсно хочете видалити ці дії? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -6697,7 +6677,7 @@ Ви дійсно хочете видалити цю активність? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -7405,7 +7385,7 @@ Австралія libs/ui/src/lib/i18n.ts - 73 + 74 @@ -7413,7 +7393,7 @@ Австрія libs/ui/src/lib/i18n.ts - 74 + 75 @@ -7421,7 +7401,7 @@ Бельгія libs/ui/src/lib/i18n.ts - 75 + 76 @@ -7429,7 +7409,7 @@ Болгарія libs/ui/src/lib/i18n.ts - 76 + 78 @@ -7437,7 +7417,7 @@ Канада libs/ui/src/lib/i18n.ts - 77 + 79 @@ -7445,7 +7425,7 @@ Чеська Республіка libs/ui/src/lib/i18n.ts - 78 + 80 @@ -7453,7 +7433,7 @@ Фінляндія libs/ui/src/lib/i18n.ts - 79 + 81 @@ -7461,7 +7441,7 @@ Франція libs/ui/src/lib/i18n.ts - 80 + 82 @@ -7469,7 +7449,7 @@ Німеччина libs/ui/src/lib/i18n.ts - 81 + 83 @@ -7477,7 +7457,7 @@ Індія libs/ui/src/lib/i18n.ts - 82 + 84 @@ -7485,7 +7465,7 @@ Італія libs/ui/src/lib/i18n.ts - 83 + 85 @@ -7493,7 +7473,7 @@ Японія libs/ui/src/lib/i18n.ts - 84 + 86 @@ -7501,7 +7481,7 @@ Нідерланди libs/ui/src/lib/i18n.ts - 85 + 87 @@ -7509,7 +7489,7 @@ Нова Зеландія libs/ui/src/lib/i18n.ts - 86 + 88 @@ -7517,7 +7497,7 @@ Польща libs/ui/src/lib/i18n.ts - 87 + 89 @@ -7525,7 +7505,7 @@ Румунія libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7533,7 +7513,7 @@ Південна Африка libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7541,7 +7521,7 @@ Таїланд libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7549,7 +7529,7 @@ Україна libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7557,7 +7537,7 @@ Сполучені Штати libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7565,7 +7545,7 @@ Екстремальний страх libs/ui/src/lib/i18n.ts - 96 + 99 @@ -7573,7 +7553,7 @@ Екстремальна жадібність libs/ui/src/lib/i18n.ts - 97 + 100 @@ -7581,7 +7561,7 @@ Нейтрально libs/ui/src/lib/i18n.ts - 100 + 103 @@ -7652,14 +7632,6 @@ 153 - - Copy AI prompt to clipboard - Скопіюйте запит AI в буфер обміну - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Early Access Ранній доступ @@ -7673,7 +7645,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7756,6 +7728,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index ac05bc01a..4241f4601 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -87,7 +87,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -187,7 +187,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -435,7 +435,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -649,7 +649,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -752,7 +752,7 @@ The risk of loss in trading can be substantial. It is not advisable to invest money you may need in the short term. apps/client/src/app/app.component.html - 205 + 214 @@ -1114,7 +1114,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1400,7 +1400,7 @@ Do you really want to delete this asset profile? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -1569,7 +1569,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -1700,53 +1700,39 @@ 124 - - Please add a currency: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - - - is an invalid currency! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - Do you really want to delete this coupon? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 Do you really want to delete this currency? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 Do you really want to flush the cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 Please set your system message: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -1790,51 +1776,47 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - User Signup apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 Read-only Mode apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 System Message apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 Set Message apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 Add apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1845,14 +1827,14 @@ Housekeeping apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 Flush Cache apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -1962,7 +1944,7 @@ Do you really want to delete this tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -2065,7 +2047,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2195,7 +2177,7 @@ Manage Activities apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2206,7 +2188,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2217,7 +2199,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2440,7 +2422,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2483,7 +2465,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -2623,7 +2605,7 @@ Report Data Glitch apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -3893,7 +3875,7 @@ Do you really want to delete these activities? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4333,49 +4315,49 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -4403,14 +4385,14 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -4965,18 +4947,18 @@ Switzerland apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5130,7 +5112,7 @@ Do you really want to delete this activity? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -5363,7 +5345,7 @@ Japan libs/ui/src/lib/i18n.ts - 84 + 86 @@ -5612,21 +5594,21 @@ Extreme Fear libs/ui/src/lib/i18n.ts - 96 + 99 Extreme Greed libs/ui/src/lib/i18n.ts - 97 + 100 Neutral libs/ui/src/lib/i18n.ts - 100 + 103 @@ -5751,21 +5733,21 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -5780,28 +5762,28 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -5925,7 +5907,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6104,7 +6086,7 @@ Do you really want to delete these profiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6118,7 +6100,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6153,224 +6135,224 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 Thailand libs/ui/src/lib/i18n.ts - 91 + 94 India libs/ui/src/lib/i18n.ts - 82 + 84 Austria libs/ui/src/lib/i18n.ts - 74 + 75 Poland libs/ui/src/lib/i18n.ts - 87 + 89 Italy libs/ui/src/lib/i18n.ts - 83 + 85 User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 Canada libs/ui/src/lib/i18n.ts - 77 + 79 New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 Romania libs/ui/src/lib/i18n.ts - 88 + 90 Germany libs/ui/src/lib/i18n.ts - 81 + 83 United States libs/ui/src/lib/i18n.ts - 93 + 96 Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 Belgium libs/ui/src/lib/i18n.ts - 75 + 76 Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 Australia libs/ui/src/lib/i18n.ts - 73 + 74 South Africa libs/ui/src/lib/i18n.ts - 89 + 92 Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 Finland libs/ui/src/lib/i18n.ts - 79 + 81 France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6398,7 +6380,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -6650,7 +6632,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -6745,7 +6727,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -6864,14 +6846,14 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -6885,7 +6867,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -6906,14 +6888,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 - - - - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 + 57 @@ -6941,7 +6916,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7014,6 +6989,66 @@ 50 + + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + + + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 680906979..3d07023d9 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -88,7 +88,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 23 + 22 @@ -190,7 +190,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 24 + 23 @@ -444,7 +444,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 26 + 25 @@ -668,7 +668,7 @@ apps/client/src/app/pages/about/overview/about-overview-page.html - 146 + 154 @@ -776,7 +776,7 @@ 交易损失的风险可能很大。不建议将短期内可能需要的资金进行投资。 apps/client/src/app/app.component.html - 205 + 214 @@ -1156,7 +1156,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 206 + 196 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1464,7 +1464,7 @@ 您确实要删除此资产配置文件吗? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 36 + 37 @@ -1648,7 +1648,7 @@ libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html - 70 + 71 @@ -1791,28 +1791,12 @@ 124 - - Please add a currency: - 请添加货币: - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 126 - - - - is an invalid currency! - 是无效的货币! - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - Do you really want to delete this coupon? 您确实要删除此优惠券吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 156 + 137 @@ -1820,7 +1804,7 @@ 您真的要删除该货币吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 169 + 150 @@ -1828,7 +1812,7 @@ 您真的要删除这条系统消息吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 182 + 163 @@ -1836,7 +1820,7 @@ 您真的要刷新缓存吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 206 + 187 @@ -1844,7 +1828,7 @@ 请设置您的系统消息: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 226 + 207 @@ -1894,17 +1878,13 @@ apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 22 - - apps/client/src/app/components/admin-overview/admin-overview.html - 105 - User Signup 用户注册 apps/client/src/app/components/admin-overview/admin-overview.html - 111 + 101 @@ -1912,7 +1892,7 @@ 只读模式 apps/client/src/app/components/admin-overview/admin-overview.html - 125 + 115 @@ -1920,7 +1900,7 @@ 系统信息 apps/client/src/app/components/admin-overview/admin-overview.html - 149 + 139 @@ -1928,7 +1908,7 @@ 设置留言 apps/client/src/app/components/admin-overview/admin-overview.html - 171 + 161 @@ -1936,7 +1916,7 @@ 优惠券 apps/client/src/app/components/admin-overview/admin-overview.html - 179 + 169 @@ -1944,7 +1924,7 @@ 添加 apps/client/src/app/components/admin-overview/admin-overview.html - 239 + 229 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1956,7 +1936,7 @@ 家政 apps/client/src/app/components/admin-overview/admin-overview.html - 247 + 237 @@ -1964,7 +1944,7 @@ 刷新缓存 apps/client/src/app/components/admin-overview/admin-overview.html - 251 + 241 @@ -2084,7 +2064,7 @@ 您真的要删除此标签吗? apps/client/src/app/components/admin-tag/admin-tag.component.ts - 87 + 85 @@ -2200,7 +2180,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 93 @@ -2344,7 +2324,7 @@ 管理活动 apps/client/src/app/components/home-holdings/home-holdings.html - 63 + 62 @@ -2356,7 +2336,7 @@ libs/ui/src/lib/i18n.ts - 98 + 101 @@ -2368,7 +2348,7 @@ libs/ui/src/lib/i18n.ts - 99 + 102 @@ -2616,7 +2596,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 178 + 213 @@ -2664,7 +2644,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 34 + 69 @@ -2820,7 +2800,7 @@ 报告数据故障 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 385 + 409 @@ -4236,7 +4216,7 @@ 您真的要删除所有活动吗? libs/ui/src/lib/activities-table/activities-table.component.ts - 218 + 219 @@ -4728,7 +4708,7 @@ 顶部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 197 + 215 @@ -4736,7 +4716,7 @@ 底部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 246 + 264 @@ -4744,7 +4724,7 @@ 投资组合演变 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 299 + 317 @@ -4752,7 +4732,7 @@ 投资时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 326 + 344 @@ -4760,7 +4740,7 @@ 当前连胜 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 347 + 365 @@ -4768,7 +4748,7 @@ 最长连续纪录 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 356 + 374 @@ -4776,7 +4756,7 @@ 股息时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 383 + 401 @@ -4808,7 +4788,7 @@ 货币集群风险 apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 58 + 93 @@ -4816,7 +4796,7 @@ 账户集群风险 apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 106 + 141 @@ -5432,11 +5412,11 @@ 瑞士 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 59 + 58 libs/ui/src/lib/i18n.ts - 90 + 93 @@ -5444,7 +5424,7 @@ 全球的 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 59 libs/ui/src/lib/i18n.ts @@ -5616,7 +5596,7 @@ 您确实要删除此活动吗? libs/ui/src/lib/activities-table/activities-table.component.ts - 228 + 229 @@ -5880,7 +5860,7 @@ 日本 libs/ui/src/lib/i18n.ts - 84 + 86 @@ -6164,7 +6144,7 @@ 极度恐惧 libs/ui/src/lib/i18n.ts - 96 + 99 @@ -6172,7 +6152,7 @@ 极度贪婪 libs/ui/src/lib/i18n.ts - 97 + 100 @@ -6180,7 +6160,7 @@ 中性的 libs/ui/src/lib/i18n.ts - 100 + 103 @@ -6320,7 +6300,7 @@ 绝对货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 103 + 121 @@ -6328,7 +6308,7 @@ 绝对净性能 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 150 + 168 @@ -6336,7 +6316,7 @@ 绝对资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 60 + 78 @@ -6352,7 +6332,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 87 @@ -6360,7 +6340,7 @@ 资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 81 + 99 @@ -6368,7 +6348,7 @@ 净绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 169 + 187 @@ -6376,7 +6356,7 @@ 货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 127 + 145 @@ -6516,7 +6496,7 @@ 数据收集 apps/client/src/app/components/admin-overview/admin-overview.html - 137 + 127 @@ -6736,7 +6716,7 @@ Do you really want to delete these profiles? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 67 + 68 @@ -6744,7 +6724,7 @@ Oops! Could not delete profiles. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 55 + 56 @@ -6776,7 +6756,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 82 + 81 @@ -6784,7 +6764,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 83 + 82 @@ -6792,7 +6772,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 84 + 83 @@ -6800,7 +6780,7 @@ Community apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 85 + 84 @@ -6808,7 +6788,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 85 @@ -6816,7 +6796,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 88 @@ -6824,7 +6804,7 @@ Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 89 @@ -6832,7 +6812,7 @@ Personal Finance apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 91 @@ -6840,7 +6820,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 92 @@ -6848,7 +6828,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 95 + 94 @@ -6856,7 +6836,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 95 @@ -6864,7 +6844,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 96 @@ -6872,7 +6852,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 97 @@ -6880,7 +6860,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 98 @@ -6888,7 +6868,7 @@ Australia libs/ui/src/lib/i18n.ts - 73 + 74 @@ -6896,7 +6876,7 @@ Austria libs/ui/src/lib/i18n.ts - 74 + 75 @@ -6904,7 +6884,7 @@ Belgium libs/ui/src/lib/i18n.ts - 75 + 76 @@ -6912,7 +6892,7 @@ Bulgaria libs/ui/src/lib/i18n.ts - 76 + 78 @@ -6920,7 +6900,7 @@ Canada libs/ui/src/lib/i18n.ts - 77 + 79 @@ -6928,7 +6908,7 @@ Czech Republic libs/ui/src/lib/i18n.ts - 78 + 80 @@ -6936,7 +6916,7 @@ Finland libs/ui/src/lib/i18n.ts - 79 + 81 @@ -6944,7 +6924,7 @@ France libs/ui/src/lib/i18n.ts - 80 + 82 @@ -6952,7 +6932,7 @@ Germany libs/ui/src/lib/i18n.ts - 81 + 83 @@ -6960,7 +6940,7 @@ India libs/ui/src/lib/i18n.ts - 82 + 84 @@ -6968,7 +6948,7 @@ Italy libs/ui/src/lib/i18n.ts - 83 + 85 @@ -6976,7 +6956,7 @@ Netherlands libs/ui/src/lib/i18n.ts - 85 + 87 @@ -6984,7 +6964,7 @@ New Zealand libs/ui/src/lib/i18n.ts - 86 + 88 @@ -6992,7 +6972,7 @@ Poland libs/ui/src/lib/i18n.ts - 87 + 89 @@ -7000,7 +6980,7 @@ Romania libs/ui/src/lib/i18n.ts - 88 + 90 @@ -7008,7 +6988,7 @@ South Africa libs/ui/src/lib/i18n.ts - 89 + 92 @@ -7016,7 +6996,7 @@ Thailand libs/ui/src/lib/i18n.ts - 91 + 94 @@ -7024,7 +7004,7 @@ United States libs/ui/src/lib/i18n.ts - 93 + 96 @@ -7056,7 +7036,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 197 + 232 @@ -7368,7 +7348,7 @@ Ukraine libs/ui/src/lib/i18n.ts - 92 + 95 @@ -7458,7 +7438,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 130 + 165 @@ -7586,7 +7566,7 @@ out of apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 22 + 56 @@ -7594,7 +7574,7 @@ rules align with your portfolio. apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 24 + 58 @@ -7610,7 +7590,7 @@ Asset Class Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 82 + 117 @@ -7634,7 +7614,7 @@ Please enter your Ghostfolio API key. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts - 59 + 57 @@ -7645,14 +7625,6 @@ 153 - - Copy AI prompt to clipboard - Copy AI prompt to clipboard - - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 27 - - Link has been copied to the clipboard Link has been copied to the clipboard @@ -7674,7 +7646,7 @@ Regional Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 154 + 189 @@ -7757,6 +7729,74 @@ 50 + + Market Data + Market Data + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 374 + + + + Change + Change + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + + Performance + Performance + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 365 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 378 + + + + Copy portfolio data to clipboard for AI prompt + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 28 + + + + Copy AI prompt to clipboard for analysis + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 44 + + + + Armenia + Armenia + + libs/ui/src/lib/i18n.ts + 73 + + + + British Virgin Islands + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 77 + + + + Singapore + Singapore + + libs/ui/src/lib/i18n.ts + 91 + + diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index bd0c74189..e5104a991 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -354,6 +354,16 @@ export function isDerivedCurrency(aCurrency: string) { }); } +export function isRootCurrency(aCurrency: string) { + if (aCurrency === 'USD') { + return true; + } + + return DERIVED_CURRENCIES.find(({ rootCurrency }) => { + return rootCurrency === aCurrency; + }); +} + export function parseDate(date: string): Date { if (!date) { return undefined; diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts index de3d7cf02..0772159e6 100644 --- a/libs/common/src/lib/interfaces/export.interface.ts +++ b/libs/common/src/lib/interfaces/export.interface.ts @@ -1,11 +1,9 @@ -import { Account, Order } from '@prisma/client'; +import { Account, Order, Platform, Tag } from '@prisma/client'; export interface Export { - meta: { - date: string; - version: string; - }; - accounts: Omit[]; + accounts: (Omit & { + balances: { date: string; value: number }[]; + })[]; activities: (Omit< Order, | 'accountUserId' @@ -16,5 +14,11 @@ export interface Export { | 'updatedAt' | 'userId' > & { date: string; symbol: string })[]; + meta: { + date: string; + version: string; + }; + platforms: Platform[]; + tags: Omit[]; user: { settings: { currency: string } }; } diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 7ad4948dc..3dcbbb32a 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -41,6 +41,7 @@ import type { AccountBalancesResponse } from './responses/account-balances-respo import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; import type { BenchmarkResponse } from './responses/benchmark-response.interface'; +import type { DataProviderGhostfolioAssetProfileResponse } from './responses/data-provider-ghostfolio-asset-profile-response.interface'; import type { DataProviderGhostfolioStatusResponse } from './responses/data-provider-ghostfolio-status-response.interface'; import type { DividendsResponse } from './responses/dividends-response.interface'; import type { ResponseError } from './responses/errors.interface'; @@ -83,6 +84,7 @@ export { BenchmarkProperty, BenchmarkResponse, Coupon, + DataProviderGhostfolioAssetProfileResponse, DataProviderGhostfolioStatusResponse, DataProviderInfo, DividendsResponse, diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index e455f73ca..746736f6b 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -14,6 +14,7 @@ export interface PortfolioDetails { valueInPercentage?: number; }; }; + createdAt: Date; holdings: { [symbol: string]: PortfolioPosition }; markets?: { [key in Market]: { diff --git a/libs/common/src/lib/interfaces/responses/data-provider-ghostfolio-asset-profile-response.interface.ts b/libs/common/src/lib/interfaces/responses/data-provider-ghostfolio-asset-profile-response.interface.ts new file mode 100644 index 000000000..7fd0314fb --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/data-provider-ghostfolio-asset-profile-response.interface.ts @@ -0,0 +1,4 @@ +import { SymbolProfile } from '@prisma/client'; + +export interface DataProviderGhostfolioAssetProfileResponse + extends Partial {} diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index dc6e57587..7a98e3c8d 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -32,6 +32,7 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { } interface PublicPortfolioResponseV1 { + createdAt: Date; performance: { '1d': { relativeChange: number; diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index 667e59fd8..84f48d1dc 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -10,6 +10,7 @@ import { UserSettings } from './user-settings.interface'; export interface User { access: Pick[]; accounts: Account[]; + activitiesCount: number; dateOfFirstActivity: Date; id: string; permissions: string[]; diff --git a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts index 4a31ef7b2..9ae32d802 100644 --- a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts +++ b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts @@ -9,8 +9,10 @@ export interface XRayRulesSettings { EconomicMarketClusterRiskEmergingMarkets?: RuleSettings; EmergencyFundSetup?: RuleSettings; FeeRatioInitialInvestment?: RuleSettings; + RegionalMarketClusterRiskAsiaPacific?: RuleSettings; RegionalMarketClusterRiskEmergingMarkets?: RuleSettings; RegionalMarketClusterRiskEurope?: RuleSettings; + RegionalMarketClusterRiskJapan?: RuleSettings; RegionalMarketClusterRiskNorthAmerica?: RuleSettings; } diff --git a/libs/common/src/lib/models/portfolio-snapshot.ts b/libs/common/src/lib/models/portfolio-snapshot.ts index 46bd5c18f..e30585b82 100644 --- a/libs/common/src/lib/models/portfolio-snapshot.ts +++ b/libs/common/src/lib/models/portfolio-snapshot.ts @@ -9,6 +9,10 @@ import { Big } from 'big.js'; import { Transform, Type } from 'class-transformer'; export class PortfolioSnapshot { + activitiesCount: number; + + createdAt: Date; + @Transform(transformToBig, { toClassOnly: true }) @Type(() => Big) currentValueInBaseCurrency: Big; diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index d19b8daf0..1c79720f5 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -13,6 +13,7 @@ export const permissions = { createMarketData: 'createMarketData', createMarketDataOfOwnAssetProfile: 'createMarketDataOfOwnAssetProfile', createOrder: 'createOrder', + createOwnTag: 'createOwnTag', createPlatform: 'createPlatform', createTag: 'createTag', createUserAccount: 'createUserAccount', @@ -67,6 +68,7 @@ export function getPermissions(aRole: Role): string[] { permissions.createMarketData, permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, + permissions.createOwnTag, permissions.createPlatform, permissions.createTag, permissions.deleteAccess, @@ -110,6 +112,7 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccountBalance, permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, + permissions.createOwnTag, permissions.deleteAccess, permissions.deleteAccount, permissions.deleteAccountBalance, diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index e4c820fb7..936f3c6f6 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -82,6 +82,16 @@ export const personalFinanceTools: Product[] = [ regions: ['Global'], slogan: 'Take control of your financial future' }, + { + founded: 2017, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'coinstats', + name: 'CoinStats', + origin: 'Armenia', + pricingPerYear: '$168', + slogan: 'Manage All Your Wallets & Exchanges From One Place' + }, { founded: 2013, hasFreePlan: true, @@ -154,6 +164,7 @@ export const personalFinanceTools: Product[] = [ name: 'Delta Investment Tracker', note: 'Acquired by eToro', origin: 'Belgium', + pricingPerYear: '$150', slogan: 'The app to track all your investments. Make smart moves only.' }, { @@ -252,6 +263,13 @@ export const personalFinanceTools: Product[] = [ slogan: 'The most convenient mobile application for personal finance accounting' }, + { + founded: 2022, + key: 'fincake', + name: 'Fincake', + origin: 'British Virgin Islands', + slogan: 'Easy-to-use Portfolio Tracker' + }, { founded: 2023, hasFreePlan: true, @@ -340,6 +358,15 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '€119', slogan: 'ETF portfolios made simple' }, + { + founded: 2018, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'koinly', + name: 'Koinly', + origin: 'Singapore', + slogan: 'Track all your crypto wallets in one place' + }, { founded: 2016, hasFreePlan: true, @@ -469,6 +496,16 @@ export const personalFinanceTools: Product[] = [ slogan: 'Track your equity, fund, investment trust, ETF and pension investments in one place.' }, + { + founded: 2020, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'nansen', + name: 'Crypto Portfolio Tracker by Nansen', + origin: 'Singapore', + pricingPerYear: '$1188', + slogan: 'Your Complete Crypto Portfolio, Reimagined' + }, { founded: 2017, hasFreePlan: false, @@ -634,6 +671,16 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '€80', slogan: 'Stock Portfolio Tracker' }, + { + founded: 2014, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'simply-wallstreet', + name: 'Stock Portfolio Tracker & Visualizer by Simply Wall St', + origin: 'Australia', + pricingPerYear: '$120', + slogan: 'Smart portfolio tracker for informed investors' + }, { founded: 2021, hasFreePlan: true, @@ -706,6 +753,16 @@ export const personalFinanceTools: Product[] = [ slogan: 'Your financial life in a spreadsheet, automatically updated each day' }, + { + founded: 2011, + hasFreePlan: false, + hasSelfHostingAbility: false, + key: 'tradervue', + name: 'Tradervue', + origin: 'United States', + pricingPerYear: '$360', + slogan: 'The Trading Journal to Improve Your Trading Performance' + }, { hasFreePlan: true, hasSelfHostingAbility: false, diff --git a/libs/common/src/lib/types/ai-prompt-mode.type.ts b/libs/common/src/lib/types/ai-prompt-mode.type.ts new file mode 100644 index 000000000..00a031df0 --- /dev/null +++ b/libs/common/src/lib/types/ai-prompt-mode.type.ts @@ -0,0 +1 @@ +export type AiPromptMode = 'analysis' | 'portfolio'; diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts index 9e8178d3c..668486a94 100644 --- a/libs/common/src/lib/types/index.ts +++ b/libs/common/src/lib/types/index.ts @@ -2,6 +2,7 @@ import type { AccessType } from './access-type.type'; import type { AccessWithGranteeUser } from './access-with-grantee-user.type'; import type { AccountWithPlatform } from './account-with-platform.type'; import type { AccountWithValue } from './account-with-value.type'; +import type { AiPromptMode } from './ai-prompt-mode.type'; import type { BenchmarkTrend } from './benchmark-trend.type'; import type { ColorScheme } from './color-scheme.type'; import type { DateRange } from './date-range.type'; @@ -24,6 +25,7 @@ export type { AccessWithGranteeUser, AccountWithPlatform, AccountWithValue, + AiPromptMode, BenchmarkTrend, ColorScheme, DateRange, diff --git a/libs/ui/.storybook/main.js b/libs/ui/.storybook/main.js index 8c8323a0c..5b50ae062 100644 --- a/libs/ui/.storybook/main.js +++ b/libs/ui/.storybook/main.js @@ -1,9 +1,16 @@ +/** @type {import('@storybook/angular').StorybookConfig} */ const config = { addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/angular', options: {} }, + staticDirs: [ + { + from: '../../../apps/client/src/assets', + to: '/assets' + } + ], stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'] }; diff --git a/libs/ui/project.json b/libs/ui/project.json index 1b3f0e4e7..4c8e6c125 100644 --- a/libs/ui/project.json +++ b/libs/ui/project.json @@ -41,7 +41,7 @@ "executor": "@storybook/angular:build-storybook", "outputs": ["{options.outputDir}"], "options": { - "outputDir": "dist/storybook/ui", + "outputDir": "dist/apps/client/development/storybook", "configDir": "libs/ui/.storybook", "browserTarget": "ui:build-storybook", "compodoc": false, @@ -61,7 +61,7 @@ "executor": "@nx/web:file-server", "options": { "buildTarget": "ui:build-storybook", - "staticFilePath": "dist/storybook/ui" + "staticFilePath": "dist/storybook" }, "configurations": { "ci": { diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index 4fdc41c59..e5b33efd2 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -500,7 +500,10 @@ /> @if ( - dataSource?.data.length === 0 && hasPermissionToCreateActivity && !isLoading + !hasActivities && + dataSource?.data.length === 0 && + hasPermissionToCreateActivity && + !isLoading ) {
    diff --git a/libs/ui/src/lib/activities-table/activities-table.component.ts b/libs/ui/src/lib/activities-table/activities-table.component.ts index 01b4c6ead..8e5a44a50 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.ts @@ -76,6 +76,7 @@ export class GfActivitiesTableComponent @Input() baseCurrency: string; @Input() dataSource: MatTableDataSource; @Input() deviceType: string; + @Input() hasActivities: boolean; @Input() hasPermissionToCreateActivity: boolean; @Input() hasPermissionToDeleteActivity: boolean; @Input() hasPermissionToExportActivities: boolean; diff --git a/libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts b/libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts index 38c6252a8..96dc6800e 100644 --- a/libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts +++ b/libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts @@ -8,7 +8,6 @@ import { } from '@ghostfolio/common/interfaces'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; -import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, @@ -33,7 +32,6 @@ import { BenchmarkDetailDialogParams } from './interfaces/interfaces'; changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'd-flex flex-column h-100' }, imports: [ - CommonModule, GfDialogFooterModule, GfDialogHeaderModule, GfLineChartComponent, diff --git a/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.html b/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.html index fb9f2c13b..921433620 100644 --- a/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.html +++ b/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.html @@ -1,9 +1,16 @@ - Market data provided by {{ + Market data provided by  + @for ( + dataProviderInfo of dataProviderInfos; + track dataProviderInfo; + let last = $last + ) { + {{ dataProviderInfo.name - }}, . + }} + @if (!last) { + ,  + } + } + . diff --git a/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.ts b/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.ts index 9be034e64..2d455c0d6 100644 --- a/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.ts +++ b/libs/ui/src/lib/data-provider-credits/data-provider-credits.component.ts @@ -1,6 +1,5 @@ import { DataProviderInfo } from '@ghostfolio/common/interfaces'; -import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, @@ -10,7 +9,6 @@ import { @Component({ changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-data-provider-credits', styleUrls: ['./data-provider-credits.component.scss'], diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts index f8ce3dd50..64fbe1b74 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -41,7 +41,7 @@ import { Tooltip } from 'chart.js'; import 'chartjs-adapter-date-fns'; -import * as Color from 'color'; +import Color from 'color'; import { add, addYears, diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts index 710cb4020..73f382c5e 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts @@ -1,7 +1,6 @@ import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; -import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -29,7 +28,6 @@ import { HistoricalMarketDataEditorDialogParams } from './interfaces/interfaces' changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'h-100' }, imports: [ - CommonModule, FormsModule, MatButtonModule, MatDatepickerModule, diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html index b35e1d812..b72ba15f8 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html @@ -48,6 +48,7 @@ cdkTextareaAutosize formControlName="csvString" matInput + rows="2" type="text" (keyup.enter)="$event.stopPropagation()" > diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.html b/libs/ui/src/lib/holdings-table/holdings-table.component.html index ad6858dd6..d187d7dc7 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.html +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -277,11 +277,3 @@
    } - -@if ( - dataSource.data.length === 0 && hasPermissionToCreateActivity && !isLoading -) { -
    - -
    -} diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts index d5b5d2475..beb2f62d1 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts @@ -5,7 +5,6 @@ import { AssetProfileIdentifier, PortfolioPosition } from '@ghostfolio/common/interfaces'; -import { GfNoTransactionsInfoComponent } from '@ghostfolio/ui/no-transactions-info'; import { GfValueComponent } from '@ghostfolio/ui/value'; import { CommonModule } from '@angular/common'; @@ -34,7 +33,6 @@ import { Subject, Subscription } from 'rxjs'; imports: [ CommonModule, GfAssetProfileIconComponent, - GfNoTransactionsInfoComponent, GfSymbolModule, GfValueComponent, MatButtonModule, @@ -52,7 +50,6 @@ import { Subject, Subscription } from 'rxjs'; export class GfHoldingsTableComponent implements OnChanges, OnDestroy { @Input() baseCurrency: string; @Input() deviceType: string; - @Input() hasPermissionToCreateActivity: boolean; @Input() hasPermissionToOpenDetails = true; @Input() hasPermissionToShowValues = true; @Input() holdings: PortfolioPosition[]; diff --git a/libs/ui/src/lib/i18n.ts b/libs/ui/src/lib/i18n.ts index 2af142ac4..f198973ac 100644 --- a/libs/ui/src/lib/i18n.ts +++ b/libs/ui/src/lib/i18n.ts @@ -71,9 +71,11 @@ const locales = { 'South America': $localize`South America`, // Countries + Armenia: $localize`Armenia`, Australia: $localize`Australia`, Austria: $localize`Austria`, Belgium: $localize`Belgium`, + 'British Virgin Islands': $localize`British Virgin Islands`, Bulgaria: $localize`Bulgaria`, Canada: $localize`Canada`, 'Czech Republic': $localize`Czech Republic`, @@ -87,6 +89,7 @@ const locales = { 'New Zealand': $localize`New Zealand`, Poland: $localize`Poland`, Romania: $localize`Romania`, + Singapore: $localize`Singapore`, 'South Africa': $localize`South Africa`, Switzerland: $localize`Switzerland`, Thailand: $localize`Thailand`, diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts index 6319c3cd7..cc3c40d32 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts @@ -29,7 +29,7 @@ import { ArcElement } from 'chart.js'; import { DoughnutController } from 'chart.js'; import { Chart } from 'chart.js'; import ChartDataLabels from 'chartjs-plugin-datalabels'; -import * as Color from 'color'; +import Color from 'color'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; const { diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.html b/libs/ui/src/lib/tags-selector/tags-selector.component.html index 4f3929423..c3c02f3c7 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.html +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.html @@ -43,7 +43,7 @@ } - @if (hasPermissionToCreateTags && tagInputControl.value) { + @if (hasPermissionToCreateTag && tagInputControl.value) { diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts index 8d1431b46..42fb68876 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts @@ -1,4 +1,5 @@ import { CommonModule } from '@angular/common'; +import '@angular/localize/init'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; @@ -49,7 +50,7 @@ export const Default: Story = { export const CreateCustomTags: Story = { args: { - hasPermissionToCreateTags: true, + hasPermissionToCreateTag: true, tags: [ { id: 'EMERGENCY_FUND', diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.ts index 611c1e938..02b3a0a95 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts @@ -42,7 +42,7 @@ import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; templateUrl: 'tags-selector.component.html' }) export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy { - @Input() hasPermissionToCreateTags = false; + @Input() hasPermissionToCreateTag = false; @Input() readonly = false; @Input() tags: Tag[]; @Input() tagsAvailable: Tag[]; @@ -81,7 +81,7 @@ export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy { return id === event.option.value; }); - if (!tag && this.hasPermissionToCreateTags) { + if (!tag && this.hasPermissionToCreateTag) { tag = { id: undefined, name: event.option.value as string, diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.stories.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.stories.ts new file mode 100644 index 000000000..2000b4f98 --- /dev/null +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.stories.ts @@ -0,0 +1,392 @@ +import { CommonModule } from '@angular/common'; +import '@angular/localize/init'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; + +import { GfTreemapChartComponent } from './treemap-chart.component'; + +export default { + title: 'Treemap Chart', + component: GfTreemapChartComponent, + decorators: [ + moduleMetadata({ + imports: [CommonModule, NgxSkeletonLoaderModule] + }) + ], + argTypes: { + colorScheme: { + control: { + type: 'select' + }, + options: ['DARK', 'LIGHT'] + }, + cursor: { + control: { + type: 'select' + }, + options: ['', 'pointer'] + } + } +} as Meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + baseCurrency: 'USD', + colorScheme: 'LIGHT', + cursor: undefined, + dateRange: 'mtd', + holdings: [ + { + allocationInPercentage: 0.042990776363386086, + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2021-12-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 3856, + grossPerformancePercent: 0.46047289228564603, + grossPerformancePercentWithCurrencyEffect: 0.46047289228564603, + grossPerformanceWithCurrencyEffect: 3856, + holdings: [], + investment: 8374, + marketPrice: 244.6, + name: 'Apple Inc', + netPerformance: 3855, + netPerformancePercent: 0.460353475041796, + netPerformancePercentWithCurrencyEffect: 0.036440677966101696, + netPerformanceWithCurrencyEffect: 430, + quantity: 50, + sectors: [], + symbol: 'AAPL', + tags: [], + transactionCount: 1, + url: 'https://www.apple.com', + valueInBaseCurrency: 12230 + }, + { + allocationInPercentage: 0.02377401948293552, + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + countries: [], + currency: 'EUR', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2021-04-23T00:00:00.000Z'), + dividend: 192, + grossPerformance: 2226.700251889169, + grossPerformancePercent: 0.49083842309827874, + grossPerformancePercentWithCurrencyEffect: 0.29306136948826367, + grossPerformanceWithCurrencyEffect: 1532.8272791336772, + holdings: [], + investment: 4536.523929471033, + marketPrice: 322.2, + name: 'Allianz SE', + netPerformance: 2222.2921914357685, + netPerformancePercent: 0.48986674069961134, + netPerformancePercentWithCurrencyEffect: 0.034489367670592026, + netPerformanceWithCurrencyEffect: 225.48257403052068, + quantity: 20, + sectors: [], + symbol: 'ALV.DE', + tags: [], + transactionCount: 2, + url: 'https://www.allianz.com', + valueInBaseCurrency: 6763.224181360202 + }, + { + allocationInPercentage: 0.08038536990007467, + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2018-10-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 12758.05, + grossPerformancePercent: 1.2619300787837724, + grossPerformancePercentWithCurrencyEffect: 1.2619300787837724, + grossPerformanceWithCurrencyEffect: 12758.05, + holdings: [], + investment: 10109.95, + marketPrice: 228.68, + name: 'Amazon.com, Inc.', + netPerformance: 12677.26, + netPerformancePercent: 1.253938941339967, + netPerformancePercentWithCurrencyEffect: -0.037866008722316276, + netPerformanceWithCurrencyEffect: -899.99926757812, + quantity: 100, + sectors: [], + symbol: 'AMZN', + tags: [], + transactionCount: 1, + url: 'https://www.aboutamazon.com', + valueInBaseCurrency: 22868 + }, + { + allocationInPercentage: 0.19216416482928922, + assetClass: 'LIQUIDITY', + assetSubClass: 'CRYPTOCURRENCY', + countries: [], + currency: 'USD', + dataSource: 'COINGECKO', + dateOfFirstActivity: new Date('2017-08-16T00:00:00.000Z'), + dividend: 0, + grossPerformance: 52666.7898248, + grossPerformancePercent: 26.333394912400003, + grossPerformancePercentWithCurrencyEffect: 26.333394912400003, + grossPerformanceWithCurrencyEffect: 52666.7898248, + holdings: [], + investment: 1999.9999999999998, + marketPrice: 97364, + name: 'Bitcoin', + netPerformance: 52636.8898248, + netPerformancePercent: 26.3184449124, + netPerformancePercentWithCurrencyEffect: -0.04760906442310894, + netPerformanceWithCurrencyEffect: -2732.737808972287, + quantity: 0.5614682, + sectors: [], + symbol: 'bitcoin', + tags: [], + transactionCount: 1, + url: null, + valueInBaseCurrency: 54666.7898248 + }, + { + allocationInPercentage: 0.007378652850073097, + assetClass: 'FIXED_INCOME', + assetSubClass: 'BOND', + countries: [], + currency: 'EUR', + dataSource: 'MANUAL', + dateOfFirstActivity: new Date('2021-02-01T00:00:00.000Z'), + dividend: 11.45, + grossPerformance: 0, + grossPerformancePercent: 0, + grossPerformancePercentWithCurrencyEffect: -0.1247202380342517, + grossPerformanceWithCurrencyEffect: -258.2576430160448, + holdings: [], + investment: 2099.0764063811926, + marketPrice: 1, + name: 'Bondora Go & Grow', + netPerformance: 0, + netPerformancePercent: 0, + netPerformancePercentWithCurrencyEffect: 0.009445843828715519, + netPerformanceWithCurrencyEffect: 19.6420125363184, + quantity: 2000, + sectors: [], + symbol: 'BONDORA_GO_AND_GROW', + tags: [], + transactionCount: 5, + url: null, + valueInBaseCurrency: 2099.0764063811926 + }, + { + allocationInPercentage: 0.07787531695543741, + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'CHF', + dataSource: 'MANUAL', + dateOfFirstActivity: new Date('2021-04-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 4550.843985045582, + grossPerformancePercent: 0.3631417324494093, + grossPerformancePercentWithCurrencyEffect: 0.42037247857285137, + grossPerformanceWithCurrencyEffect: 5107.057936556927, + holdings: [], + investment: 17603.097090932337, + marketPrice: 188.22, + name: 'frankly Extreme 95 Index', + netPerformance: 4550.843985045582, + netPerformancePercent: 0.3631417324494093, + netPerformancePercentWithCurrencyEffect: 0.026190604904358043, + netPerformanceWithCurrencyEffect: 565.4165171873152, + quantity: 105.87328656807, + sectors: [], + symbol: 'FRANKLY95P', + tags: [], + transactionCount: 6, + url: 'https://www.frankly.ch', + valueInBaseCurrency: 22153.941075977917 + }, + { + allocationInPercentage: 0.04307127421937313, + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2023-01-03T00:00:00.000Z'), + dividend: 0, + grossPerformance: 5065.5, + grossPerformancePercent: 0.7047750229568411, + grossPerformancePercentWithCurrencyEffect: 0.7047750229568411, + grossPerformanceWithCurrencyEffect: 5065.5, + holdings: [], + investment: 7187.4, + marketPrice: 408.43, + name: 'Microsoft Corporation', + netPerformance: 5065.5, + netPerformancePercent: 0.7047750229568411, + netPerformancePercentWithCurrencyEffect: -0.015973588391056275, + netPerformanceWithCurrencyEffect: -198.899926757814, + quantity: 30, + sectors: [], + symbol: 'MSFT', + tags: [], + transactionCount: 1, + url: 'https://www.microsoft.com', + valueInBaseCurrency: 12252.9 + }, + { + allocationInPercentage: 0.18762679306394897, + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2017-01-03T00:00:00.000Z'), + dividend: 0, + grossPerformance: 51227.500000005, + grossPerformancePercent: 23.843379101756675, + grossPerformancePercentWithCurrencyEffect: 23.843379101756675, + grossPerformanceWithCurrencyEffect: 51227.500000005, + holdings: [], + investment: 2148.499999995, + marketPrice: 355.84, + name: 'Tesla, Inc.', + netPerformance: 51197.500000005, + netPerformancePercent: 23.829415871596066, + netPerformancePercentWithCurrencyEffect: -0.12051410125545206, + netPerformanceWithCurrencyEffect: -7314.00091552734, + quantity: 150, + sectors: [], + symbol: 'TSLA', + tags: [], + transactionCount: 1, + url: 'https://www.tesla.com', + valueInBaseCurrency: 53376 + }, + { + allocationInPercentage: 0.053051250766657634, + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2019-03-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 6845.8, + grossPerformancePercent: 1.0164758094605268, + grossPerformancePercentWithCurrencyEffect: 1.0164758094605268, + grossPerformanceWithCurrencyEffect: 6845.8, + holdings: [], + investment: 8246.2, + marketPrice: 301.84, + name: 'Vanguard Total Stock Market Index Fund ETF Shares', + netPerformance: 6746.3, + netPerformancePercent: 1.0017018833976383, + netPerformancePercentWithCurrencyEffect: 0.01085061564051406, + netPerformanceWithCurrencyEffect: 161.99969482422, + quantity: 50, + sectors: [], + symbol: 'VTI', + tags: [], + transactionCount: 5, + url: 'https://www.vanguard.com', + valueInBaseCurrency: 15092 + }, + { + allocationInPercentage: 0.0836576192450555, + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'CHF', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2018-03-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 6462.42356864925, + grossPerformancePercent: 0.5463044783973836, + grossPerformancePercentWithCurrencyEffect: 0.6282343505275325, + grossPerformanceWithCurrencyEffect: 7121.935580698947, + holdings: [], + investment: 17336.464702612564, + marketPrice: 129.74, + name: 'Vanguard FTSE All-World UCITS ETF', + netPerformance: 6373.040578098944, + netPerformancePercent: 0.5387484388540966, + netPerformancePercentWithCurrencyEffect: 0.008409682389650015, + netPerformanceWithCurrencyEffect: 198.47200506226807, + quantity: 165, + sectors: [], + symbol: 'VWRL.SW', + tags: [], + transactionCount: 5, + url: 'https://www.vanguard.com', + valueInBaseCurrency: 23798.888271261814 + }, + { + allocationInPercentage: 0.03265192235898284, + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'EUR', + dataSource: 'YAHOO', + dateOfFirstActivity: new Date('2021-08-19T00:00:00.000Z'), + dividend: 0, + grossPerformance: 3112.7991183879094, + grossPerformancePercent: 0.5040147846036197, + grossPerformancePercentWithCurrencyEffect: 0.3516875105542396, + grossPerformanceWithCurrencyEffect: 2416.799201046856, + holdings: [], + investment: 6176.007556675063, + marketPrice: 118.005, + name: 'Xtrackers MSCI World UCITS ETF 1C', + netPerformance: 3081.4179261125105, + netPerformancePercent: 0.4989336392216841, + netPerformancePercentWithCurrencyEffect: 0.006460676966633529, + netPerformanceWithCurrencyEffect: 59.626750161726044, + quantity: 75, + sectors: [], + symbol: 'XDWD.DE', + tags: [], + transactionCount: 1, + url: null, + valueInBaseCurrency: 9288.806675062973 + }, + { + allocationInPercentage: 0.17537283996478595, + assetClass: 'LIQUIDITY', + assetSubClass: 'CASH', + countries: [], + currency: 'USD', + dataSource: 'MANUAL', + dateOfFirstActivity: new Date('2021-04-01T00:00:00.000Z'), + dividend: 0, + grossPerformance: 0, + grossPerformancePercent: 0, + grossPerformancePercentWithCurrencyEffect: 0, + grossPerformanceWithCurrencyEffect: 0, + holdings: [], + investment: 49890, + marketPrice: 0, + name: 'USD', + netPerformance: 0, + netPerformancePercent: 0, + netPerformancePercentWithCurrencyEffect: 0, + netPerformanceWithCurrencyEffect: 0, + quantity: 0, + sectors: [], + symbol: 'USD', + tags: [], + transactionCount: 0, + valueInBaseCurrency: 49890 + } + ], + locale: 'en-US' + } +}; diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts index 4c3167c9e..8c5ee94de 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -342,24 +342,42 @@ export class GfTreemapChartComponent }), callbacks: { label: (context) => { + const allocationInPercentage = `${((context.raw._data.allocationInPercentage as number) * 100).toFixed(2)}%`; const name = context.raw._data.name; + const sign = + context.raw._data.netPerformancePercentWithCurrencyEffect > 0 + ? '+' + : ''; const symbol = context.raw._data.symbol; + const netPerformanceInPercentageWithSign = `${sign}${(context.raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`; + if (context.raw._data.valueInBaseCurrency !== null) { const value = context.raw._data.valueInBaseCurrency as number; return [ - `${name ?? symbol}`, + `${name ?? symbol} (${allocationInPercentage})`, `${value.toLocaleString(this.locale, { maximumFractionDigits: 2, minimumFractionDigits: 2 - })} ${this.baseCurrency}` + })} ${this.baseCurrency}`, + '', + $localize`Change` + ' (' + $localize`Performance` + ')', + `${sign}${context.raw._data.netPerformanceWithCurrencyEffect.toLocaleString( + this.locale, + { + maximumFractionDigits: 2, + minimumFractionDigits: 2 + } + )} ${this.baseCurrency} (${netPerformanceInPercentageWithSign})` ]; } else { - const percentage = - (context.raw._data.allocationInPercentage as number) * 100; - - return [`${name ?? symbol}`, `${percentage.toFixed(2)}%`]; + return [ + `${name ?? symbol} (${allocationInPercentage})`, + '', + $localize`Performance`, + netPerformanceInPercentageWithSign + ]; } }, title: () => { diff --git a/libs/ui/src/lib/trend-indicator/trend-indicator.component.ts b/libs/ui/src/lib/trend-indicator/trend-indicator.component.ts index e44c41aa9..efb48c981 100644 --- a/libs/ui/src/lib/trend-indicator/trend-indicator.component.ts +++ b/libs/ui/src/lib/trend-indicator/trend-indicator.component.ts @@ -1,6 +1,5 @@ import { DateRange, MarketState } from '@ghostfolio/common/types'; -import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, @@ -11,7 +10,7 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule, NgxSkeletonLoaderModule], + imports: [NgxSkeletonLoaderModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-trend-indicator', styleUrls: ['./trend-indicator.component.scss'], diff --git a/libs/ui/src/lib/value/value.component.html b/libs/ui/src/lib/value/value.component.html index a691c54e8..0ead7497e 100644 --- a/libs/ui/src/lib/value/value.component.html +++ b/libs/ui/src/lib/value/value.component.html @@ -10,8 +10,8 @@ > -
    +
    -
    -
    +
    +
    +
    -
    =6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", + "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/generator": "^7.26.8", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/types": "^7.26.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -4182,9 +4180,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -8667,9 +8665,9 @@ "license": "MIT" }, "node_modules/@prisma/client": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.3.0.tgz", - "integrity": "sha512-BY3Fi28PUSk447Bpv22LhZp4HgNPo7NsEN+EteM1CLDnLjig5863jpW+3c3HHLFmml+nB/eJv1CjSriFZ8z7Cg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.4.1.tgz", + "integrity": "sha512-A7Mwx44+GVZVexT5e2GF/WcKkEkNNKbgr059xpr5mn+oUm2ZW1svhe+0TRNBwCdzhfIZ+q23jEgsNPvKD9u+6g==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -8689,53 +8687,53 @@ } }, "node_modules/@prisma/debug": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.3.0.tgz", - "integrity": "sha512-m1lQv//0Rc5RG8TBpNUuLCxC35Ghi5XfpPmL83Gh04/GICHD2J5H2ndMlaljrUNaQDF9dOxIuFAYP1rE9wkXkg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.4.1.tgz", + "integrity": "sha512-Q9xk6yjEGIThjSD8zZegxd5tBRNHYd13GOIG0nLsanbTXATiPXCLyvlYEfvbR2ft6dlRsziQXfQGxAgv7zcMUA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.3.0.tgz", - "integrity": "sha512-RXqYhlZb9sx/xkUfYIZuEPn7sT0WgTxNOuEYQ7AGw3IMpP9QGVEDVsluc/GcNkM8NTJszeqk8AplJzI9lm7Jxw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.4.1.tgz", + "integrity": "sha512-KldENzMHtKYwsOSLThghOIdXOBEsfDuGSrxAZjMnimBiDKd3AE4JQ+Kv+gBD/x77WoV9xIPf25GXMWffXZ17BA==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.0", - "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "@prisma/fetch-engine": "6.3.0", - "@prisma/get-platform": "6.3.0" + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/fetch-engine": "6.4.1", + "@prisma/get-platform": "6.4.1" } }, "node_modules/@prisma/engines-version": { - "version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0.tgz", - "integrity": "sha512-R/ZcMuaWZT2UBmgX3Ko6PAV3f8//ZzsjRIG1eKqp3f2rqEqVtCv+mtzuH2rBPUC9ujJ5kCb9wwpxeyCkLcHVyA==", + "version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d.tgz", + "integrity": "sha512-Xq54qw55vaCGrGgIJqyDwOq0TtjZPJEWsbQAHugk99hpDf2jcEeQhUcF+yzEsSqegBaDNLA4IC8Nn34sXmkiTQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.3.0.tgz", - "integrity": "sha512-GBy0iT4f1mH31ePzfcpVSUa7JLRTeq4914FG2vR3LqDwRweSm4ja1o5flGDz+eVIa/BNYfkBvRRxv4D6ve6Eew==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.4.1.tgz", + "integrity": "sha512-uZ5hVeTmDspx7KcaRCNoXmcReOD+84nwlO2oFvQPRQh9xiFYnnUKDz7l9bLxp8t4+25CsaNlgrgilXKSQwrIGQ==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.0", - "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "@prisma/get-platform": "6.3.0" + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/get-platform": "6.4.1" } }, "node_modules/@prisma/get-platform": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.3.0.tgz", - "integrity": "sha512-V8zZ1d0xfyi6FjpNP4AcYuwSpGcdmu35OXWnTPm8IW594PYALzKXHwIa9+o0f+Lo9AecFWrwrwaoYe56UNfTtQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.4.1.tgz", + "integrity": "sha512-gXqZaDI5scDkBF8oza7fOD3Q3QMD0e0rBynlzDDZdTWbWmzjuW58PRZtj+jkvKje2+ZigCWkH8SsWZAsH6q1Yw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.0" + "@prisma/debug": "6.4.1" } }, "node_modules/@redis/bloom": { @@ -9046,18 +9044,15 @@ } }, "node_modules/@simplewebauthn/browser": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-9.0.1.tgz", - "integrity": "sha512-wD2WpbkaEP4170s13/HUxPcAV5y4ZXaKo1TfNklS5zDefPinIgXOpgz1kpEvobAsaLPa2KeH7AKKX/od1mrBJw==", - "license": "MIT", - "dependencies": { - "@simplewebauthn/types": "^9.0.1" - } + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.0.tgz", + "integrity": "sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==", + "license": "MIT" }, "node_modules/@simplewebauthn/server": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-9.0.3.tgz", - "integrity": "sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.1.1.tgz", + "integrity": "sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==", "license": "MIT", "dependencies": { "@hexagon/base64": "^1.1.27", @@ -9066,29 +9061,12 @@ "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "@simplewebauthn/types": "^9.0.1", - "cross-fetch": "^4.0.0" + "@peculiar/asn1-x509": "^2.3.8" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.0.0" } }, - "node_modules/@simplewebauthn/server/node_modules/cross-fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/@simplewebauthn/types": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-9.0.1.tgz", - "integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==", - "license": "MIT" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -10292,16 +10270,16 @@ } }, "node_modules/@trivago/prettier-plugin-sort-imports": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.1.tgz", - "integrity": "sha512-NDZndt0fmVThIx/8cExuJHLZagUVzfGCoVrwH9x6aZvwfBdkrDFTYujecek6X2WpG4uUFsVaPg5+aNQPSyjcmw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", + "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@babel/generator": "^7.26.2", - "@babel/parser": "^7.26.2", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "javascript-natural-sort": "^0.7.1", "lodash": "^4.17.21" }, @@ -10523,33 +10501,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/color": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/color/-/color-4.2.0.tgz", - "integrity": "sha512-6+xrIRImMtGAL2X3qYkd02Mgs+gFGs+WsK0b7VVMaO4mYRISwyTjcqNrO0mNSmYEoq++rSLDB2F5HDNmqfOe+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/color-convert": "*" - } - }, - "node_modules/@types/color-convert": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.4.tgz", - "integrity": "sha512-Ub1MmDdyZ7mX//g25uBAoH/mWGd9swVbt8BseymnaE18SU4po/PjmCrHxqIIRjBo3hV/vh1KGr0eMxUhp+t+dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/color-name": "^1.1.0" - } - }, - "node_modules/@types/color-name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.5.tgz", - "integrity": "sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -14116,16 +14067,16 @@ "license": "MIT" }, "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.0.tgz", + "integrity": "sha512-16BlyiuyLq3MLxpRWyOTiWsO3ii/eLQLJUQXBSNcxMBBSnyt1ee9YUdaozQp03ifwm5woztEZGDbk9RGVuCsdw==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "color-convert": "^3.0.1", + "color-string": "^2.0.0" }, "engines": { - "node": ">=12.5.0" + "node": ">=18" } }, "node_modules/color-convert": { @@ -14147,13 +14098,45 @@ "license": "MIT" }, "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.0.1.tgz", + "integrity": "sha512-5z9FbYTZPAo8iKsNEqRNv+OlpBbDcoE+SY9GjLfDUHEfcNNV7tS9eSAlFHEaub/r5tBL9LtskAeq1l9SaoZ5tQ==", "license": "MIT", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.0.tgz", + "integrity": "sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.0.1.tgz", + "integrity": "sha512-5kQah2eolfQV7HCrxtsBBArPfT5dwaKYMCXeMQsdRO7ihTO/cuNLGjd50ITCDn+ZU/YbS0Go64SjP9154eopxg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.0.tgz", + "integrity": "sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow==", + "license": "MIT", + "engines": { + "node": ">=12.20" } }, "node_modules/colord": { @@ -16594,7 +16577,7 @@ "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -16635,7 +16618,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" @@ -16880,42 +16863,26 @@ "license": "MIT" }, "node_modules/eslint-plugin-cypress": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.2.0.tgz", - "integrity": "sha512-HaxMz6BoU4ay+K4WrG9ZJC1NdX06FqSlAwtRDStjM0ORFT7zCNPNuRJ+kUPc17Rt2AMUBSqeD9L0zTR3uZhPpw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-4.1.0.tgz", + "integrity": "sha512-JhqkMY02mw74USwK9OFhectx3YSj6Co1NgWBxlGdKvlqiAp9vdEuQqt33DKGQFvvGS/NWtduuhWXWNnU29xDSg==", "dev": true, "license": "MIT", "dependencies": { - "globals": "^13.20.0" + "globals": "^15.11.0" }, "peerDependencies": { - "eslint": ">=7" + "eslint": ">=9" } }, "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-cypress/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -17009,9 +16976,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.2.tgz", - "integrity": "sha512-iOrFJfyLgRLIbWDLbbs3J4yrknvIB+uiZ8KGqGOEXTL7/BGuBMZLiIU9Q4Pm/VYJrHN4JqmMtwCSrAemHL2nFg==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.2.tgz", + "integrity": "sha512-0Z4DUklJrC+GHjCRXa7PYfPzWC15DaVnwaOYenpgXiCEijXPZkLKCms+rHhtoRcWccP7Z8DpOOaP1gc3P9oOwg==", "dev": true, "license": "MIT", "dependencies": { @@ -17023,7 +16990,7 @@ "node": ">= 18" }, "peerDependencies": { - "eslint": ">=6" + "eslint": ">=8" } }, "node_modules/eslint-scope": { @@ -26788,9 +26755,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", + "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "bin": { @@ -26869,14 +26836,16 @@ } }, "node_modules/prisma": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.3.0.tgz", - "integrity": "sha512-y+Zh3Qg+xGCWyyrNUUNaFW/OltaV/yXYuTa0WRgYkz5LGyifmAsgpv94I47+qGRocZrMGcbF2A/78/oO2zgifA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.4.1.tgz", + "integrity": "sha512-q2uJkgXnua/jj66mk6P9bX/zgYJFI/jn4Yp0aS6SPRrjH/n6VyOV7RDe1vHD0DX8Aanx4MvgmUPPoYnR6MJnPg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "6.3.0" + "@prisma/engines": "6.4.1", + "esbuild": ">=0.12 <1", + "esbuild-register": "3.6.0" }, "bin": { "prisma": "build/index.js" @@ -28621,21 +28590,6 @@ "integrity": "sha512-rijcxtwx2b4Bje3sqeIqw5EeW7UlOIC4YfOdwqIKacpvRQ/D78bWg/4/0m5e0U91oKvlGh7LlJuZCu07ISCC7w==", "license": "ISC" }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", diff --git a/package.json b/package.json index e42ea51d2..cec20d833 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.138.0", + "version": "2.145.1", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -15,7 +15,7 @@ "affected:test": "nx affected:test", "analyze:client": "nx run client:build:production --stats-json && webpack-bundle-analyzer -p 1234 dist/apps/client/en/stats.json", "angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng", - "build:production": "nx run api:copy-assets && nx run api:build:production && nx run client:copy-assets && nx run client:build:production && npm run replace-placeholders-in-build", + "build:production": "nx run api:copy-assets && nx run api:build:production && nx run client:copy-assets && nx run client:build:production && nx run ui:build-storybook && npm run replace-placeholders-in-build", "build:storybook": "nx run ui:build-storybook", "database:format-schema": "prisma format", "database:generate-typings": "prisma generate", @@ -87,9 +87,9 @@ "@nestjs/platform-express": "10.4.15", "@nestjs/schedule": "4.1.2", "@nestjs/serve-static": "4.0.2", - "@prisma/client": "6.3.0", - "@simplewebauthn/browser": "9.0.1", - "@simplewebauthn/server": "9.0.3", + "@prisma/client": "6.4.1", + "@simplewebauthn/browser": "13.1.0", + "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "5.4.0", "alphavantage": "2.2.0", "await-lock": "^2.2.2", @@ -106,7 +106,7 @@ "cheerio": "1.0.0", "class-transformer": "0.5.1", "class-validator": "0.14.1", - "color": "4.2.3", + "color": "5.0.0", "countries-and-timezones": "3.7.2", "countries-list": "3.1.1", "countup.js": "2.8.0", @@ -168,15 +168,13 @@ "@nx/web": "20.3.3", "@nx/workspace": "20.3.3", "@schematics/angular": "19.0.6", - "@simplewebauthn/types": "9.0.1", "@storybook/addon-essentials": "8.4.7", "@storybook/addon-interactions": "8.4.7", "@storybook/angular": "8.4.7", "@storybook/core-server": "8.4.7", - "@trivago/prettier-plugin-sort-imports": "5.2.1", + "@trivago/prettier-plugin-sort-imports": "5.2.2", "@types/big.js": "6.2.2", "@types/cache-manager": "4.0.6", - "@types/color": "4.2.0", "@types/google-spreadsheet": "3.1.5", "@types/jest": "29.5.13", "@types/lodash": "4.17.7", @@ -189,17 +187,17 @@ "cypress": "6.2.1", "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-cypress": "3.2.0", + "eslint-plugin-cypress": "4.1.0", "eslint-plugin-import": "2.31.0", - "eslint-plugin-storybook": "0.10.2", + "eslint-plugin-storybook": "0.11.2", "husky": "9.1.7", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jest-preset-angular": "14.4.2", "nx": "20.3.3", - "prettier": "3.4.2", + "prettier": "3.5.1", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.3.0", + "prisma": "6.4.1", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", diff --git a/prisma/migrations/20250222084717_added_cusip_to_symbol_profile/migration.sql b/prisma/migrations/20250222084717_added_cusip_to_symbol_profile/migration.sql new file mode 100644 index 000000000..b60cebef6 --- /dev/null +++ b/prisma/migrations/20250222084717_added_cusip_to_symbol_profile/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "SymbolProfile" ADD COLUMN "cusip" TEXT; + +-- CreateIndex +CREATE INDEX "SymbolProfile_cusip_idx" ON "SymbolProfile"("cusip"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index fbffa92c2..648c57fd5 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) +# It should be added in your version-control system (e.g., Git) provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 56174277b..a2d1d87bd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -174,6 +174,7 @@ model SymbolProfile { countries Json? createdAt DateTime @default(now()) currency String + cusip String? dataSource DataSource figi String? figiComposite String? @@ -197,6 +198,7 @@ model SymbolProfile { @@unique([dataSource, symbol]) @@index([assetClass]) @@index([currency]) + @@index([cusip]) @@index([dataSource]) @@index([isin]) @@index([name])