diff --git a/.config/prisma.ts b/.config/prisma.ts new file mode 100644 index 000000000..64691136c --- /dev/null +++ b/.config/prisma.ts @@ -0,0 +1,14 @@ +import { defineConfig } from '@prisma/config'; +import { config } from 'dotenv'; +import { expand } from 'dotenv-expand'; +import { join } from 'node:path'; + +expand(config({ quiet: true })); + +export default defineConfig({ + migrations: { + path: join(__dirname, '..', 'prisma', 'migrations'), + seed: `node ${join(__dirname, '..', 'prisma', 'seed.mts')}` + }, + schema: join(__dirname, '..', 'prisma', 'schema.prisma') +}); diff --git a/CHANGELOG.md b/CHANGELOG.md index dd841de15..52ed65258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,15 +7,211 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +#### Changed + +- Upgraded `envalid` from version `8.1.0` to `8.1.1` + +## 2.221.0 - 2025-12-01 + +### Changed + +- Refactored the API query parameters in various data provider services +- Extended the _Storybook_ stories of the portfolio proportion chart component by a story using percentage values +- Upgraded `@internationalized/number` from version `3.6.3` to `3.6.5` +- Upgraded `prettier` from version `3.7.2` to `3.7.3` + +### Fixed + +- Improved the country weightings in the _Financial Modeling Prep_ service +- Improved the search functionality by name in the _Financial Modeling Prep_ service +- Resolved an issue in the user endpoint where the list was returning empty in the admin control panel’s users section + +## 2.220.0 - 2025-11-29 + +### Changed + +- Restricted the asset profile data gathering on Sundays to only process outdated asset profiles +- Removed the _Cypress_ testing setup +- Eliminated `uuid` in favor of using `randomUUID` from `node:crypto` +- Upgraded `color` from version `5.0.0` to `5.0.3` +- Upgraded `prettier` from version `3.6.2` to `3.7.2` + +### Fixed + +- Fixed an issue with the exchange rate calculation when converting between derived currencies and their root currencies + +## 2.219.0 - 2025-11-23 + +### Added + +- Extended the user detail dialog of the admin control panel’s users section by the authentication method + +### Changed + +- Disabled the action to delete activities if the activities table is empty +- Improved the validation of the currency management in the admin control panel +- Improved the content of the pricing page +- Resolved the data source of the `GHOSTFOLIO` data provider in the export functionality +- Resolved the data source of the `GHOSTFOLIO` data provider in the import functionality +- Refreshed the cryptocurrencies list +- Improved the language localization for German (`de`) +- Upgraded `yahoo-finance2` from version `3.10.1` to `3.10.2` + +### Fixed + +- Fixed an issue with the edit of future activities (drafts) + +## 2.218.0 - 2025-11-20 + +### Added + +- Extended the accounts table menu with a _View Details_ item +- Extended the portfolio summary tab on the home page by percentage values (experimental) +- Added the _OSS Gallery_ logo to the logo carousel on the landing page + +### Changed + +- Improved the dynamic numerical precision for various values in the portfolio summary tab on the home page +- Upgraded `yahoo-finance2` from version `3.10.0` to `3.10.1` + +## 2.217.1 - 2025-11-16 + +### Added + +- Introduced support for automatically gathering required exchange rates, exposed as an environment variable (`ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES`) +- Added a blog post: _Black Weeks 2025_ + +### Changed + +- Refactored the get holding functionality in the portfolio service +- Changed the user data loading in the user detail dialog of the admin control panel’s users section to fetch data on demand +- Exposed the authentication with access token as an environment variable (`ENABLE_FEATURE_AUTH_TOKEN`) +- Improved the search functionality of the _Financial Modeling Prep_ service +- Improved the language localization for German (`de`) +- Upgraded `prisma` from version `6.18.0` to `6.19.0` + +### Todo + +- Rename the environment variable from `ENABLE_FEATURE_SOCIAL_LOGIN` to `ENABLE_FEATURE_AUTH_GOOGLE` + +## 2.216.0 - 2025-11-10 + +### Changed + +- Improved the language localization for Chinese (`zh`) +- Upgraded `chart.js` from version `4.5.0` to `4.5.1` +- Upgraded `svgmap` from version `2.12.2` to `2.14.0` + +## 2.215.0 - 2025-11-06 + +### Added + +- Added the endpoint `GET /api/v1/admin/user/:id` + +### Changed + +- Improved the _Self-Hosting_ section content for the _Compare with..._ concept on the Frequently Asked Questions (FAQ) page +- Improved the _Self-Hosting_ section content for the _Markets_ concept on the Frequently Asked Questions (FAQ) page +- Changed the build executor of the client from `@nx/angular:webpack-browser` to `@nx/angular:browser-esbuild` +- Refactored the app component to standalone +- Improved the language localization for German (`de`) +- Upgraded `@ionic/angular` from version `8.7.3` to `8.7.8` + +### Fixed + +- Fixed the style of the safe withdrawal rate selector in the _FIRE_ section (experimental) +- Assigned the `ADMIN` role to the first user signing up via a social login provider if no administrator existed +- Improved the table headers’ alignment in the platform management of the admin control panel +- Improved the table headers’ alignment in the tag management of the admin control panel + +## 2.214.0 - 2025-11-01 + +### Changed + +- Improved the icon of the _View Holding_ menu item in the activities table +- Ensured atomic data replacement during historical market data gathering +- Removed _Internet Identity_ as a social login provider +- Refreshed the cryptocurrencies list +- Upgraded `countries-list` from version `3.1.1` to `3.2.0` +- Upgraded `ng-extract-i18n-merge` from version `3.0.0` to `3.1.0` +- Upgraded `twitter-api-v2` from version `1.23.0` to `1.27.0` + +## 2.213.0 - 2025-10-30 + +### Added + +- Extended the activities table menu with a _View Holding_ item +- Added the error logging to the symbol lookup in the _Trackinsight_ data enhancer + +### Changed + +- Improved the icon of the holdings tab on the home page +- Improved the icon of the holdings tab on the home page for the _Zen Mode_ +- Improved the icon of the holdings tab in the account detail dialog +- Migrated the tags selector component in the holding detail dialog to form control +- Improved the language localization for German (`de`) +- Upgraded `nestjs` from version `11.1.3` to `11.1.8` + +## 2.212.0 - 2025-10-29 + +### Added + +- Added a close holding button to the holding detail dialog +- Added the _Sponsors_ section to the about page +- Extended the user detail dialog in the users section of the admin control panel + +### Changed + +- Refactored the generation of the holdings table in the _Copy AI prompt to clipboard for analysis_ action on the analysis page (experimental) +- Refactored the generation of the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action on the analysis page (experimental) +- Improved the usability of the user detail dialog in the users section of the admin control panel +- Improved the language localization for German (`de`) + +### Fixed + +- Ensured the locale is available in the settings dialog to customize the rule thresholds of the _X-ray_ page + +## 2.211.0 - 2025-10-25 + +### Added + +- Extended the export functionality by the user account’s performance calculation type +- Added the _SelfhostedHub_ logo to the logo carousel on the landing page +- Added a user detail dialog to the users section of the admin control panel + +### Changed + +- Localized the number formatting in the static portfolio analysis rule: _Liquidity_ (Buying Power) +- Moved the _Prisma Configuration File_ from `prisma.config.ts` to `.config/prisma.ts` +- Improved the language localization for German (`de`) +- Upgraded `prisma` from version `6.17.1` to `6.18.0` +- Upgraded `tablemark` from version `3.1.0` to `4.1.0` + +### Fixed + +- Fixed the style in the footer row of the accounts table +- Fixed the rendering of names and symbols for custom assets in the import activities dialog +- Fixed an issue with the market price in base currency during the portfolio snapshot calculation + +## 2.210.1 - 2025-10-22 + ### Added - Added support for data gathering by date range in the asset profile details dialog of the admin control panel ### Changed +- Extracted the portfolio filter form of the assistant to a reusable component - Formatted the holdings table in the _Copy AI prompt to clipboard for analysis_ action on the analysis page (experimental) -- Formatted the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action of the analysis page (experimental) +- Formatted the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action on the analysis page (experimental) +- Reverted the explicit configuration of the _Redis_ address family in the job queue module - Improved the language localization for German (`de`) +- Upgraded `ioredis` from version `5.6.1` to `5.8.2` + +### Fixed + +- Fixed the enter key press to submit the form of the login with access token dialog +- Fixed an issue in the database seeding process caused by unresolved environment variables in `DATABASE_URL` ## 2.209.0 - 2025-10-18 @@ -59,7 +255,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the deprecated endpoint `GET api/v1/portfolio/position/:dataSource/:symbol` - Removed the deprecated endpoint `PUT api/v1/portfolio/position/:dataSource/:symbol/tags` - Improved the language localization for German (`de`) -- Upgraded `prisma` from version `6.16.1` to `6.16.3` +- Upgraded `prisma` from version `6.16.1` to `6.17.1` ### Fixed @@ -2064,7 +2260,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue in the portfolio summary with the currency conversion of fees -- Fixed an issue in the the search for a holding +- Fixed an issue in the search for a holding - Removed the show condition of the experimental features setting in the user settings ## 2.95.0 - 2024-07-12 diff --git a/Dockerfile b/Dockerfile index be1bb53ea..5beaf6e03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,11 +13,11 @@ RUN apt-get update && apt-get install -y --no-install-suggests \ # Only add basic files without the application itself to avoid rebuilding # layers when files (package.json etc.) have not changed +COPY ./.config .config/ COPY ./CHANGELOG.md CHANGELOG.md COPY ./LICENSE LICENSE COPY ./package.json package.json COPY ./package-lock.json package-lock.json -COPY ./prisma.config.ts prisma.config.ts COPY ./prisma/schema.prisma prisma/ RUN npm install @@ -44,7 +44,7 @@ WORKDIR /ghostfolio/dist/apps/api COPY ./package-lock.json /ghostfolio/dist/apps/api/ RUN npm install -COPY prisma.config.ts /ghostfolio/dist/apps/api/ +COPY .config /ghostfolio/dist/apps/api/.config/ COPY prisma /ghostfolio/dist/apps/api/prisma/ # Overwrite the generated package.json with the original one to ensure having diff --git a/README.md b/README.md index 82d5710d1..1a5cc6e95 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,18 @@ Ghostfolio is **100% free** and **open source**. We encourage and support an act Not sure what to work on? We have [some ideas](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22%20no%3Aassignee), even for [newcomers](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%20no%3Aassignee). Please join the Ghostfolio [Slack](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) channel or post to [@ghostfolio\_](https://x.com/ghostfolio_) on _X_. We would love to hear from you. -If you like to support this project, get [**Ghostfolio Premium**](https://ghostfol.io/en/pricing) or [**Buy me a coffee**](https://www.buymeacoffee.com/ghostfolio). +If you like to support this project, become a [**Sponsor**](https://github.com/sponsors/ghostfolio), get [**Ghostfolio Premium**](https://ghostfol.io/en/pricing) or [**Buy me a coffee**](https://www.buymeacoffee.com/ghostfolio). + +## Sponsors + +
+

+ Browser testing via
+ + LambdaTest Logo + +

+
## Analytics diff --git a/apps/api/src/app/access/access.controller.ts b/apps/api/src/app/access/access.controller.ts index cb1e2d4af..5056a6d71 100644 --- a/apps/api/src/app/access/access.controller.ts +++ b/apps/api/src/app/access/access.controller.ts @@ -1,6 +1,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { CreateAccessDto, UpdateAccessDto } from '@ghostfolio/common/dtos'; import { Access } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -23,8 +24,6 @@ import { Access as AccessModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { AccessService } from './access.service'; -import { CreateAccessDto } from './create-access.dto'; -import { UpdateAccessDto } from './update-access.dto'; @Controller('access') export class AccessController { diff --git a/apps/api/src/app/account-balance/account-balance.controller.ts b/apps/api/src/app/account-balance/account-balance.controller.ts index bc454c5ab..baf002bd3 100644 --- a/apps/api/src/app/account-balance/account-balance.controller.ts +++ b/apps/api/src/app/account-balance/account-balance.controller.ts @@ -1,6 +1,7 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos'; import { permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -20,7 +21,6 @@ import { AccountBalance } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { AccountBalanceService } from './account-balance.service'; -import { CreateAccountBalanceDto } from './create-account-balance.dto'; @Controller('account-balance') export class AccountBalanceController { diff --git a/apps/api/src/app/account-balance/account-balance.service.ts b/apps/api/src/app/account-balance/account-balance.service.ts index 10353f4ca..321624003 100644 --- a/apps/api/src/app/account-balance/account-balance.service.ts +++ b/apps/api/src/app/account-balance/account-balance.service.ts @@ -2,6 +2,7 @@ import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed. import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; +import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos'; import { DATE_FORMAT, getSum, resetHours } from '@ghostfolio/common/helper'; import { AccountBalancesResponse, @@ -15,8 +16,6 @@ import { AccountBalance, Prisma } from '@prisma/client'; import { Big } from 'big.js'; import { format, parseISO } from 'date-fns'; -import { CreateAccountBalanceDto } from './create-account-balance.dto'; - @Injectable() export class AccountBalanceService { public constructor( diff --git a/apps/api/src/app/account/account.controller.ts b/apps/api/src/app/account/account.controller.ts index 7b24ccdcb..542b199fd 100644 --- a/apps/api/src/app/account/account.controller.ts +++ b/apps/api/src/app/account/account.controller.ts @@ -7,15 +7,18 @@ import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interce import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; +import { + CreateAccountDto, + TransferBalanceDto, + UpdateAccountDto +} from '@ghostfolio/common/dtos'; import { AccountBalancesResponse, + AccountResponse, AccountsResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; -import type { - AccountWithValue, - RequestWithUser -} from '@ghostfolio/common/types'; +import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -38,9 +41,6 @@ import { Account as AccountModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { AccountService } from './account.service'; -import { CreateAccountDto } from './create-account.dto'; -import { TransferBalanceDto } from './transfer-balance.dto'; -import { UpdateAccountDto } from './update-account.dto'; @Controller('account') export class AccountController { @@ -114,7 +114,7 @@ export class AccountController { public async getAccountById( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Param('id') id: string - ): Promise { + ): Promise { const impersonationUserId = await this.impersonationService.validateImpersonationId(impersonationId); diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index d7c4c5d3d..24467c732 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -4,7 +4,6 @@ import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interce import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service'; import { DemoService } from '@ghostfolio/api/services/demo/demo.service'; -import { PropertyDto } from '@ghostfolio/api/services/property/property.dto'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { @@ -13,11 +12,16 @@ import { GATHER_ASSET_PROFILE_PROCESS_JOB_NAME, GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS } from '@ghostfolio/common/config'; +import { + UpdateAssetProfileDto, + UpdatePropertyDto +} from '@ghostfolio/common/dtos'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { AdminData, AdminMarketData, - AdminUsers, + AdminUserResponse, + AdminUsersResponse, EnhancedSymbolProfile, ScraperConfiguration } from '@ghostfolio/common/interfaces'; @@ -51,7 +55,6 @@ import { isDate, parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { AdminService } from './admin.service'; -import { UpdateAssetProfileDto } from './update-asset-profile.dto'; @Controller('admin') export class AdminController { @@ -90,7 +93,7 @@ export class AdminController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async gatherMax(): Promise { const assetProfileIdentifiers = - await this.dataGatheringService.getAllActiveAssetProfileIdentifiers(); + await this.dataGatheringService.getActiveAssetProfileIdentifiers(); await this.dataGatheringService.addJobsToQueue( assetProfileIdentifiers.map(({ dataSource, symbol }) => { @@ -117,7 +120,7 @@ export class AdminController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async gatherProfileData(): Promise { const assetProfileIdentifiers = - await this.dataGatheringService.getAllActiveAssetProfileIdentifiers(); + await this.dataGatheringService.getActiveAssetProfileIdentifiers(); await this.dataGatheringService.addJobsToQueue( assetProfileIdentifiers.map(({ dataSource, symbol }) => { @@ -304,7 +307,7 @@ export class AdminController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async updateProperty( @Param('key') key: string, - @Body() data: PropertyDto + @Body() data: UpdatePropertyDto ) { return this.adminService.putSetting(key, data.value); } @@ -315,10 +318,17 @@ export class AdminController { public async getUsers( @Query('skip') skip?: number, @Query('take') take?: number - ): Promise { + ): Promise { return this.adminService.getUsers({ skip: isNaN(skip) ? undefined : skip, take: isNaN(take) ? undefined : take }); } + + @Get('user/:id') + @HasPermission(permissions.accessAdminControl) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getUser(@Param('id') id: string): Promise { + return this.adminService.getUser(id); + } } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 11f6f0599..705085a48 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -23,7 +23,8 @@ import { AdminMarketData, AdminMarketDataDetails, AdminMarketDataItem, - AdminUsers, + AdminUserResponse, + AdminUsersResponse, AssetProfileIdentifier, EnhancedSymbolProfile, Filter @@ -35,7 +36,8 @@ import { BadRequestException, HttpException, Injectable, - Logger + Logger, + NotFoundException } from '@nestjs/common'; import { AssetClass, @@ -507,16 +509,31 @@ export class AdminService { }; } + public async getUser(id: string): Promise { + const [user] = await this.getUsersWithAnalytics({ + where: { id } + }); + + if (!user) { + throw new NotFoundException(`User with ID ${id} not found`); + } + + return user; + } + public async getUsers({ skip, take = Number.MAX_SAFE_INTEGER }: { skip?: number; take?: number; - }): Promise { + }): Promise { const [count, users] = await Promise.all([ this.countUsersWithAnalytics(), - this.getUsersWithAnalytics({ skip, take }) + this.getUsersWithAnalytics({ + skip, + take + }) ]); return { count, users }; @@ -814,17 +831,17 @@ export class AdminService { private async getUsersWithAnalytics({ skip, - take + take, + where }: { skip?: number; take?: number; - }): Promise { + where?: Prisma.UserWhereInput; + }): Promise { let orderBy: Prisma.Enumerable = [ { createdAt: 'desc' } ]; - let where: Prisma.UserWhereInput; - if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { orderBy = [ { @@ -834,11 +851,19 @@ export class AdminService { } ]; - where = { - NOT: { - analytics: null - } + const noAnalyticsCondition: Prisma.UserWhereInput['NOT'] = { + analytics: null }; + + if (where) { + if (where.NOT) { + where.NOT = { ...where.NOT, ...noAnalyticsCondition }; + } else { + where.NOT = noAnalyticsCondition; + } + } else { + where = { NOT: noAnalyticsCondition }; + } } const usersWithAnalytics = await this.prismaService.user.findMany({ @@ -860,6 +885,7 @@ export class AdminService { }, createdAt: true, id: true, + provider: true, role: true, subscriptions: { orderBy: { @@ -876,7 +902,7 @@ export class AdminService { }); return usersWithAnalytics.map( - ({ _count, analytics, createdAt, id, role, subscriptions }) => { + ({ _count, analytics, createdAt, id, provider, role, subscriptions }) => { const daysSinceRegistration = differenceInDays(new Date(), createdAt) + 1; const engagement = analytics @@ -893,6 +919,7 @@ export class AdminService { createdAt, engagement, id, + provider, role, subscription, accountCount: _count.accounts || 0, diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 86ceede28..5ec148558 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -71,7 +71,6 @@ import { UserModule } from './user/user.module'; BullModule.forRoot({ redis: { db: parseInt(process.env.REDIS_DB ?? '0', 10), - family: 0, host: process.env.REDIS_HOST, password: process.env.REDIS_PASSWORD, port: parseInt(process.env.REDIS_PORT ?? '6379', 10) diff --git a/apps/api/src/app/asset/asset.controller.ts b/apps/api/src/app/asset/asset.controller.ts index 828320f82..3b2031084 100644 --- a/apps/api/src/app/asset/asset.controller.ts +++ b/apps/api/src/app/asset/asset.controller.ts @@ -1,7 +1,7 @@ import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; 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 type { AdminMarketDataDetails } from '@ghostfolio/common/interfaces'; +import type { AssetResponse } from '@ghostfolio/common/interfaces'; import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; import { DataSource } from '@prisma/client'; @@ -17,7 +17,7 @@ export class AssetController { public async getAsset( @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string - ): Promise { + ): Promise { const { assetProfile, marketData } = await this.adminService.getMarketDataBySymbol({ dataSource, symbol }); diff --git a/apps/api/src/app/auth/auth.controller.ts b/apps/api/src/app/auth/auth.controller.ts index 13d8e37f6..b45e7b97b 100644 --- a/apps/api/src/app/auth/auth.controller.ts +++ b/apps/api/src/app/auth/auth.controller.ts @@ -2,7 +2,11 @@ import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; -import { OAuthResponse } from '@ghostfolio/common/interfaces'; +import { + AssertionCredentialJSON, + AttestationCredentialJSON, + OAuthResponse +} from '@ghostfolio/common/interfaces'; import { Body, @@ -22,10 +26,6 @@ import { Request, Response } from 'express'; import { getReasonPhrase, StatusCodes } from 'http-status-codes'; import { AuthService } from './auth.service'; -import { - AssertionCredentialJSON, - AttestationCredentialJSON -} from './interfaces/simplewebauthn'; @Controller('auth') export class AuthController { @@ -102,23 +102,6 @@ export class AuthController { } } - @Post('internet-identity') - public async internetIdentityLogin( - @Body() body: { principalId: string } - ): Promise { - try { - const authToken = await this.authService.validateInternetIdentityLogin( - body.principalId - ); - return { authToken }; - } catch { - throw new HttpException( - getReasonPhrase(StatusCodes.FORBIDDEN), - StatusCodes.FORBIDDEN - ); - } - } - @Get('webauthn/generate-registration-options') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async generateRegistrationOptions() { diff --git a/apps/api/src/app/auth/auth.service.ts b/apps/api/src/app/auth/auth.service.ts index 85f0d3fb4..6bd691c08 100644 --- a/apps/api/src/app/auth/auth.service.ts +++ b/apps/api/src/app/auth/auth.service.ts @@ -4,7 +4,6 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; -import { Provider } from '@prisma/client'; import { ValidateOAuthLoginParams } from './interfaces/interfaces'; @@ -48,42 +47,6 @@ export class AuthService { }); } - public async validateInternetIdentityLogin(principalId: string) { - try { - const provider: Provider = 'INTERNET_IDENTITY'; - - let [user] = await this.userService.users({ - where: { provider, thirdPartyId: principalId } - }); - - if (!user) { - const isUserSignupEnabled = - await this.propertyService.isUserSignupEnabled(); - - if (!isUserSignupEnabled || true) { - throw new Error('Sign up forbidden'); - } - - // Create new user if not found - user = await this.userService.createUser({ - data: { - provider, - thirdPartyId: principalId - } - }); - } - - return this.jwtService.sign({ - id: user.id - }); - } catch (error) { - throw new InternalServerErrorException( - 'validateInternetIdentityLogin', - error.message - ); - } - } - public async validateOAuthLogin({ provider, thirdPartyId diff --git a/apps/api/src/app/auth/google.strategy.ts b/apps/api/src/app/auth/google.strategy.ts index 02f82a7a8..3e4b4ca0d 100644 --- a/apps/api/src/app/auth/google.strategy.ts +++ b/apps/api/src/app/auth/google.strategy.ts @@ -3,6 +3,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { Injectable, Logger } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Provider } from '@prisma/client'; +import { DoneCallback } from 'passport'; import { Profile, Strategy } from 'passport-google-oauth20'; import { AuthService } from './auth.service'; @@ -29,7 +30,7 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { _token: string, _refreshToken: string, profile: Profile, - done: Function + done: DoneCallback ) { try { const jwt = await this.authService.validateOAuthLogin({ diff --git a/apps/api/src/app/auth/interfaces/interfaces.ts b/apps/api/src/app/auth/interfaces/interfaces.ts index 45415355e..4fdcc25b5 100644 --- a/apps/api/src/app/auth/interfaces/interfaces.ts +++ b/apps/api/src/app/auth/interfaces/interfaces.ts @@ -1,4 +1,4 @@ -import { AuthDeviceDto } from '@ghostfolio/api/app/auth-device/auth-device.dto'; +import { AuthDeviceDto } from '@ghostfolio/common/dtos'; import { Provider } from '@prisma/client'; diff --git a/apps/api/src/app/auth/web-auth.service.ts b/apps/api/src/app/auth/web-auth.service.ts index d14ef7798..6cffcd244 100644 --- a/apps/api/src/app/auth/web-auth.service.ts +++ b/apps/api/src/app/auth/web-auth.service.ts @@ -1,7 +1,11 @@ -import { AuthDeviceDto } from '@ghostfolio/api/app/auth-device/auth-device.dto'; import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { AuthDeviceDto } from '@ghostfolio/common/dtos'; +import { + AssertionCredentialJSON, + AttestationCredentialJSON +} from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -27,11 +31,6 @@ import { import { isoBase64URL, isoUint8Array } from '@simplewebauthn/server/helpers'; import ms from 'ms'; -import { - AssertionCredentialJSON, - AttestationCredentialJSON -} from './interfaces/simplewebauthn'; - @Injectable() export class WebAuthService { public constructor( diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts index d1e1b413f..d07768d69 100644 --- a/apps/api/src/app/endpoints/ai/ai.service.ts +++ b/apps/api/src/app/endpoints/ai/ai.service.ts @@ -10,10 +10,31 @@ import type { AiPromptMode } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { generateText } from 'ai'; -import tablemark, { ColumnDescriptor } from 'tablemark'; +import type { ColumnDescriptor } from 'tablemark'; @Injectable() export class AiService { + private static readonly HOLDINGS_TABLE_COLUMN_DEFINITIONS: ({ + key: + | 'ALLOCATION_PERCENTAGE' + | 'ASSET_CLASS' + | 'ASSET_SUB_CLASS' + | 'CURRENCY' + | 'NAME' + | 'SYMBOL'; + } & ColumnDescriptor)[] = [ + { key: 'NAME', name: 'Name' }, + { key: 'SYMBOL', name: 'Symbol' }, + { key: 'CURRENCY', name: 'Currency' }, + { key: 'ASSET_CLASS', name: 'Asset Class' }, + { key: 'ASSET_SUB_CLASS', name: 'Asset Sub Class' }, + { + align: 'right', + key: 'ALLOCATION_PERCENTAGE', + name: 'Allocation in Percentage' + } + ]; + public constructor( private readonly portfolioService: PortfolioService, private readonly propertyService: PropertyService @@ -59,14 +80,10 @@ export class AiService { userId }); - const holdingsTableColumns: ColumnDescriptor[] = [ - { name: 'Name' }, - { name: 'Symbol' }, - { name: 'Currency' }, - { name: 'Asset Class' }, - { name: 'Asset Sub Class' }, - { align: 'right', name: 'Allocation in Percentage' } - ]; + const holdingsTableColumns: ColumnDescriptor[] = + AiService.HOLDINGS_TABLE_COLUMN_DEFINITIONS.map(({ align, name }) => { + return { name, align: align ?? 'left' }; + }); const holdingsTableRows = Object.values(holdings) .sort((a, b) => { @@ -78,20 +95,55 @@ export class AiService { assetClass, assetSubClass, currency, - name, + name: label, symbol }) => { - return { - Name: name, - Symbol: symbol, - Currency: currency, - 'Asset Class': assetClass ?? '', - 'Asset Sub Class': assetSubClass ?? '', - 'Allocation in Percentage': `${(allocationInPercentage * 100).toFixed(3)}%` - }; + return AiService.HOLDINGS_TABLE_COLUMN_DEFINITIONS.reduce( + (row, { key, name }) => { + switch (key) { + case 'ALLOCATION_PERCENTAGE': + row[name] = `${(allocationInPercentage * 100).toFixed(3)}%`; + break; + + case 'ASSET_CLASS': + row[name] = assetClass ?? ''; + break; + + case 'ASSET_SUB_CLASS': + row[name] = assetSubClass ?? ''; + break; + + case 'CURRENCY': + row[name] = currency; + break; + + case 'NAME': + row[name] = label; + break; + + case 'SYMBOL': + row[name] = symbol; + break; + + default: + row[name] = ''; + break; + } + + return row; + }, + {} as Record + ); } ); + // Dynamic import to load ESM module from CommonJS context + // eslint-disable-next-line @typescript-eslint/no-implied-eval + const dynamicImport = new Function('s', 'return import(s)') as ( + s: string + ) => Promise; + const { tablemark } = await dynamicImport('tablemark'); + const holdingsTableString = tablemark(holdingsTableRows, { columns: holdingsTableColumns }); 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 ac5881c4d..d088bf3ac 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 @@ -8,7 +8,6 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { @@ -18,6 +17,7 @@ import { import { PROPERTY_DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER_MAX_REQUESTS } from '@ghostfolio/common/config'; import { DataProviderGhostfolioAssetProfileResponse, + DataProviderHistoricalResponse, DataProviderInfo, DividendsResponse, HistoricalResponse, @@ -114,7 +114,7 @@ export class GhostfolioService { try { const promises: Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }>[] = []; for (const dataProviderService of this.getDataProviderServices()) { @@ -156,7 +156,7 @@ export class GhostfolioService { try { const promises: Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }>[] = []; for (const dataProviderService of this.getDataProviderServices()) { 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 4843536da..987d34918 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 @@ -10,6 +10,7 @@ import { ghostfolioFearAndGreedIndexSymbolCryptocurrencies, ghostfolioFearAndGreedIndexSymbolStocks } from '@ghostfolio/common/config'; +import { UpdateBulkMarketDataDto } from '@ghostfolio/common/dtos'; import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; import { MarketDataDetailsResponse, @@ -35,8 +36,6 @@ import { DataSource, Prisma } from '@prisma/client'; import { parseISO } from 'date-fns'; import { getReasonPhrase, StatusCodes } from 'http-status-codes'; -import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto'; - @Controller('market-data') export class MarketDataController { public constructor( diff --git a/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts b/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts deleted file mode 100644 index d07b189b2..000000000 --- a/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Type } from 'class-transformer'; -import { - ArrayNotEmpty, - IsArray, - IsISO8601, - IsNumber, - IsOptional -} from 'class-validator'; - -export class UpdateBulkMarketDataDto { - @ArrayNotEmpty() - @IsArray() - @Type(() => UpdateMarketDataDto) - marketData: UpdateMarketDataDto[]; -} - -class UpdateMarketDataDto { - @IsISO8601() - @IsOptional() - date?: string; - - @IsNumber() - marketPrice: number; -} diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index b09ced4fb..b4ecd37ba 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -82,7 +82,6 @@ export class PublicController { ]); const { activities } = await this.orderService.getOrders({ - includeDrafts: false, sortColumn: 'date', sortDirection: 'desc', take: 10, diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts index 359a29531..e7e05330f 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts @@ -116,6 +116,10 @@ export class SitemapService { { languageCode: 'en', routerLink: ['2025', '09', 'hacktoberfest-2025'] + }, + { + languageCode: 'en', + routerLink: ['2025', '11', 'black-weeks-2025'] } ] .map(({ languageCode, routerLink }) => { diff --git a/apps/api/src/app/endpoints/tags/tags.controller.ts b/apps/api/src/app/endpoints/tags/tags.controller.ts index bf216bb21..925e1e0ed 100644 --- a/apps/api/src/app/endpoints/tags/tags.controller.ts +++ b/apps/api/src/app/endpoints/tags/tags.controller.ts @@ -1,6 +1,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { TagService } from '@ghostfolio/api/services/tag/tag.service'; +import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -21,9 +22,6 @@ import { AuthGuard } from '@nestjs/passport'; import { Tag } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { CreateTagDto } from './create-tag.dto'; -import { UpdateTagDto } from './update-tag.dto'; - @Controller('tags') export class TagsController { public constructor( diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts index 2a8ea9875..78693239a 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts @@ -4,6 +4,7 @@ import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interce import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; +import { CreateWatchlistItemDto } from '@ghostfolio/common/dtos'; import { WatchlistResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -26,7 +27,6 @@ import { AuthGuard } from '@nestjs/passport'; import { DataSource } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { CreateWatchlistItemDto } from './create-watchlist-item.dto'; import { WatchlistService } from './watchlist.service'; @Controller('watchlist') diff --git a/apps/api/src/app/exchange-rate/exchange-rate.controller.ts b/apps/api/src/app/exchange-rate/exchange-rate.controller.ts index a5b2823d5..239b4b27a 100644 --- a/apps/api/src/app/exchange-rate/exchange-rate.controller.ts +++ b/apps/api/src/app/exchange-rate/exchange-rate.controller.ts @@ -1,5 +1,5 @@ import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderHistoricalResponse } from '@ghostfolio/common/interfaces'; import { Controller, @@ -25,7 +25,7 @@ export class ExchangeRateController { public async getExchangeRate( @Param('dateString') dateString: string, @Param('symbol') symbol: string - ): Promise { + ): Promise { const date = parseISO(dateString); const exchangeRate = await this.exchangeRateService.getExchangeRate({ diff --git a/apps/api/src/app/export/export.controller.ts b/apps/api/src/app/export/export.controller.ts index 8fa2baa43..6fda8f17f 100644 --- a/apps/api/src/app/export/export.controller.ts +++ b/apps/api/src/app/export/export.controller.ts @@ -1,7 +1,8 @@ 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 { Export } from '@ghostfolio/common/interfaces'; +import { ExportResponse } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -28,6 +29,7 @@ export class ExportController { @Get() @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInRequestInterceptor) + @UseInterceptors(TransformDataSourceInResponseInterceptor) public async export( @Query('accounts') filterByAccounts?: string, @Query('activityIds') filterByActivityIds?: string, @@ -35,7 +37,7 @@ export class ExportController { @Query('dataSource') filterByDataSource?: string, @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string - ): Promise { + ): Promise { const activityIds = filterByActivityIds?.split(',') ?? []; const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, @@ -48,8 +50,8 @@ export class ExportController { return this.exportService.export({ activityIds, filters, - userCurrency: this.request.user.settings.settings.baseCurrency, - userId: this.request.user.id + userId: this.request.user.id, + userSettings: this.request.user.settings.settings }); } } diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 7d78bdf22..d07b199be 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -3,7 +3,11 @@ import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { environment } from '@ghostfolio/api/environments/environment'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { TagService } from '@ghostfolio/api/services/tag/tag.service'; -import { Filter, Export } from '@ghostfolio/common/interfaces'; +import { + ExportResponse, + Filter, + UserSettings +} from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { Platform, Prisma } from '@prisma/client'; @@ -21,14 +25,14 @@ export class ExportService { public async export({ activityIds, filters, - userCurrency, - userId + userId, + userSettings }: { activityIds?: string[]; filters?: Filter[]; - userCurrency: string; userId: string; - }): Promise { + userSettings: UserSettings; + }): Promise { const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => { return type; }); @@ -36,11 +40,11 @@ export class ExportService { let { activities } = await this.orderService.getOrders({ filters, - userCurrency, userId, includeDrafts: true, sortColumn: 'date', sortDirection: 'asc', + userCurrency: userSettings?.baseCurrency, withExcludedAccountsAndActivities: true }); @@ -244,7 +248,10 @@ export class ExportService { } ), user: { - settings: { currency: userCurrency } + settings: { + currency: userSettings?.baseCurrency, + performanceCalculationType: userSettings?.performanceCalculationType + } } }; } diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts index 330bc52f6..bf45c7cda 100644 --- a/apps/api/src/app/import/import-data.dto.ts +++ b/apps/api/src/app/import/import-data.dto.ts @@ -1,12 +1,13 @@ -import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; +import { + CreateAccountWithBalancesDto, + CreateAssetProfileWithMarketDataDto, + CreateOrderDto, + CreateTagDto +} from '@ghostfolio/common/dtos'; import { Type } from 'class-transformer'; import { IsArray, IsOptional, ValidateNested } from 'class-validator'; -import { CreateTagDto } from '../endpoints/tags/create-tag.dto'; -import { CreateAccountWithBalancesDto } from './create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from './create-asset-profile-with-market-data.dto'; - export class ImportDataDto { @IsArray() @IsOptional() diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 2725747aa..2deef1c44 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -1,10 +1,4 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; -import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; -import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; -import { - Activity, - ActivityError -} from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; @@ -15,11 +9,20 @@ import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathe import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { DATA_GATHERING_QUEUE_PRIORITY_HIGH } from '@ghostfolio/common/config'; +import { + CreateAssetProfileDto, + CreateAccountDto, + CreateOrderDto +} from '@ghostfolio/common/dtos'; import { getAssetProfileIdentifier, parseDate } from '@ghostfolio/common/helper'; -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; +import { + Activity, + ActivityError, + AssetProfileIdentifier +} from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { AccountWithPlatform, @@ -32,9 +35,8 @@ import { DataSource, Prisma, SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; import { endOfToday, isAfter, isSameSecond, parseISO } from 'date-fns'; import { omit, uniqBy } from 'lodash'; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'node:crypto'; -import { CreateAssetProfileDto } from '../admin/create-asset-profile.dto'; import { ImportDataDto } from './import-data.dto'; @Injectable() @@ -58,13 +60,18 @@ export class ImportService { userId }: AssetProfileIdentifier & { userId: string }): Promise { try { - const { activities, firstBuyDate, historicalData } = - await this.portfolioService.getHolding({ - dataSource, - symbol, - userId, - impersonationId: undefined - }); + const holding = await this.portfolioService.getHolding({ + dataSource, + symbol, + userId, + impersonationId: undefined + }); + + if (!holding) { + return []; + } + + const { activities, firstBuyDate, historicalData } = holding; const [[assetProfile], dividends] = await Promise.all([ this.symbolProfileService.getSymbolProfiles([ @@ -270,7 +277,7 @@ export class ImportService { // Asset profile belongs to a different user if (existingAssetProfile) { - const symbol = uuidv4(); + const symbol = randomUUID(); assetProfileSymbolMapping[assetProfile.symbol] = symbol; assetProfile.symbol = symbol; } @@ -489,7 +496,7 @@ export class ImportService { accountId: validatedAccount?.id, accountUserId: undefined, createdAt: new Date(), - id: uuidv4(), + id: randomUUID(), isDraft: isAfter(date, endOfToday()), SymbolProfile: { assetClass, @@ -539,6 +546,7 @@ export class ImportService { connectOrCreate: { create: { dataSource, + name, symbol, currency: assetProfile.currency, userId: dataSource === 'MANUAL' ? user.id : undefined @@ -746,10 +754,19 @@ export class ImportService { if (['FEE', 'INTEREST', 'LIABILITY'].includes(type)) { // Skip asset profile validation for FEE, INTEREST, and LIABILITY // as these activity types don't require asset profiles + const assetProfileInImport = assetProfilesWithMarketDataDto?.find( + (profile) => { + return ( + profile.dataSource === dataSource && profile.symbol === symbol + ); + } + ); + assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })] = { currency, dataSource, - symbol + symbol, + name: assetProfileInImport?.name }; continue; diff --git a/apps/api/src/app/info/info.controller.ts b/apps/api/src/app/info/info.controller.ts index 67d4101a3..7011713dd 100644 --- a/apps/api/src/app/info/info.controller.ts +++ b/apps/api/src/app/info/info.controller.ts @@ -1,5 +1,5 @@ import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; -import { InfoItem } from '@ghostfolio/common/interfaces'; +import { InfoResponse } from '@ghostfolio/common/interfaces'; import { Controller, Get, UseInterceptors } from '@nestjs/common'; @@ -11,7 +11,7 @@ export class InfoController { @Get() @UseInterceptors(TransformDataSourceInResponseInterceptor) - public async getInfo(): Promise { + public async getInfo(): Promise { return this.infoService.get(); } } diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index c31f601e3..634fc959c 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -51,6 +51,14 @@ export class InfoService { const globalPermissions: string[] = []; + if (this.configurationService.get('ENABLE_FEATURE_AUTH_GOOGLE')) { + globalPermissions.push(permissions.enableAuthGoogle); + } + + if (this.configurationService.get('ENABLE_FEATURE_AUTH_TOKEN')) { + globalPermissions.push(permissions.enableAuthToken); + } + if (this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { info.fearAndGreedDataSource = encodeDataSource( @@ -70,10 +78,6 @@ export class InfoService { ); } - if (this.configurationService.get('ENABLE_FEATURE_SOCIAL_LOGIN')) { - globalPermissions.push(permissions.enableSocialLogin); - } - if (this.configurationService.get('ENABLE_FEATURE_STATISTICS')) { globalPermissions.push(permissions.enableStatistics); } diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index ffed8ac2e..73c295f1b 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -11,6 +11,11 @@ import { DATA_GATHERING_QUEUE_PRIORITY_HIGH, HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; +import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos'; +import { + ActivitiesResponse, + ActivityResponse +} from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { DateRange, RequestWithUser } from '@ghostfolio/common/types'; @@ -35,10 +40,7 @@ import { Order as OrderModel, Prisma } from '@prisma/client'; import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { CreateOrderDto } from './create-order.dto'; -import { Activities, Activity } from './interfaces/activities.interface'; import { OrderService } from './order.service'; -import { UpdateOrderDto } from './update-order.dto'; @Controller('order') export class OrderController { @@ -113,7 +115,7 @@ export class OrderController { @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string, @Query('take') take?: number - ): Promise { + ): Promise { let endDate: Date; let startDate: Date; @@ -157,13 +159,14 @@ export class OrderController { public async getOrderById( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Param('id') id: string - ): Promise { + ): Promise { const impersonationUserId = await this.impersonationService.validateImpersonationId(impersonationId); const userCurrency = this.request.user.settings.settings.baseCurrency; const { activities } = await this.orderService.getOrders({ userCurrency, + includeDrafts: true, userId: impersonationUserId || this.request.user.id, withExcludedAccountsAndActivities: true }); diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 11579bbf1..001d43b7a 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -1,4 +1,5 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; +import { AssetProfileChangedEvent } from '@ghostfolio/api/events/asset-profile-changed.event'; import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.event'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; @@ -14,6 +15,7 @@ import { } from '@ghostfolio/common/config'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { + ActivitiesResponse, AssetProfileIdentifier, EnhancedSymbolProfile, Filter @@ -35,9 +37,7 @@ import { Big } from 'big.js'; import { isUUID } from 'class-validator'; import { endOfToday, isAfter } from 'date-fns'; import { groupBy, uniqBy } from 'lodash'; -import { v4 as uuidv4 } from 'uuid'; - -import { Activities } from './interfaces/activities.interface'; +import { randomUUID } from 'node:crypto'; @Injectable() export class OrderService { @@ -129,7 +129,7 @@ export class OrderService { const assetSubClass = data.assetSubClass; const dataSource: DataSource = 'MANUAL'; - let name: string; + let name = data.SymbolProfile.connectOrCreate.create.name; let symbol: string; if ( @@ -142,8 +142,8 @@ export class OrderService { symbol = data.SymbolProfile.connectOrCreate.create.symbol; } else { // Create custom asset profile - name = data.SymbolProfile.connectOrCreate.create.symbol; - symbol = uuidv4(); + name = name ?? data.SymbolProfile.connectOrCreate.create.symbol; + symbol = randomUUID(); } data.SymbolProfile.connectOrCreate.create.assetClass = assetClass; @@ -226,6 +226,15 @@ export class OrderService { }); } + this.eventEmitter.emit( + AssetProfileChangedEvent.getName(), + new AssetProfileChangedEvent({ + currency: order.SymbolProfile.currency, + dataSource: order.SymbolProfile.dataSource, + symbol: order.SymbolProfile.symbol + }) + ); + this.eventEmitter.emit( PortfolioChangedEvent.getName(), new PortfolioChangedEvent({ @@ -345,7 +354,7 @@ export class OrderService { userCurrency: string; userId: string; withExcludedAccountsAndActivities?: boolean; - }): Promise { + }): Promise { let orderBy: Prisma.Enumerable = [ { date: 'asc' } ]; diff --git a/apps/api/src/app/platform/platform.controller.ts b/apps/api/src/app/platform/platform.controller.ts index c91f58cf8..2d4a1d413 100644 --- a/apps/api/src/app/platform/platform.controller.ts +++ b/apps/api/src/app/platform/platform.controller.ts @@ -1,5 +1,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; import { permissions } from '@ghostfolio/common/permissions'; import { @@ -17,9 +18,7 @@ import { AuthGuard } from '@nestjs/passport'; import { Platform } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { CreatePlatformDto } from './create-platform.dto'; import { PlatformService } from './platform.service'; -import { UpdatePlatformDto } from './update-platform.dto'; @Controller('platform') export class PlatformController { diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts index ccdbafac8..f4c99916f 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts @@ -1,4 +1,4 @@ -import { Export } from '@ghostfolio/common/interfaces'; +import { ExportResponse } from '@ghostfolio/common/interfaces'; import { readFileSync } from 'node:fs'; @@ -39,6 +39,6 @@ export const userDummyData = { id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }; -export function loadExportFile(filePath: string): Export { +export function loadExportFile(filePath: string): ExportResponse { return JSON.parse(readFileSync(filePath, 'utf8')); } diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index 24fe2b2f3..7b5ab1a0d 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -1,10 +1,13 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; -import { Filter, HistoricalDataItem } from '@ghostfolio/common/interfaces'; +import { + Activity, + Filter, + HistoricalDataItem +} from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Injectable } from '@nestjs/common'; diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 8a8606003..b3cedb00b 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; @@ -9,7 +8,7 @@ import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { @@ -26,6 +25,7 @@ import { resetHours } from '@ghostfolio/common/helper'; import { + Activity, AssetProfileIdentifier, DataProviderInfo, Filter, @@ -193,7 +193,7 @@ export abstract class PortfolioCalculator { } const currencies: { [symbol: string]: string } = {}; - const dataGatheringItems: IDataGatheringItem[] = []; + const dataGatheringItems: DataGatheringItem[] = []; let firstIndex = transactionPoints.length; let firstTransactionPoint: TransactionPoint = null; let totalInterestWithCurrencyEffect = new Big(0); @@ -336,7 +336,7 @@ export abstract class PortfolioCalculator { ).mul( exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ endDateString - ] + ] ?? 1 ); const { diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts index 9ffa1b409..f0e2f6488 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index ab8000702..10b1fabd3 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index cc65fa8a2..32cd9f7d4 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index 7d9544666..84cab99e1 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts new file mode 100644 index 000000000..1f64684a0 --- /dev/null +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts @@ -0,0 +1,139 @@ +import { + activityDummyData, + loadExportFile, + symbolProfileDummyData, + userDummyData +} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; +import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; +import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { parseDate } from '@ghostfolio/common/helper'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; + +import { Big } from 'big.js'; +import { join } from 'node:path'; + +jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { + return { + CurrentRateService: jest.fn().mockImplementation(() => { + return CurrentRateServiceMock; + }) + }; +}); + +jest.mock( + '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service', + () => { + return { + ExchangeRateDataService: jest.fn().mockImplementation(() => { + return ExchangeRateDataServiceMock; + }) + }; + } +); + +jest.mock( + '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', + () => { + return { + PortfolioSnapshotService: jest.fn().mockImplementation(() => { + return PortfolioSnapshotServiceMock; + }) + }; + } +); + +jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { + return { + RedisCacheService: jest.fn().mockImplementation(() => { + return RedisCacheServiceMock; + }) + }; +}); + +describe('PortfolioCalculator', () => { + let exportResponse: ExportResponse; + + let configurationService: ConfigurationService; + let currentRateService: CurrentRateService; + let exchangeRateDataService: ExchangeRateDataService; + let portfolioCalculatorFactory: PortfolioCalculatorFactory; + let portfolioSnapshotService: PortfolioSnapshotService; + let redisCacheService: RedisCacheService; + + beforeAll(() => { + exportResponse = loadExportFile( + join(__dirname, '../../../../../../../test/import/ok/btceur.json') + ); + }); + + beforeEach(() => { + configurationService = new ConfigurationService(); + + currentRateService = new CurrentRateService(null, null, null, null); + + exchangeRateDataService = new ExchangeRateDataService( + null, + null, + null, + null + ); + + portfolioSnapshotService = new PortfolioSnapshotService(null); + + redisCacheService = new RedisCacheService(null, null); + + portfolioCalculatorFactory = new PortfolioCalculatorFactory( + configurationService, + currentRateService, + exchangeRateDataService, + portfolioSnapshotService, + redisCacheService + ); + }); + + describe('get current positions', () => { + it.only('with BTCUSD buy (in EUR)', async () => { + jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); + + const activities: Activity[] = exportResponse.activities.map( + (activity) => ({ + ...activityDummyData, + ...activity, + date: parseDate(activity.date), + feeInAssetProfileCurrency: 4.46, + SymbolProfile: { + ...symbolProfileDummyData, + currency: 'USD', + dataSource: activity.dataSource, + name: 'Bitcoin', + symbol: activity.symbol + }, + unitPriceInAssetProfileCurrency: 44558.42 + }) + ); + + const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ + activities, + calculationType: PerformanceCalculationType.ROAI, + currency: 'EUR', + userId: userDummyData.id + }); + + const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + + expect(portfolioSnapshot.positions[0].fee).toEqual(new Big(4.46)); + expect( + portfolioSnapshot.positions[0].feeInBaseCurrency.toNumber() + ).toBeCloseTo(3.94, 1); + }); + }); +}); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts index 1ac0dcd16..ce639b564 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, loadExportFile, @@ -15,7 +14,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -23,7 +22,6 @@ import { join } from 'node:path'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -34,7 +32,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -44,7 +41,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -52,7 +48,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { }); describe('PortfolioCalculator', () => { - let exportResponse: Export; + let exportResponse: ExportResponse; let configurationService: ConfigurationService; let currentRateService: CurrentRateService; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index de3f5d3cd..0c111fab2 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -15,13 +14,13 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -32,7 +31,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -42,7 +40,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -53,7 +50,6 @@ jest.mock( '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention ExchangeRateDataService: jest.fn().mockImplementation(() => { return ExchangeRateDataServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts index 29413c6ad..618dc805c 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, loadExportFile, @@ -15,7 +14,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -23,7 +22,6 @@ import { join } from 'node:path'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -34,7 +32,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -44,7 +41,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -52,7 +48,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { }); describe('PortfolioCalculator', () => { - let exportResponse: Export; + let exportResponse: ExportResponse; let configurationService: ConfigurationService; let currentRateService: CurrentRateService; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts index 26b3325c2..a7cbe746c 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, loadExportFile, @@ -15,7 +14,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -23,7 +22,6 @@ import { join } from 'node:path'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -34,7 +32,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -44,7 +41,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -52,7 +48,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { }); describe('PortfolioCalculator', () => { - let exportResponse: Export; + let exportResponse: ExportResponse; let configurationService: ConfigurationService; let currentRateService: CurrentRateService; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index aaf2c4302..aae77c876 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index f7d0e9e6d..495728e22 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -15,13 +14,13 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -32,7 +31,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -42,7 +40,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -53,7 +50,6 @@ jest.mock( '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention ExchangeRateDataService: jest.fn().mockImplementation(() => { return ExchangeRateDataServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 2cb3899e9..1fd88dacc 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts index bb976564a..4c8ccdcf5 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,6 +13,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { @@ -28,7 +28,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -38,7 +37,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 36b6e8be9..0331e163e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts index f64328d39..fdd9e4718 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts @@ -15,7 +15,6 @@ import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -26,7 +25,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -36,7 +34,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 0f1cdfff7..650944421 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, loadExportFile, @@ -15,7 +14,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -23,7 +22,6 @@ import { join } from 'node:path'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -34,7 +32,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -44,7 +41,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -52,7 +48,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { }); describe('PortfolioCalculator', () => { - let exportResponse: Export; + let exportResponse: ExportResponse; let configurationService: ConfigurationService; let currentRateService: CurrentRateService; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index e426a68fa..2e408dc3c 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, loadExportFile, @@ -15,7 +14,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -23,7 +22,6 @@ import { join } from 'node:path'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -34,7 +32,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -44,7 +41,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) @@ -52,7 +48,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { }); describe('PortfolioCalculator', () => { - let exportResponse: Export; + let exportResponse: ExportResponse; let configurationService: ConfigurationService; let currentRateService: CurrentRateService; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts index 5e9949dd2..3c7c3be4b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts @@ -1,4 +1,3 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { activityDummyData, symbolProfileDummyData, @@ -14,13 +13,13 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention CurrentRateService: jest.fn().mockImplementation(() => { return CurrentRateServiceMock; }) @@ -31,7 +30,6 @@ jest.mock( '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention PortfolioSnapshotService: jest.fn().mockImplementation(() => { return PortfolioSnapshotServiceMock; }) @@ -41,7 +39,6 @@ jest.mock( jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { return { - // eslint-disable-next-line @typescript-eslint/naming-convention RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; }) diff --git a/apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts b/apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts index 5cf7c8811..ffb74ee9b 100644 --- a/apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/get-values-params.interface.ts @@ -1,8 +1,8 @@ -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DateQuery } from './date-query.interface'; export interface GetValuesParams { - dataGatheringItems: IDataGatheringItem[]; + dataGatheringItems: DataGatheringItem[]; dateQuery: DateQuery; } diff --git a/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts b/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts index 1c53430f6..9362184c7 100644 --- a/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts @@ -1,4 +1,4 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; +import { Activity } from '@ghostfolio/common/interfaces'; export interface PortfolioOrder extends Pick { date: string; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index e7adb4b86..2221b35ba 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1,7 +1,6 @@ import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface'; -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; @@ -40,6 +39,7 @@ import { import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper'; import { AccountsResponse, + Activity, EnhancedSymbolProfile, Filter, HistoricalDataItem, @@ -88,7 +88,6 @@ import { parseISO, set } from 'date-fns'; -import { isEmpty } from 'lodash'; import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory'; @@ -788,35 +787,7 @@ export class PortfolioService { }); if (activities.length === 0) { - return { - activities: [], - activitiesCount: 0, - averagePrice: undefined, - dataProviderInfo: undefined, - dividendInBaseCurrency: undefined, - dividendYieldPercent: undefined, - dividendYieldPercentWithCurrencyEffect: undefined, - feeInBaseCurrency: undefined, - firstBuyDate: undefined, - grossPerformance: undefined, - grossPerformancePercent: undefined, - grossPerformancePercentWithCurrencyEffect: undefined, - grossPerformanceWithCurrencyEffect: undefined, - historicalData: [], - investmentInBaseCurrencyWithCurrencyEffect: undefined, - marketPrice: undefined, - marketPriceMax: undefined, - marketPriceMin: undefined, - netPerformance: undefined, - netPerformancePercent: undefined, - netPerformancePercentWithCurrencyEffect: undefined, - netPerformanceWithCurrencyEffect: undefined, - performances: undefined, - quantity: undefined, - SymbolProfile: undefined, - tags: [], - value: undefined - }; + return undefined; } const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ @@ -830,7 +801,6 @@ export class PortfolioService { currency: userCurrency }); - const portfolioStart = portfolioCalculator.getStartDate(); const transactionPoints = portfolioCalculator.getTransactionPoints(); const { positions } = await portfolioCalculator.getSnapshot(); @@ -839,225 +809,108 @@ export class PortfolioService { return position.dataSource === dataSource && position.symbol === symbol; }); - if (holding) { - const { - averagePrice, - currency, - dividendInBaseCurrency, - fee, - firstBuyDate, - grossPerformance, - grossPerformancePercentage, - grossPerformancePercentageWithCurrencyEffect, - grossPerformanceWithCurrencyEffect, - investmentWithCurrencyEffect, - marketPrice, - netPerformance, - netPerformancePercentage, - netPerformancePercentageWithCurrencyEffectMap, - netPerformanceWithCurrencyEffectMap, - quantity, - tags, - timeWeightedInvestment, - timeWeightedInvestmentWithCurrencyEffect, - transactionCount - } = holding; - - const activitiesOfHolding = activities.filter(({ SymbolProfile }) => { - return ( - SymbolProfile.dataSource === dataSource && - SymbolProfile.symbol === symbol - ); - }); - - const dividendYieldPercent = getAnnualizedPerformancePercent({ - daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), - netPerformancePercentage: timeWeightedInvestment.eq(0) - ? new Big(0) - : dividendInBaseCurrency.div(timeWeightedInvestment) - }); - - const dividendYieldPercentWithCurrencyEffect = - getAnnualizedPerformancePercent({ - daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), - netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq( - 0 - ) - ? new Big(0) - : dividendInBaseCurrency.div( - timeWeightedInvestmentWithCurrencyEffect - ) - }); + if (!holding) { + return undefined; + } - const historicalData = await this.dataProviderService.getHistorical( - [{ dataSource, symbol }], - 'day', - parseISO(firstBuyDate), - new Date() - ); + const { + averagePrice, + currency, + dividendInBaseCurrency, + fee, + firstBuyDate, + grossPerformance, + grossPerformancePercentage, + grossPerformancePercentageWithCurrencyEffect, + grossPerformanceWithCurrencyEffect, + investmentWithCurrencyEffect, + marketPrice, + netPerformance, + netPerformancePercentage, + netPerformancePercentageWithCurrencyEffectMap, + netPerformanceWithCurrencyEffectMap, + quantity, + tags, + timeWeightedInvestment, + timeWeightedInvestmentWithCurrencyEffect, + transactionCount + } = holding; - const historicalDataArray: HistoricalDataItem[] = []; - let marketPriceMax = Math.max( - activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - marketPrice - ); - let marketPriceMaxDate = - marketPrice > activitiesOfHolding[0].unitPriceInAssetProfileCurrency - ? new Date() - : activitiesOfHolding[0].date; - let marketPriceMin = Math.min( - activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - marketPrice + const activitiesOfHolding = activities.filter(({ SymbolProfile }) => { + return ( + SymbolProfile.dataSource === dataSource && + SymbolProfile.symbol === symbol ); + }); - if (historicalData[symbol]) { - let j = -1; - for (const [date, { marketPrice }] of Object.entries( - historicalData[symbol] - )) { - while ( - j + 1 < transactionPoints.length && - !isAfter(parseDate(transactionPoints[j + 1].date), parseDate(date)) - ) { - j++; - } - - let currentAveragePrice = 0; - let currentQuantity = 0; + const dividendYieldPercent = getAnnualizedPerformancePercent({ + daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), + netPerformancePercentage: timeWeightedInvestment.eq(0) + ? new Big(0) + : dividendInBaseCurrency.div(timeWeightedInvestment) + }); - const currentSymbol = transactionPoints[j]?.items.find( - (transactionPointSymbol) => { - return transactionPointSymbol.symbol === symbol; - } - ); + const dividendYieldPercentWithCurrencyEffect = + getAnnualizedPerformancePercent({ + daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), + netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq(0) + ? new Big(0) + : dividendInBaseCurrency.div(timeWeightedInvestmentWithCurrencyEffect) + }); - if (currentSymbol) { - currentAveragePrice = currentSymbol.averagePrice.toNumber(); - currentQuantity = currentSymbol.quantity.toNumber(); - } + const historicalData = await this.dataProviderService.getHistorical( + [{ dataSource, symbol }], + 'day', + parseISO(firstBuyDate), + new Date() + ); - historicalDataArray.push({ - date, - averagePrice: currentAveragePrice, - marketPrice: - historicalDataArray.length > 0 - ? marketPrice - : currentAveragePrice, - quantity: currentQuantity - }); + const historicalDataArray: HistoricalDataItem[] = []; + let marketPriceMax = Math.max( + activitiesOfHolding[0].unitPriceInAssetProfileCurrency, + marketPrice + ); + let marketPriceMaxDate = + marketPrice > activitiesOfHolding[0].unitPriceInAssetProfileCurrency + ? new Date() + : activitiesOfHolding[0].date; + let marketPriceMin = Math.min( + activitiesOfHolding[0].unitPriceInAssetProfileCurrency, + marketPrice + ); - if (marketPrice > marketPriceMax) { - marketPriceMax = marketPrice; - marketPriceMaxDate = parseISO(date); - } - marketPriceMin = Math.min( - marketPrice ?? Number.MAX_SAFE_INTEGER, - marketPriceMin - ); + if (historicalData[symbol]) { + let j = -1; + for (const [date, { marketPrice }] of Object.entries( + historicalData[symbol] + )) { + while ( + j + 1 < transactionPoints.length && + !isAfter(parseDate(transactionPoints[j + 1].date), parseDate(date)) + ) { + j++; } - } else { - // Add historical entry for buy date, if no historical data available - historicalDataArray.push({ - averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - date: firstBuyDate, - marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, - quantity: activitiesOfHolding[0].quantity - }); - } - const performancePercent = - this.benchmarkService.calculateChangeInPercentage( - marketPriceMax, - marketPrice - ); + let currentAveragePrice = 0; + let currentQuantity = 0; - return { - firstBuyDate, - marketPrice, - marketPriceMax, - marketPriceMin, - SymbolProfile, - tags, - activities: activitiesOfHolding, - activitiesCount: transactionCount, - averagePrice: averagePrice.toNumber(), - dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], - dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), - dividendYieldPercent: dividendYieldPercent.toNumber(), - dividendYieldPercentWithCurrencyEffect: - dividendYieldPercentWithCurrencyEffect.toNumber(), - feeInBaseCurrency: this.exchangeRateDataService.toCurrency( - fee.toNumber(), - SymbolProfile.currency, - userCurrency - ), - grossPerformance: grossPerformance?.toNumber(), - grossPerformancePercent: grossPerformancePercentage?.toNumber(), - grossPerformancePercentWithCurrencyEffect: - grossPerformancePercentageWithCurrencyEffect?.toNumber(), - grossPerformanceWithCurrencyEffect: - grossPerformanceWithCurrencyEffect?.toNumber(), - historicalData: historicalDataArray, - investmentInBaseCurrencyWithCurrencyEffect: - investmentWithCurrencyEffect?.toNumber(), - netPerformance: netPerformance?.toNumber(), - netPerformancePercent: netPerformancePercentage?.toNumber(), - netPerformancePercentWithCurrencyEffect: - netPerformancePercentageWithCurrencyEffectMap?.['max']?.toNumber(), - netPerformanceWithCurrencyEffect: - netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(), - performances: { - allTimeHigh: { - performancePercent, - date: marketPriceMaxDate + const currentSymbol = transactionPoints[j]?.items.find( + (transactionPointSymbol) => { + return transactionPointSymbol.symbol === symbol; } - }, - quantity: quantity.toNumber(), - value: this.exchangeRateDataService.toCurrency( - quantity.mul(marketPrice ?? 0).toNumber(), - currency, - userCurrency - ) - }; - } else { - const currentData = await this.dataProviderService.getQuotes({ - user, - items: [{ symbol, dataSource: DataSource.YAHOO }] - }); - const marketPrice = currentData[symbol]?.marketPrice; - - let historicalData = await this.dataProviderService.getHistorical( - [{ symbol, dataSource: DataSource.YAHOO }], - 'day', - portfolioStart, - new Date() - ); + ); - if (isEmpty(historicalData)) { - try { - historicalData = await this.dataProviderService.getHistoricalRaw({ - assetProfileIdentifiers: [{ symbol, dataSource: DataSource.YAHOO }], - from: portfolioStart, - to: new Date() - }); - } catch { - historicalData = { - [symbol]: {} - }; + if (currentSymbol) { + currentAveragePrice = currentSymbol.averagePrice.toNumber(); + currentQuantity = currentSymbol.quantity.toNumber(); } - } - - const historicalDataArray: HistoricalDataItem[] = []; - let marketPriceMax = marketPrice; - let marketPriceMaxDate = new Date(); - let marketPriceMin = marketPrice; - for (const [date, { marketPrice }] of Object.entries( - historicalData[symbol] - )) { historicalDataArray.push({ date, - value: marketPrice + averagePrice: currentAveragePrice, + marketPrice: + historicalDataArray.length > 0 ? marketPrice : currentAveragePrice, + quantity: currentQuantity }); if (marketPrice > marketPriceMax) { @@ -1069,48 +922,70 @@ export class PortfolioService { marketPriceMin ); } + } else { + // Add historical entry for buy date, if no historical data available + historicalDataArray.push({ + averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, + date: firstBuyDate, + marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, + quantity: activitiesOfHolding[0].quantity + }); + } - const performancePercent = - this.benchmarkService.calculateChangeInPercentage( - marketPriceMax, - marketPrice - ); - - return { - marketPrice, + const performancePercent = + this.benchmarkService.calculateChangeInPercentage( marketPriceMax, - marketPriceMin, - SymbolProfile, - activities: [], - activitiesCount: 0, - averagePrice: 0, - dataProviderInfo: undefined, - dividendInBaseCurrency: 0, - dividendYieldPercent: 0, - dividendYieldPercentWithCurrencyEffect: 0, - feeInBaseCurrency: 0, - firstBuyDate: undefined, - grossPerformance: undefined, - grossPerformancePercent: undefined, - grossPerformancePercentWithCurrencyEffect: undefined, - grossPerformanceWithCurrencyEffect: undefined, - historicalData: historicalDataArray, - investmentInBaseCurrencyWithCurrencyEffect: 0, - netPerformance: undefined, - netPerformancePercent: undefined, - netPerformancePercentWithCurrencyEffect: undefined, - netPerformanceWithCurrencyEffect: undefined, - performances: { - allTimeHigh: { - performancePercent, - date: marketPriceMaxDate - } - }, - quantity: 0, - tags: [], - value: 0 - }; - } + marketPrice + ); + + return { + firstBuyDate, + marketPrice, + marketPriceMax, + marketPriceMin, + SymbolProfile, + tags, + activities: activitiesOfHolding, + activitiesCount: transactionCount, + averagePrice: averagePrice.toNumber(), + dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], + dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), + dividendYieldPercent: dividendYieldPercent.toNumber(), + dividendYieldPercentWithCurrencyEffect: + dividendYieldPercentWithCurrencyEffect.toNumber(), + feeInBaseCurrency: this.exchangeRateDataService.toCurrency( + fee.toNumber(), + SymbolProfile.currency, + userCurrency + ), + grossPerformance: grossPerformance?.toNumber(), + grossPerformancePercent: grossPerformancePercentage?.toNumber(), + grossPerformancePercentWithCurrencyEffect: + grossPerformancePercentageWithCurrencyEffect?.toNumber(), + grossPerformanceWithCurrencyEffect: + grossPerformanceWithCurrencyEffect?.toNumber(), + historicalData: historicalDataArray, + investmentInBaseCurrencyWithCurrencyEffect: + investmentWithCurrencyEffect?.toNumber(), + netPerformance: netPerformance?.toNumber(), + netPerformancePercent: netPerformancePercentage?.toNumber(), + netPerformancePercentWithCurrencyEffect: + netPerformancePercentageWithCurrencyEffectMap?.['max']?.toNumber(), + netPerformanceWithCurrencyEffect: + netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(), + performances: { + allTimeHigh: { + performancePercent, + date: marketPriceMaxDate + } + }, + quantity: quantity.toNumber(), + value: this.exchangeRateDataService.toCurrency( + quantity.mul(marketPrice ?? 0).toNumber(), + currency, + userCurrency + ) + }; } public async getPerformance({ diff --git a/apps/api/src/app/portfolio/rules.service.ts b/apps/api/src/app/portfolio/rules.service.ts index 48d1658aa..5bfb116e0 100644 --- a/apps/api/src/app/portfolio/rules.service.ts +++ b/apps/api/src/app/portfolio/rules.service.ts @@ -1,7 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { PortfolioReportRule, + RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; diff --git a/apps/api/src/app/subscription/subscription.controller.ts b/apps/api/src/app/subscription/subscription.controller.ts index 244a6b806..e1c705fdd 100644 --- a/apps/api/src/app/subscription/subscription.controller.ts +++ b/apps/api/src/app/subscription/subscription.controller.ts @@ -5,7 +5,10 @@ import { DEFAULT_LANGUAGE_CODE, PROPERTY_COUPONS } from '@ghostfolio/common/config'; -import { Coupon } from '@ghostfolio/common/interfaces'; +import { + Coupon, + CreateStripeCheckoutSessionResponse +} from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -111,11 +114,11 @@ export class SubscriptionController { @Post('stripe/checkout-session') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async createCheckoutSession( + public createStripeCheckoutSession( @Body() { couponId, priceId }: { couponId?: string; priceId: string } - ) { + ): Promise { try { - return this.subscriptionService.createCheckoutSession({ + return this.subscriptionService.createStripeCheckoutSession({ couponId, priceId, user: this.request.user diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index 9574d17e9..0fad8c8ac 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -5,13 +5,16 @@ import { DEFAULT_LANGUAGE_CODE, PROPERTY_STRIPE_CONFIG } from '@ghostfolio/common/config'; +import { SubscriptionType } from '@ghostfolio/common/enums'; import { parseDate } from '@ghostfolio/common/helper'; -import { SubscriptionOffer } from '@ghostfolio/common/interfaces'; +import { + CreateStripeCheckoutSessionResponse, + SubscriptionOffer +} from '@ghostfolio/common/interfaces'; import { SubscriptionOfferKey, UserWithSettings } from '@ghostfolio/common/types'; -import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; import { Injectable, Logger } from '@nestjs/common'; import { Subscription } from '@prisma/client'; @@ -38,7 +41,7 @@ export class SubscriptionService { } } - public async createCheckoutSession({ + public async createStripeCheckoutSession({ couponId, priceId, user @@ -46,7 +49,7 @@ export class SubscriptionService { couponId?: string; priceId: string; user: UserWithSettings; - }) { + }): Promise { const subscriptionOffers: { [offer in SubscriptionOfferKey]: SubscriptionOffer; } = @@ -58,33 +61,34 @@ export class SubscriptionService { } ); - const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = { - cancel_url: `${this.configurationService.get('ROOT_URL')}/${ - user.settings.settings.language - }/account`, - client_reference_id: user.id, - line_items: [ - { - price: priceId, - quantity: 1 - } - ], - locale: - (user.settings?.settings - ?.language as Stripe.Checkout.SessionCreateParams.Locale) ?? - DEFAULT_LANGUAGE_CODE, - metadata: subscriptionOffer - ? { subscriptionOffer: JSON.stringify(subscriptionOffer) } - : {}, - mode: 'payment', - payment_method_types: ['card'], - success_url: `${this.configurationService.get( - 'ROOT_URL' - )}/api/v1/subscription/stripe/callback?checkoutSessionId={CHECKOUT_SESSION_ID}` - }; + const stripeCheckoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = + { + cancel_url: `${this.configurationService.get('ROOT_URL')}/${ + user.settings.settings.language + }/account`, + client_reference_id: user.id, + line_items: [ + { + price: priceId, + quantity: 1 + } + ], + locale: + (user.settings?.settings + ?.language as Stripe.Checkout.SessionCreateParams.Locale) ?? + DEFAULT_LANGUAGE_CODE, + metadata: subscriptionOffer + ? { subscriptionOffer: JSON.stringify(subscriptionOffer) } + : {}, + mode: 'payment', + payment_method_types: ['card'], + success_url: `${this.configurationService.get( + 'ROOT_URL' + )}/api/v1/subscription/stripe/callback?checkoutSessionId={CHECKOUT_SESSION_ID}` + }; if (couponId) { - checkoutSessionCreateParams.discounts = [ + stripeCheckoutSessionCreateParams.discounts = [ { coupon: couponId } @@ -92,7 +96,7 @@ export class SubscriptionService { } const session = await this.stripe.checkout.sessions.create( - checkoutSessionCreateParams + stripeCheckoutSessionCreateParams ); return { @@ -175,6 +179,8 @@ export class SubscriptionService { offerKey = 'renewal-early-bird-2023'; } else if (isBefore(createdAt, parseDate('2024-01-01'))) { offerKey = 'renewal-early-bird-2024'; + } else if (isBefore(createdAt, parseDate('2025-12-01'))) { + offerKey = 'renewal-early-bird-2025'; } const offer = await this.getSubscriptionOffer({ diff --git a/apps/api/src/app/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index 5d9a49a29..501692ae5 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/apps/api/src/app/symbol/symbol.controller.ts @@ -1,8 +1,11 @@ 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 { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { LookupResponse } from '@ghostfolio/common/interfaces'; +import { + DataProviderHistoricalResponse, + LookupResponse, + SymbolItem +} from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -22,7 +25,6 @@ import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { isDate, isEmpty } from 'lodash'; -import { SymbolItem } from './interfaces/symbol-item.interface'; import { SymbolService } from './symbol.service'; @Controller('symbol') @@ -97,7 +99,7 @@ export class SymbolController { @Param('dataSource') dataSource: DataSource, @Param('dateString') dateString: string, @Param('symbol') symbol: string - ): Promise { + ): Promise { const date = parseISO(dateString); if (!isDate(date)) { diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index 56befb9b6..15498e80d 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -1,21 +1,18 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; -import { - IDataGatheringItem, - IDataProviderHistoricalResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, HistoricalDataItem, - LookupResponse + LookupResponse, + SymbolItem } from '@ghostfolio/common/interfaces'; import { UserWithSettings } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { format, subDays } from 'date-fns'; -import { SymbolItem } from './interfaces/symbol-item.interface'; - @Injectable() export class SymbolService { public constructor( @@ -27,7 +24,7 @@ export class SymbolService { dataGatheringItem, includeHistoricalData }: { - dataGatheringItem: IDataGatheringItem; + dataGatheringItem: DataGatheringItem; includeHistoricalData?: number; }): Promise { const quotes = await this.dataProviderService.getQuotes({ @@ -75,10 +72,10 @@ export class SymbolService { dataSource, date = new Date(), symbol - }: IDataGatheringItem): Promise { + }: DataGatheringItem): Promise { let historicalData: { [symbol: string]: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; } = { [symbol]: {} diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 83f3f6500..3c19e71af 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -3,9 +3,15 @@ import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard' import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { + DeleteOwnUserDto, + UpdateOwnAccessTokenDto, + UpdateUserSettingDto +} from '@ghostfolio/common/dtos'; import { AccessTokenResponse, User, + UserItem, UserSettings } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -31,10 +37,6 @@ import { User as UserModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { merge, size } from 'lodash'; -import { DeleteOwnUserDto } from './delete-own-user.dto'; -import { UserItem } from './interfaces/user-item.interface'; -import { UpdateOwnAccessTokenDto } from './update-own-access-token.dto'; -import { UpdateUserSettingDto } from './update-user-setting.dto'; import { UserService } from './user.service'; @Controller('user') @@ -126,11 +128,7 @@ export class UserController { ); } - const hasAdmin = await this.userService.hasAdmin(); - - const { accessToken, id, role } = await this.userService.createUser({ - data: { role: hasAdmin ? 'USER' : 'ADMIN' } - }); + const { accessToken, id, role } = await this.userService.createUser(); return { accessToken, diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index fb0741089..7aeaec1f7 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -527,15 +527,23 @@ export class UserService { }); } - public async createUser({ - data - }: { - data: Prisma.UserCreateInput; - }): Promise { - if (!data?.provider) { + public async createUser( + { + data + }: { + data: Prisma.UserCreateInput; + } = { data: {} } + ): Promise { + if (!data.provider) { data.provider = 'ANONYMOUS'; } + if (!data.role) { + const hasAdmin = await this.hasAdmin(); + + data.role = hasAdmin ? 'USER' : 'ADMIN'; + } + const user = await this.prismaService.user.create({ data: { ...data, diff --git a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json index a6db74dfd..4bd7794ca 100644 --- a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json +++ b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json @@ -1,10 +1,13 @@ { "1": "just buy $1 worth of this coin", "3": "The Three Musketeers", + "4": "4", "7": "Lucky7", + "8": "8", "32": "Project 32", - "42": "42 Coin", + "42": "Semantic Layer", "47": "President Trump", + "67": "The Official 67 Coin", "300": "300 token", "365": "365Coin", "369": "Nikola Tesla Token", @@ -28,6 +31,7 @@ "00": "ZER0ZER0", "007": "007 coin", "0DOG": "Bitcoin Dogs", + "0G": "0G", "0KN": "0 Knowledge Network", "0LNETWORK": "0L Network", "0NE": "Stone", @@ -95,6 +99,7 @@ "2OMB": "2omb Finance", "2SHARES": "2SHARE", "2TF": "2TF", + "2Z": "DoubleZero", "300F": "300FIT", "314DAO": "Tonken 314 DAO", "32BIT": "32Bitcoin", @@ -117,6 +122,7 @@ "404A": "404Aliens", "404BLOCKS": "404Blocks", "420CHAN": "420chan", + "42COIN": "42 Coin", "4ART": "4ART Coin", "4CHAN": "4Chan", "4CZ": "FourCZ", @@ -133,12 +139,14 @@ "50TRUMP": "50TRUMP", "50X": "50x.com", "5IRE": "5ire", + "5PT": "Five Pillars Token", "69MINUTES": "69 Minutes", "77G": "GraphenTech", "7E": "7ELEVEN", "88MPH": "88mph", "8BIT": "8BIT Coin", "8BT": "8 Circuit Studios", + "8LNDS": "8Lends", "8PAY": "8Pay", "8X8": "8X8 Protocol", "99BTC": "99 Bitcoins", @@ -157,7 +165,7 @@ "A5T": "Alpha5", "A7A5": "A7A5", "A8": "Ancient8", - "AA": "Alva", + "AA": "ARAI Token", "AAA": "Moon Rabbit", "AAAHHM": "Plankton in Pain", "AAAI": "AAAI_agent by Virtuals", @@ -205,6 +213,7 @@ "ABONDV1": "ApeSwap", "ABR": "Allbridge", "ABSIMPSON": "abstract simpson", + "ABSTER": "Abster", "ABT": "ArcBlock", "ABTC": "aBTC", "ABTX": "Abbott xStock", @@ -222,6 +231,7 @@ "ACAT": "Alphacat", "ACATO": "ACA Token", "ACCEL": "Accel Defi", + "ACCES": "Metacces", "ACCN": "Accelerator Network", "ACD": "Alliance Cargo Direct", "ACDC": "Volt", @@ -285,6 +295,7 @@ "ADF": "Art de Finance", "ADH": "Adhive", "ADI": "Aditus", + "ADIX": "Adix Token", "ADK": "Aidos Kuneen", "ADL": "Adel", "ADM": "ADAMANT Messenger", @@ -394,6 +405,7 @@ "AGRO": "Bit Agro", "AGRS": "Agoras Token", "AGS": "Aegis", + "AGT": "Alaya Governance Token", "AGURI": "Aguri-Chan", "AGV": "Astra Guild Ventures", "AGVC": "AgaveCoin", @@ -406,8 +418,10 @@ "AI21X": "ai21x", "AI23T": "23 Turtles", "AI3": "Autonomys Network", + "AI4": "AI⁴", "AI69SAKURA": "Sakura", - "AIA": "AIA Chain", + "AIA": "DeAgentAI", + "AIACHAIN": "AIA Chain", "AIAF": "AI Agent Factory", "AIAGENT": "AI Agents", "AIAGENTAPP": "Aiagent.app", @@ -533,8 +547,10 @@ "AIX": "ALIENX", "AIX9": "AthenaX9", "AIXBT": "aixbt by Virtuals", + "AIXCB": "aixCB by Virtuals", "AIXERC": "AI-X", "AIXT": "AIXTerminal", + "AJC": "AI Judge Companion", "AJNA": "Ajna Protocol", "AJUN": "Ajuna Network", "AK12": "AK12", @@ -615,6 +631,7 @@ "ALLEY": "NFT Alley", "ALLIN": "All in", "ALLMEE": "All.me", + "ALLO": "Allora", "ALM": "Alium Finance", "ALMAN": "Alman", "ALMC": "Awkward Look Monkey Club", @@ -624,6 +641,7 @@ "ALNV1": "Aluna v1", "ALOHA": "Aloha", "ALON": "Alon", + "ALOR": "The Algorix", "ALOT": "Dexalot", "ALP": "Alphacon", "ALPA": "Alpaca", @@ -641,6 +659,7 @@ "ALPHAS": "Alpha Shards", "ALPHR": "Alphr", "ALPINE": "Alpine F1 Team Fan Token", + "ALPRO": "Assets Alphabet", "ALPS": "Alpenschillling", "ALT": "Altlayer", "ALTA": "Alta Finance", @@ -651,12 +670,14 @@ "ALTMAN": "SAM", "ALTOCAR": "AltoCar", "ALTR": "Altranium", + "ALTT": "Altcoinist", "ALU": "Altura", "ALUSD": "Alchemix USD", "ALUX": "Alux Bank", "ALV": "Allive", "ALV1": "ArchLoot v1", "ALVA": "Alvara Protocol", + "ALVACOIN": "Alva", "ALWAYS": "Always Evolving", "ALX": "ALAX", "ALY": "Ally", @@ -676,6 +697,7 @@ "AMC": "AI Meta Coin", "AMDC": "Allmedi Coin", "AMDG": "AMDG", + "AMDX": "AMD xStock", "AME": "Amepay", "AMEP": "America Party", "AMER": "America", @@ -764,6 +786,8 @@ "ANML": "Animal Concerts", "ANN": "Annex Finance", "ANNE": "ANNE", + "ANOA": "ANOA", + "ANOME": "Anome", "ANON": "HeyAnon", "ANONCOIN": "Anoncoin", "ANONCRYPTO": "ANON", @@ -786,16 +810,18 @@ "ANUS": "URANUS", "ANV": "Aniverse", "ANVL": "Anvil", + "ANVLV1": "Anvil v1", "ANW": "Anchor Neural World", "ANY": "Anyswap", "ANYONE": "ANyONe Protocol", "ANZENUSD": "Anzen Finance", "AO": "AO", "AOC": "Alickshundra Occasional-Cortex", + "AOE": "Agentic Open Economy", "AOG": "AgeOfGods", "AOK": "AOK", "AOL": "AOL (America Online)", - "AOP": "Averopay", + "AOP": "Ark Of Panda", "AOS": "AOS", "AOT": "Age of Tanks", "AP": "America Party", @@ -803,7 +829,7 @@ "APAD": "Anypad", "APC": "AlpaCoin", "APCG": "ALLPAYCOIN", - "APD": "Aptopad", + "APD": "APD", "APE": "ApeCoin", "APED": "Aped", "APEDEV": "The dev is an Ape", @@ -839,6 +865,8 @@ "APPLE": "AppleSwap", "APPLESWAPAI": "AppleSwap AI", "APPX": "AppLovin xStock", + "APR": "aPriori", + "APRCOIN": "APR Coin", "APRICOT": "Apricot Finance", "APRIL": "April", "APRS": "Aperios", @@ -848,6 +876,7 @@ "APTESG": "AppleTree Token", "APTM": "Apertum", "APTOGE": "Aptoge", + "APTOPAD": "Aptopad", "APTR": "Aperture Finance", "APU": "Apu Apustaja", "APUAPU": "APU", @@ -917,7 +946,8 @@ "AREN": "Arenon", "ARENA": "Arena", "AREPA": "Arepacoin", - "ARES": "Ares Protocol", + "ARES": "ARES", + "ARESP": "Ares Protocol", "ARG": "Argentine Football Association Fan Token", "ARGENTUM": "Argentum", "ARGO": "ArGoApp", @@ -927,6 +957,7 @@ "ARI10": "Ari10", "ARIA": "ARIA.AI", "ARIA20": "Arianee", + "ARIAIP": "Aria", "ARIT": "ArithFi", "ARIX": "Arix", "ARK": "ARK", @@ -947,6 +978,7 @@ "ARNC": "Arnoya classic", "ARNM": "Arenum", "ARNO": "ARNO", + "ARNOLD": "ARNOLD", "ARNX": "Aeron", "ARNXM": "Armor NXM", "ARO": "Arionum", @@ -964,7 +996,7 @@ "ARRR": "Pirate Chain", "ARSL": "Aquarius Loan", "ARSW": "ArthSwap", - "ART": "Genify ART", + "ART": "LiveArt", "ARTC": "Artcoin", "ARTDECO": "ARTDECO", "ARTDRAW": "ArtDraw", @@ -998,7 +1030,7 @@ "ASAN": "ASAN VERSE", "ASAP": "Asap Sniper Bot", "ASBNB": "Astherus Staked BNB", - "ASC": "Ascend", + "ASCEND": "Ascend", "ASD": "AscendEX Token", "ASDEX": "AstraDEX", "ASEED": "aUSD SEED (Acala)", @@ -1018,6 +1050,7 @@ "ASKO": "Asko", "ASM": "Assemble Protocol", "ASMAT": "AsMatch", + "ASMLX": "ASML xStock", "ASMO": "AS Monaco Fan Token", "ASN": "Ascension Coin", "ASNT": "Assent Protocol", @@ -1037,6 +1070,8 @@ "ASST": "AssetStream", "AST": "AirSwap", "ASTA": "ASTA", + "ASTER": "Aster", + "ASTERINU": "Aster INU", "ASTO": "Altered State Token", "ASTON": "Aston", "ASTONV": "Aston Villa Fan Token", @@ -1061,7 +1096,7 @@ "ASVA": "Asva", "ASW": "AdaSwap", "ASY": "ASYAGRO", - "AT": "AWARE", + "AT": "APRO oracle Token", "ATA": "Automata", "ATB": "ATB coin", "ATC": "AutoBlock", @@ -1187,11 +1222,13 @@ "AVE": "Avesta", "AVEN": "Aventis AI", "AVENT": "Aventa", + "AVEROPAY": "Averopay", "AVERY": "Avery Games", "AVG": "Avocado DAO", "AVGOX": "Broadcom xStock", "AVH": "Animation Vision Cash", "AVI": "Aviator", + "AVICI": "Avici", "AVINOC": "AVINOC", "AVIVE": "Avive World", "AVL": "AVL", @@ -1209,6 +1246,7 @@ "AVXT": "Avaxtars Token", "AWARDCOIN": "Award", "AWARE": "ChainAware.ai", + "AWARETOKEN": "AWARE", "AWAX": "AWAX", "AWC": "Atomic Wallet Coin", "AWE": "AWE Network", @@ -1241,8 +1279,11 @@ "AXNT": "Axentro", "AXO": "Axo", "AXOL": "Axol", + "AXOME": "Axolotl Meme", "AXON": "AxonDAO Governance Token", + "AXP": "aXpire v1", "AXPR": "aXpire", + "AXPRV2": "aXpire v2", "AXR": "AXRON", "AXS": "Axie Infinity Shards", "AXSV1": "Axie Infinity Shards v1", @@ -1286,7 +1327,9 @@ "BABI": "Babylons", "BABL": "Babylon Finance", "BABY": "Babylon", + "BABY4": "Baby 4", "BABYANDY": "Baby Andy", + "BABYASTER": "Baby Aster", "BABYB": "Baby Bali", "BABYBI": "Baby Bitcoin", "BABYBINANCE": "BABYBINANCE", @@ -1311,6 +1354,7 @@ "BABYCEO": "Baby Doge CEO", "BABYCRASH": "BabyCrash", "BABYCRAZYT": "BABY CRAZY TIGER", + "BABYCREPE": "BABY CREPE", "BABYCUBAN": "Baby Cuban", "BABYCZHAO": "Baby Czhao", "BABYD": "Baby Dragon", @@ -1394,6 +1438,7 @@ "BABYWLFI": "Baby World Liberty Financial", "BABYWLFIN": "Baby WLFI", "BABYX": "Baby X", + "BABYXRP": "Baby Ripple", "BAC": "Basis Cash", "BACHI": "Bachi on Base", "BACK": "DollarBack", @@ -1411,6 +1456,7 @@ "BAFC": "BabyApeFunClub", "BAG": "Bag", "BAGS": "Basis Gold Share", + "BAGWORK": "Bagwork", "BAHAMAS": "Bahamas", "BAHIA": "Esporte Clube Bahia Fan Token", "BAI": "BearAI", @@ -1470,15 +1516,17 @@ "BANNER": "BannerCoin", "BANUS": "Banus.Finance", "BANX": "Banx.gg", - "BAO": "Bao Finance", + "BAO": "Bao Token V2", "BAOBAO": "BaoBao", "BAOE": "Business Age of Empires", "BAOM": "Battle of Memes", "BAOS": "BaoBaoSol", + "BAOV1": "BaoToken v1", "BAR": "FC Barcelona Fan Token", "BARA": "Capybara", "BARAKATUH": "Barakatuh", "BARC": "The Blu Arctic Water Company", + "BARD": "Lombard", "BAREBEARS": "BAREBEARS", "BARIO": "Bario", "BARK": "Bored Ark", @@ -1496,6 +1544,7 @@ "BASED": "Based Money", "BASEDAI": "BasedAI", "BASEDALF": "Based Alf", + "BASEDB": "Based Bonk", "BASEDCHILL": "Based Chill Guy", "BASEDCOPE": "COPE", "BASEDFINANCE": "Based", @@ -1517,6 +1566,7 @@ "BASID": "Basid Coin", "BASIL": "Basilisk", "BASIS": "Basis", + "BASISCOIN": "Basis Coin", "BASK": "BasketDAO", "BAST": "Bast", "BASTET": "Bastet Goddess", @@ -1530,7 +1580,7 @@ "BAX": "BABB", "BAXS": "BoxAxis", "BAXV1": "BABB v1", - "BAY": "BitBay", + "BAY": "Marina Protocol", "BAYSE": "coynbayse", "BAZED": "Bazed Games", "BB": "BounceBit", @@ -1567,6 +1617,7 @@ "BBRETT": "Baby Brett", "BBROCCOLI": "Baby Broccoli", "BBS": "BBSCoin", + "BBSNEK": "BabySNEK", "BBSOL": "Bybit Staked SOL", "BBT": "BabyBoomToken", "BBTC": "Binance Wrapped BTC", @@ -1587,6 +1638,7 @@ "BCD": "Bitcoin Diamond", "BCDN": "BlockCDN", "BCDT": "EvidenZ", + "BCE": "bitcastle Token", "BCEO": "bitCEO", "BCF": "BitcoinFast", "BCG": "BlockChainGames", @@ -1607,6 +1659,7 @@ "BCNT": "Bincentive", "BCNX": "BCNEX", "BCO": "BridgeCoin", + "BCOIN": "Ball3", "BCOINBNB": "Bombcrypto", "BCOINM": "Bomb Crypto (MATIC)", "BCOINSOL": "Bomb Crypto (SOL)", @@ -1664,10 +1717,11 @@ "BEAR": "Bear Inu", "BEARIN": "Bear in Bathrobe", "BEAST": "MrBeast", - "BEAT": "BEAT Token", + "BEAT": "Beat Token", "BEATAI": "eBeat AI", "BEATLES": "JohnLennonC0IN", "BEATS": "Sol Beats", + "BEATTOKEN": "BEAT Token", "BEAVER": "beaver", "BEBE": "BEBE", "BEBEETH": "BEBE", @@ -1703,6 +1757,8 @@ "BEIBEI": "Chinese BEIBEI", "BEL": "Bella Protocol", "BELA": "Bela", + "BELG": "Belgian Malinois", + "BELIEVE": "Believe", "BELL": "Bellscoin", "BELLE": "Isabelle", "BELLS": "Bellscoin", @@ -1759,6 +1815,7 @@ "BETS": "BetSwirl", "BETT": "Bettium", "BETU": "Betu", + "BETURA": "BETURA", "BETZ": "Bet Lounge", "BEX": "BEX token", "BEY": "NBX", @@ -1772,7 +1829,7 @@ "BFEX": "BFEX", "BFG": "BFG Token", "BFHT": "BeFaster Holder Token", - "BFI": "BitDefi", + "BFI": "BlockFi-Ai", "BFIC": "Bficoin", "BFICGOLD": "BFICGOLD", "BFK WARZONE": "BFK Warzone", @@ -1858,9 +1915,11 @@ "BIGJIM": "BIG JIM", "BIGLEZ": "THE BIG LEZ SHOW", "BIGMIKE": "Big Mike", + "BIGOD": "BinGold Token", "BIGPUMP": "Big Pump", "BIGSB": "BigShortBets", "BIGTIME": "Big Time", + "BIGTOWN": "Burp", "BIGUP": "BigUp", "BIH": "BitHostCoin", "BIHU": "Key", @@ -1910,19 +1969,23 @@ "BISKIT": "Biskit Protocol", "BISO": "BISOSwap", "BIST": "Bistroo", + "BISTOX": "Bistox Exchange Token", "BIT": "BitDAO", "BIT16": "16BitCoin", "BITAIR": "Bitair", "BITASEAN": "BitAsean", "BITB": "BeanCash", + "BITBAY": "BitBay", "BITBEDR": "Bitcoin EDenRich", "BITBO": "BitBook", "BITBOARD": "Bitboard", "BITBOOST": "BitBoost", + "BITBOOSTTOKEN": "BitBoost", "BITBULL": "Bitbull", "BITBURN": "Bitburn", "BITC": "BitCash", "BITCAR": "BitCar", + "BITCARBON": "Bitcarbon", "BITCAT": "Bitcat", "BITCATONSOL": "Bitcat", "BITCCA": "Bitcci Cash", @@ -1939,12 +2002,14 @@ "BITCONNECT": "BitConnect Coin", "BITCORE": "BitCore", "BITCRATIC": "Bitcratic Token", + "BITDEFI": "BitDefi", "BITDEGREE": "BitDegree", "BITE": "Bitether", "BITF": "Bit Financial", "BITFLIP": "BitFlip", "BITG": "Bitcoin Green", "BITGOLD": "bitGold", + "BITGRIN": "BitGrin", "BITHER": "Bither", "BITL": "BitLux", "BITM": "BitMoney", @@ -2035,6 +2100,7 @@ "BLEPE": "Blepe", "BLERF": "BLERF", "BLES": "Blind Boxes", + "BLESS": "Bless Token", "BLET": "Brainlet", "BLF": "Baby Luffy", "BLHC": "BlackholeCoin", @@ -2060,6 +2126,7 @@ "BLOC": "Blockcloud", "BLOCK": "Blockasset", "BLOCKB": "Block Browser", + "BLOCKBID": "Blockbid", "BLOCKF": "Block Farm Club", "BLOCKG": "BlockGames", "BLOCKIFY": "Blockify.Games", @@ -2069,7 +2136,10 @@ "BLOCKS": "BLOCKS", "BLOCKSSPACE": "Blocks Space", "BLOCKSTAMP": "BlockStamp", + "BLOCKSV1": "BLOCKS v1", "BLOCKT": "Blocktools", + "BLOCKTRADE": "Blocktrade", + "BLOCKVAULT": "BLOCKVAULT TOKEN", "BLOCKW": "Blockwise", "BLOCM": "BLOC.MONEY", "BLOCX": "BLOCX.", @@ -2082,6 +2152,7 @@ "BLOVELY": "Baby Lovely Inu", "BLOX": "BLOX", "BLOXT": "Blox Token", + "BLOXWAP": "BLOXWAP", "BLP": "BullPerks", "BLPAI": "BullPerks AI", "BLPT": "Blockprompt", @@ -2093,6 +2164,7 @@ "BLTG": "Block-Logic", "BLTV": "BLTV Token", "BLU": "BlueCoin", + "BLUAI": "Bluwhale AI", "BLUB": "BLUB", "BLUE": "Bluefin", "BLUEBASE": "Blue", @@ -2106,6 +2178,7 @@ "BLUESPARROW": "BlueSparrow Token", "BLUESPARROWOLD": "BlueSparrowToken", "BLUEW": "Blue Whale", + "BLUEY": "BlueyonBase", "BLUFF": "BluffCat", "BLUI": "Blui", "BLUM": "Blum", @@ -2125,6 +2198,7 @@ "BM": "BitMoon", "BMAGA": "Baby Maga", "BMARS": "Binamars", + "BMB": "Beamable Network Token", "BMBO": "Bamboo Coin", "BMC": "Blackmoon Crypto", "BMCHAIN": "BMChain", @@ -2138,6 +2212,7 @@ "BMIC": "Bitmic", "BMICKEY": "Baby Mickey", "BMK": "Benchmark", + "BMNRX": "Bitmine xStock", "BMON": "Binamon", "BMONEY": "B-money", "BMP": "Brother Music Platform", @@ -2171,6 +2246,7 @@ "BNBFLOKI": "BNB FLOKI", "BNBFROG": "BNBFROG", "BNBH": "BnbHeroes Token", + "BNBHOLDER": "币安Holder", "BNBLION": "BNB LION", "BNBOLYMPIC": "BNB OLYMPIC", "BNBP": "BNBPot", @@ -2192,6 +2268,7 @@ "BNK": "Bankera", "BNKR": "BankrCoin", "BNKV1": "Bankera v1", + "BNL": "BitNational Token", "BNN": "Banyan Network", "BNOM": "BitNomad", "BNP": "BenePit", @@ -2348,7 +2425,7 @@ "BOOP": "BOOP", "BOOPA": "Boopa", "BOOS": "Boost Trump Campaign", - "BOOST": "PodFast", + "BOOST": "Boost", "BOOSTCO": "Boost", "BOOSTO": "BOOSTO", "BOOT": "Bostrom", @@ -2367,7 +2444,7 @@ "BORKIE": "Borkie", "BORPA": "Borpa", "BORUTO": "Boruto Inu", - "BOS": "BOScoin", + "BOSCOIN": "BOScoin", "BOSE": "Bitbose", "BOSHI": "Boshi", "BOSOL": "Book of Solana", @@ -2388,6 +2465,7 @@ "BOU": "Boulle", "BOUNCE": "Bounce Token", "BOUNTY": "ChainBounty", + "BOUNTYK": "BOUNTYKINDS", "BOUTS": "BoutsPro", "BOW": "Archer Swap", "BOWE": "Book of Whales", @@ -2508,6 +2586,7 @@ "BRK": "BreakoutCoin", "BRKBX": "Berkshire Hathaway xStock", "BRKL": "Brokoli Token", + "BRL1": "BRL1", "BRM": "BullRun Meme", "BRMV": "BRMV Token", "BRN": "BRN Metaverse", @@ -2594,11 +2673,13 @@ "BST": "Blocksquare Token", "BSTAR": "Blackstar", "BSTC": "BST Chain", + "BSTER": "Bster", "BSTK": "BattleStake", "BSTN": "BitStation", "BSTR": "BSTR", "BSTS": "Magic Beasties", "BSTY": "GlobalBoost", + "BSU": "Baby Shark Universe Token", "BSV": "Bitcoin SV", "BSVBRC": "BSVBRC", "BSW": "Biswap", @@ -2617,6 +2698,7 @@ "BTB": "BitBar", "BTBL": "Bitball", "BTBS": "BitBase Token", + "BTBTX": "Bit Digital xStock", "BTC": "Bitcoin", "BTC2": "Bitcoin 2", "BTC2XFLI": "BTC 2x Flexible Leverage Index", @@ -2670,6 +2752,7 @@ "BTDX": "Bitcloud 2.0", "BTE": "Betero", "BTECOIN": "BTEcoin", + "BTELEGRAM": "BetterTelegram Token", "BTEV1": "Betero v1", "BTEX": "BTEX", "BTF": "Blockchain Traded Fund", @@ -2772,6 +2855,7 @@ "BULLBEAR": "BullBear AI", "BULLC": "BuySell", "BULLF": "BULL FINANCE", + "BULLGOD": "Bull God", "BULLI": "Bullish On Ethereum", "BULLIEVERSE": "Bullieverse", "BULLINU": "Bull inu", @@ -2796,6 +2880,7 @@ "BUNI": "Bunicorn", "BUNKER": "BunkerCoin", "BUNNI": "Bunni", + "BUNNIE": "Bunnie", "BUNNIV1": "Timeless", "BUNNY": "Pancake Bunny", "BUNNYINU": "Bunny Inu", @@ -2820,6 +2905,7 @@ "BUTT": "Buttercat", "BUTTCOIN": "The Next Bitcoin", "BUTTHOLE": "Butthole Coin", + "BUTTPLUG": "fartcoin killer", "BUX": "BUX", "BUXCOIN": "Buxcoin", "BUY": "Burency", @@ -2860,6 +2946,7 @@ "BXT": "BitTokens", "BXTB": "BXTB Foundation", "BXX": "Baanx", + "BXXV1": "Baanx v1", "BXY": "Beaxy", "BYAT": "Byat", "BYB": "BiorBank", @@ -2885,6 +2972,8 @@ "BZZ": "Swarmv", "BZZONE": "Bzzone", "C": "Chainbase Token", + "C1USD": "Currency One USD", + "C1USDV1": "Currency One USD", "C2": "Coin.2", "C20": "Crypto20", "C25": "C25 Coin", @@ -2902,6 +2991,7 @@ "CACHE": "Cache", "CACHEGOLD": "CACHE Gold", "CACTUS": "CACTUS", + "CACXT": "Convertible ACXT", "CADAI": "CADAI", "CADC": "CAD Coin", "CADINU": "Canadian Inuit Dog", @@ -2930,6 +3020,7 @@ "CALCI": "Calcium", "CALI": "CaliCoin", "CALL": "Global Crypto Alliance", + "CALLISTO": "Callisto Network", "CALLS": "OnlyCalls by Virtuals", "CALO": "Calo", "CAM": "Consumption Avatar Matrix", @@ -2951,6 +3042,7 @@ "CANTI": "Cantina Royale", "CANTO": "CANTO", "CANYA": "CanYaCoin", + "CAOCAO": "CaoCao", "CAP": "Capverto", "CAPA": "Cake Panda", "CAPD": "Capdax", @@ -3003,6 +3095,7 @@ "CASIO": "CasinoXMetaverse", "CASPER": "Casper DeFi", "CASPERTOKEN": "Casper Token", + "CASPUR": "Caspur Zoomies", "CAST": "Castello Coin", "CASTLE": "bitCastle", "CAT": "Simon's Cat", @@ -3096,6 +3189,7 @@ "CBK": "Cobak Token", "CBL": "Credbull", "CBM": "CryptoBonusMiles", + "CBNB": "Community of BNB", "CBNT": "Create Breaking News Together", "CBOT": "C-BOT", "CBP": "CashBackPro", @@ -3110,8 +3204,9 @@ "CBUCKS": "CRYPTOBUCKS", "CBUK": "CurveBlock", "CBX": "CropBytes", + "CBXRP": "Coinbase Wrapped XRP", "CBY": "Carbify", - "CC": "CloudChat", + "CC": "Canton Coin", "CC10": "Cryptocurrency Top 10 Tokens Index", "CCA": "CCA", "CCAKE": "CheeseCake Swap", @@ -3132,6 +3227,7 @@ "CCL": "CyClean", "CCN": "CannaCoin", "CCO": "Ccore", + "CCO2": "Carbon Capture", "CCOIN": "Creditcoin", "CCOMM": "Crypto Commonwealth", "CCOMP": "cCOMP", @@ -3144,12 +3240,13 @@ "CCX": "Conceal", "CCXC": "CoolinDarkCoin", "CCXX": "CounosX", + "CDAG": "CannDollar", "CDAI": "Compound Dai", "CDBIO": "CDbio", "CDCETH": "Crypto.com Staked ETH", "CDCSOL": "Crypto.com Staked SOL", "CDEX": "Cryptodex", - "CDL": "CoinDeal Token", + "CDL": "Creditlink", "CDN": "Canada eCoin", "CDOG": "Corn Dog", "CDOGE": "cyberdoge", @@ -3220,6 +3317,7 @@ "CGG": "Chain Guardians", "CGL": "Crypto Gladiator Shards", "CGLD": "Celo Gold", + "CGN": "CYGNUS", "CGO": "Comtech Gold", "CGPT": "ChainGPT", "CGPU": "ChainGPU", @@ -3239,9 +3337,9 @@ "CHAI": "Chroma AI", "CHAIN": "Chain Games", "CHAINCADE": "ChainCade", + "CHAINSOFWAR": "Chains of War", "CHAL": "Chalice Finance", "CHAM": "Champion", - "CHAMP": "NFT Champions", "CHAMPZ": "Champz", "CHAN": "ChanCoin", "CHANCE": "Ante Casino", @@ -3253,6 +3351,7 @@ "CHAPZ": "Chappyz", "CHARGED": "GoCharge Tech", "CHARIZARD": "Charizard Inu", + "CHARLIE": "Charlie Kirk", "CHARM": "Charm Coin", "CHARS": "CHARS", "CHART": "BetOnChart", @@ -3428,7 +3527,8 @@ "CLANKER": "tokenbot", "CLAP": "Clap Cat", "CLAS": "Classic USDC", - "CLASH": "Clashub", + "CLASH": "GeorgePlaysClashRoyale", + "CLASHUB": "Clashub", "CLASS": "Class Coin", "CLAY": "Clayton", "CLAYN": "Clay Nation", @@ -3457,7 +3557,6 @@ "CLIPPY": "CLIPPY", "CLIPS": "Clips", "CLIQ": "DefiCliq", - "CLISBNB": "clisBNB", "CLIST": "Chainlist", "CLM": "CoinClaim", "CLMRS": "Crolon Mars", @@ -3465,13 +3564,14 @@ "CLND": "COLEND", "CLNX": "Coloniume Network", "CLNY": "Colony", - "CLO": "Callisto Network", + "CLO": "Yei Finance", "CLOA": "Cloak", "CLOAK": "CloakCoin", "CLOKI": "CATLOKI", "CLOOTS": "CryptoLoots", "CLORE": "Clore.ai", "CLOUD": "Cloud", + "CLOUDCHAT": "CloudChat", "CLOUDGPU": "CloudGPU", "CLOUT": "BitClout", "CLOUTIO": "Clout", @@ -3552,7 +3652,8 @@ "CNYX": "eToro Chinese Yuan", "CO": "Corite", "CO2": "CO2 Token", - "COAI": "CodeMong Ai", + "COA": "Alliance Games", + "COAI": "ChainOpera AI", "COAL": "BitCoal", "COB": "Cobinhood", "COBE": "Castle of Blackwater", @@ -3571,6 +3672,7 @@ "CODAI": "CODAI", "CODE": "Code Token", "CODEG": "CodeGenie", + "CODEMONG": "CodeMong Ai", "CODEO": "Codeo Token", "CODEXTOKEN": "CodexToken", "CODI": "Codi Finance", @@ -3592,7 +3694,9 @@ "COINB": "Coinbidex", "COINBT": "CoinBot", "COINBUCK": "Coinbuck", + "COINDEALTOKEN": "CoinDeal Token", "COINDEFI": "Coin", + "COINDEPO": "CoinDepo Token", "COING": "Coingrid", "COINH": "Coinhound", "COINLION": "CoinLion", @@ -3632,6 +3736,7 @@ "COMEW": "Coin In Meme World", "COMFI": "CompliFi", "COMM": "Community Coin", + "COMMON": "COMMON", "COMMS": "CallofMeme", "COMMUNITYCOIN": "Community Coin", "COMP": "Compound", @@ -3690,6 +3795,7 @@ "CORGIAI": "CorgiAI", "CORGIB": "The Corgi of PolkaBridge", "CORION": "Corion", + "CORL": "Coral Finance", "CORN": "Corn", "CORNELLA": "CORNELLA", "CORNFIELDFARM": "CORN", @@ -3721,7 +3827,7 @@ "COVA": "COVA", "COVAL": "Circuits of Value", "COVER": "Cover Protocol", - "COVEROLD": " Cover Protocol (old)", + "COVERV1": "Cover Protocol (old)", "COVEX": "CoVEX", "COVIDTOKEN": "Covid Token", "COVIR": "COVIR", @@ -3814,16 +3920,18 @@ "CREAM": "Cream", "CREAML": "Creamlands", "CREATIVE": "Creative Token", - "CRED": "Verify", + "CRED": "Credia Layer", "CREDI": "Credefi", "CREDIT": "Credit", "CREDITS": "Credits", "CREDO": "Credo", "CREED": "Thecreed", "CREMAT": "Cremation Coin", + "CREMEPUFF": "Creme Puff", "CREO": "Creo Engine", "CREP": "Compound Augur", - "CREPE": "Crepe Coin", + "CREPE": "CREPE", + "CREPECOIN": "Crepe Coin", "CRES": "Cresio", "CRESV1": "Cresio v1", "CREV": "CryptoRevolution", @@ -3873,6 +3981,7 @@ "CRP": "Crypton", "CRPS": "CryptoPennies", "CRPT": "Crypterium", + "CRPTC": "CRPT Classic", "CRS": "CYRUS", "CRSP": "CryptoSpots", "CRT": "Carr.Finance", @@ -3923,12 +4032,15 @@ "CRYPTONITE": "Cryptonite", "CRYPTOOFFICIAL": "Crypto", "CRYPTOPAL": "Pal", + "CRYPTOPING": "CryptoPing", "CRYPTOPRO": "CryptoProfile", "CRYPTOR": "CRYPTORG", "CRYPTOS": "CryptoSoul", "CRYPTOSDG": "Crypto SDG", "CRYPTOT": "Crypto Trump", "CRYPTOTANKS": "CryptoTanks", + "CRYPTOTR": "Crypto Trump", + "CRYPTOTYCOON": "CryptoTycoon", "CRYPTOU": "CryptoUnity", "CRYSTAL": "Crystal", "CRYSTALCLEAR": "Crystal Clear Token", @@ -4080,6 +4192,7 @@ "CWIF": "catwifhat", "CWIS": "Crypto Wisdom Coin", "CWN": "CryptoWorldNews", + "CWOIN": "cwoin", "CWR": "Cowrium", "CWS": "Crowns", "CWT": "CrossWallet", @@ -4127,11 +4240,13 @@ "CYP": "CypherPunkCoin", "CYPEPE": "CyPepe", "CYPHER": "CYPHER•GENESIS (Runes)", + "CYPR": "Cypher", "CYRS": "Cyrus Token", "CYRUS": "Cyrus Exchange", "CYS": "BlooCYS", "CYT": "Cryptokenz", "CZ": "CHANGPENG ZHAO (changpengzhao.club)", + "CZBOOK": "CZ BOOK", "CZBROCCOLI": "Cz Broccoli", "CZC": "Crazy Coin", "CZDOG": "CZ Dog", @@ -4192,7 +4307,7 @@ "DAL": "DAOLaunch", "DALI": "Dalichain", "DALMA": "Dalma Inu", - "DAM": "Datamine", + "DAM": "Reservoir", "DAMEX": "DAMEX", "DAMN": "Sol Killer", "DAMO": "Coinzen", @@ -4212,6 +4327,7 @@ "DAOLITY": "Daolity", "DAOP": "Dao Space", "DAOSOL": "MonkeDAO", + "DAOSQUARE": "DAOSquare Governance Token", "DAOVC": "DAO.VC", "DAOX": "Daox", "DAPP": "Pencils Protocol", @@ -4250,7 +4366,9 @@ "DAT": "Datum", "DATA": "Streamr", "DATAB": "Databot", + "DATAEC": "DATA Economy Index", "DATAMALL": "Datamall Coin", + "DATAMINE": "Datamine", "DATAO": "Data Ownership Protocol", "DATAWALLET": "DataWallet", "DATBOI": "Dat Boi", @@ -4347,6 +4465,7 @@ "DDRST": "DigiDinar StableToken", "DDRT": "DigiDinar Token", "DDS": "DDS.Store", + "DDUSDV1": "Decentralized USD", "DDX": "DerivaDAO", "DEA": "Degas Coin", "DEAI": "Zero1 Lab", @@ -4379,6 +4498,7 @@ "DEEPSEEK": "Global DePIN Chain", "DEEPSEEKAI": "DeepSeek AI Agent", "DEEPSEEKR1": "DeepSeek R1", + "DEEPSPACE": "DeepSpace", "DEER": "ToxicDeer Finance", "DEERSEIZED": "Deer Seized by US Government", "DEESSE": "Deesse", @@ -4475,6 +4595,7 @@ "DEVT": "DeHorizon", "DEVVE": "Devve", "DEVX": "Developeo", + "DEW": "DEW", "DEX": "DEX", "DEX223": "DEX223", "DEXA": "DEXA COIN", @@ -4531,6 +4652,7 @@ "DGLD": "Digital Gold", "DGLN": "Dogelana", "DGM": "DigiMoney", + "DGMA": "daGama", "DGME": "GameStop Tokenized Stock Defichain", "DGMS": "Digigems", "DGMT": "DigiMax DGMT", @@ -4541,6 +4663,7 @@ "DGORE": "DogeGoreCoin", "DGP": "DGPayment", "DGPT": "DigiPulse", + "DGRAM": "Datagram", "DGTA": "Digitra.com Token", "DGTX": "Digitex Token", "DGV1": "Decentral Games v1", @@ -4629,6 +4752,7 @@ "DIPA": "Doge Ipa", "DIRTY": "Dirty Street Cats", "DIS": "DisChain", + "DISCO": "Disco By Matt Furie", "DISCOVERY": "DiscoveryIoT", "DISK": "Dark Lisk", "DISPEPE": "Disabled Pepe", @@ -4658,6 +4782,7 @@ "DKS": "DarkShield", "DKT": "Duelist King", "DKUMA": "KumaDex Token", + "DL": "Dill", "DLA": "Dolla", "DLANCE": "DeeLance", "DLB": "DiemLibre", @@ -4867,6 +4992,7 @@ "DONJR": "Don Jr.", "DONK": "Don-key", "DONKE": "DONKE", + "DONKEY": "donkey", "DONNIEFIN": "Donnie Finance", "DONS": "The Dons", "DONT": "Donald Trump (dont.cash)", @@ -4887,9 +5013,10 @@ "DOPEX": "DOPE", "DOPU": "DOPU The Dog with A Purpose", "DOR": "Dorado", - "DORA": "Dora Factory", + "DORA": "DORA", "DORAEMON": "Doraemon", "DORAV1": "Dora Factory v1", + "DORAV2": "Dora Factory", "DORK": "DORK", "DORKL": "DORK LORD", "DORKVADER": "DorkVader", @@ -4962,6 +5089,7 @@ "DREAM21": "Dream21", "DREAMS": "Dreams Quest", "DREP": "DREP", + "DRESS": "Dress", "DRF": "Drife", "DRG": "Dragon Coin", "DRGN": "Dragonchain", @@ -5038,6 +5166,7 @@ "DTRC": "Datarius", "DTRUMP": "Degen Trump", "DTSLA": "Tesla Tokenized Stock Defichain", + "DTV": "DraperTV", "DTX": "DataBroker DAO", "DUA": "Brillion", "DUAL": "Dual Finance", @@ -5050,14 +5179,14 @@ "DUC": "DucatusCoin", "DUCAT": "Ducat", "DUCATO": "Ducato Protocol Token", - "DUCK": "Unit Protocol New", + "DUCK": "DuckChain Token", "DUCKAI": "Duck AI", "DUCKC": "DuckCoin", "DUCKD": "DuckDuckCoin", "DUCKER": "Ducker", "DUCKIES": "Yellow Duckies", "DUCKO": "Duck Off Coin", - "DUCKV1": "COL", + "DUCKV1": "UNITPROV1", "DUCKY": "Ducky Duck", "DUCX": "DucatusX", "DUDE": "DuDe", @@ -5082,9 +5211,10 @@ "DUREV": "Povel Durev", "DUROV": "FREE DUROV", "DURTH": "iShares MSCI World ETF Tokenized Stock Defichain", - "DUSD": "Decentralized USD", + "DUSD": "StandX DUSD", "DUSK": "Dusk Network", - "DUST": "DUST Protocol", + "DUST": "Dust", + "DUSTPROTOCOL": "DUST Protocol", "DUSTY": "Dusty", "DUX": "DuxCoin", "DUZCE": "Duzce Token", @@ -5140,6 +5270,7 @@ "DYNA": "Dynamix", "DYNAM": "Dynamic Crypto Index", "DYNAMICTRADING": "Dynamic Trading Rights", + "DYNASTYGLOB": "Dynasty Global Investments AG", "DYNCOIN": "Dyncoin", "DYNEX": "Dynex GPU", "DYNMT": "Dynamite", @@ -5180,9 +5311,11 @@ "EARNM": "EARNM", "EARTH": "Earth Token", "EARTHCOIN": "EarthCoin", + "EARTHM": "Earthmeta", + "EASY": "EASY", "EASYF": "EasyFeedback", "EASYMINE": "EasyMine", - "EAT": "EDGE Activity Token", + "EAT": "375ai", "EATH": "Eartherium", "EAURIC": "Eauric", "EAVE": "EaveAI", @@ -5250,11 +5383,14 @@ "EDDIE": "Eddie coin", "EDE": "El Dorado Exchange", "EDEL": "Coin Edelweis", - "EDEN": "EDEN", + "EDEN": "Eden Token", + "EDENA": "EDENA", + "EDENNETWORK": "EDEN", "EDEXA": "edeXa Security Token", "EDFI": "EdFi", "EDG": "Edgeless", "EDGE": "Definitive", + "EDGEACTIVITY": "EDGE Activity Token", "EDGEN": "LayerEdge", "EDGENET": "EDGE", "EDGESOL": "Edgevana Staked SOL", @@ -5266,6 +5402,7 @@ "EDNS": "EDNS Token", "EDOG": "EDOG", "EDOGE": "ElonDoge", + "EDOM": "EDOM", "EDR": "Endor Protocol Token", "EDRC": "EDRCoin", "EDSE": "Eddie Seal", @@ -5317,6 +5454,7 @@ "EGOCOIN": "EGOcoin", "EGOD": "EgodCoin", "EGOLD": "EGOLD", + "EGOLDGG": "eGold", "EGON": "EgonCoin", "EGR": "Egoras Rights", "EGRN": "Energreen", @@ -5378,6 +5516,7 @@ "ELIXIR": "Starchi", "ELIZ": "Eliza (ai16zeliza)", "ELIZA": "Eliza (elizawakesup.ai)", + "ELIZAOS": "ElizaOS", "ELK": "Elk Finance", "ELLA": "Ellaism", "ELLI": "ElliotCoin", @@ -5453,6 +5592,7 @@ "EMP": "Emp Money", "EMPC": "EmporiumCoin", "EMPH": "Emphy", + "EMPI": "Emperor", "EMPIRE": "Empire Token", "EMPR": "empowr", "EMR": "Emorya Finance", @@ -5473,11 +5613,13 @@ "ENCX": "Encrybit", "ENDCEX": "Endpoint CeX Fan Token", "ENDLESS": "Endless Board Game", + "ENDOR": "Endor Protocol Token", "ENE": "EneCoin", "ENEAR": "Near (Energiswap)", "ENEDEX": "Enedex", "ENERGYLEDGER": "Energy Ledger", "ENERGYX": "Safe Energy", + "ENF": "enfineo", "ENG": "Enigma", "ENGT": "Engagement Token", "ENIGMA": "ENIGMA", @@ -5493,6 +5635,7 @@ "ENRON": "Enron", "ENRX": "Enrex", "ENS": "Ethereum Name Service", + "ENSO": "Enso", "ENT": "Eternity", "ENTC": "EnterButton", "ENTER": "EnterCoin", @@ -5616,6 +5759,7 @@ "ET": "ENDO", "ET4": "Eticket4", "ETALON": "Etalon", + "ETAN": "Etarn", "ETBS": "EthBits", "ETBT": "Ethereum Black", "ETC": "Ethereum Classic", @@ -5624,6 +5768,7 @@ "ETERNAL": "CryptoMines Eternal", "ETERNALC": "Eternal Coin", "ETERNALT": "Eternal Token", + "ETF500": "Elon Trump Fart", "ETFETH": "ETFETH", "ETG": "Ethereum Gold", "ETGM": "ETGM", @@ -5708,6 +5853,7 @@ "EURE": "Monerium EUR emoney", "EUREV1": "Monerium EUR emoney v1", "EURI": "Eurite", + "EURL": "LUGH", "EURN": "NOKU EUR", "EURO3": "EURO3", "EUROB": "Etherfuse EUROB", @@ -5719,14 +5865,17 @@ "EURR": "StablR Euro", "EURRV1": "StablR Euro v1", "EURS": "STASIS EURS", + "EURST": "EURO Stable Token", "EURT": "Euro Tether", "EURTV1": "Euro Tether v1", "EURU": "Upper Euro", "EURX": "eToro Euro", "EUSD": "Egoras Dollar", + "EUT": "EarnUp Token", "EUTBL": "Spiko EU T-Bills Money Market Fund", "EV": "EVAI", "EVA": "Evadore", + "EVAA": "EVAA Protocol", "EVAI": "EVA Intelligence", "EVAL": "Chromia's EVAL by Virtuals", "EVAN": "Evanesco Network", @@ -5848,6 +5997,7 @@ "FACTORY": "ChainFactory", "FACTR": "Defactor", "FACTRPAY": "FactR", + "FACY": "ArAIstotle Fact Checker", "FADO": "FADO Go", "FAFO": "FAFO", "FAFOSOL": "Fafo", @@ -5877,6 +6027,7 @@ "FANG": "FANG Token", "FANS": "Fantasy Cash", "FANT": "Phantasia", + "FANTC": "FANtium Tennis Coin", "FANTOM": "Fantom Maker", "FANV": "FanVerse", "FANX": "FrontFanz", @@ -5918,6 +6069,7 @@ "FB": "Fractal Bitcoin", "FBA": "Firebird Aggregator", "FBB": "FilmBusinessBuster", + "FBD": "Fiboard", "FBG": "Fort Block Games", "FBN": "Five balance", "FBNB": "ForeverBNB", @@ -5982,11 +6134,12 @@ "FET": "Artificial Superintelligence Alliance", "FETCH": "Fetch", "FETS": "FE TECH", + "FETV1": "Fetch v1", "FEUSD": "Felix feUSD", "FEVR": "RealFevr", "FEX": "FEX Token", "FEY": "Feyorra", - "FF": "Forefront", + "FF": "Falcon Finance", "FF1": "Two Prime FF1 Token", "FFA": "Cryptofifa", "FFC": "FireflyCoin", @@ -5999,6 +6152,7 @@ "FGC": "FantasyGold", "FGD": "Freedom God DAO", "FGM": "Feels Good Man", + "FGPT": "FurGPT", "FGT": "Flozo Game Token", "FGZ": "Free Game Zone", "FHB": "FHB", @@ -6006,6 +6160,7 @@ "FHM": "FantOHM", "FI": "Fideum", "FIA": "FIA Protocol", + "FIATDAO": "FIAT DAO Token", "FIBO": "FibSWAP DEx", "FIBOS": "FIBOS", "FIBRE": "FIBRE", @@ -6126,6 +6281,7 @@ "FLIP": "Chainflip", "FLIX": "OmniFlix Network", "FLIXX": "Flixxo", + "FLK": "Fleek", "FLL": "Feellike", "FLLW": "Follow Coin", "FLM": "Flamingo", @@ -6202,6 +6358,7 @@ "FMC": "Fimarkcoin", "FME": "FME", "FMEX": "FMex", + "FMF": "Formosa Financial Token", "FMG": "FM Gallery", "FML": "FormulA", "FMT": "Finminity", @@ -6243,6 +6400,7 @@ "FOLD": "Manifold Finance", "FOLGORYUSD": "FolgoryUSD", "FOLGORYUSDV1": "FolgoryUSD", + "FOLKS": "Folks Finance", "FOLO": "Alpha Impact", "FOM": "FOMO BULL CLUB", "FOMO": "Fomo", @@ -6263,7 +6421,8 @@ "FORCE": "TriForce Tokens", "FORCEC": "Force Coin", "FORE": "FORE Protocol", - "FOREST": "FOREST", + "FOREFRONT": "Forefront", + "FOREST": "Forest", "FORESTPLUS": "The Forbidden Forest", "FOREVER": "Forever Coin", "FOREVERFOMO": "ForeverFOMO", @@ -6320,6 +6479,7 @@ "FRANK": "Frank", "FRANKLIN": "Franklin", "FRATT": "Frogg and Ratt", + "FRAX": "Frax Share", "FRAXLEGACY": "Frax", "FRAZ": "FrazCoin", "FRBK": " FreeBnk", @@ -6346,6 +6506,7 @@ "FREN": "FREN", "FRENC": "Frencoin", "FRENCH": "French On Base", + "FRENLY": "Frenly", "FRENPET": "Fren Pet", "FRENS": "Farmer Friends", "FRESCO": "Fresco", @@ -6407,7 +6568,7 @@ "FSN": "Fusion", "FSNV1": "Fusion v1", "FSO": "FSociety", - "FST": "Futureswap", + "FST": "FreeStyle Token", "FSTC": "FastCoin", "FSTR": "Fourth Star", "FSW": "Falconswap", @@ -6422,6 +6583,7 @@ "FTK": "FToken", "FTM": "Fantom", "FTMO": "Fantom Oasis", + "FTMX": "FUCK THE MATRIX", "FTN": "Fasttoken", "FTO": "FuturoCoin", "FTON": "Fanton", @@ -6491,6 +6653,7 @@ "FUTUR": "Future Token", "FUTURE": "FutureCoin", "FUTUREAI": "Future AI", + "FUTURESWAP": "Futureswap", "FUZE": "FUZE Token", "FUZEX": "FuzeX", "FUZN": "Fuzion", @@ -6498,6 +6661,7 @@ "FVT": "Finance Vote", "FWATCH": "Foliowatch", "FWB": "Friends With Benefits Pro", + "FWBV1": "Friends With Benefits Pro v1", "FWC": "Qatar 2022", "FWH": "FigureWifHat", "FWOG": "Fwog", @@ -6513,7 +6677,6 @@ "FXI": "FX1 Sports", "FXN": "FXN", "FXP": "FXPay", - "FXS": "Frax Share", "FXST": "FX Stock Token", "FXT": "Frog X Toad 6900", "FXUSD": "f(x) Protocol fxUSD", @@ -6537,11 +6700,13 @@ "GAFA": "Gafa", "GAFI": "GameFi", "GAGA": "Gaga", - "GAI": "GraphGrail AI", + "GAI": "GraphAI", "GAIA": "Gaia Token", "GAIAE": "Gaia Everworld", "GAIAPLATFORM": "GAIA Platform", - "GAIN": "Gainfy", + "GAIB": "GAIB", + "GAIN": "GriffinAI", + "GAINFY": "Gainfy", "GAINS": "Gains", "GAINSV1": "Gains v1", "GAJ": "Gaj Finance", @@ -6616,6 +6781,7 @@ "GATA": "Gata", "GATCOIN": "GATCOIN", "GATE": "GATENet", + "GATEUSD": "GUSD", "GATEWAY": "Gateway Protocol", "GATHER": "Gather", "GATSBY": "Gatsby Inu", @@ -6628,6 +6794,7 @@ "GB": "GoldBlocks", "GBA": "Geeba", "GBC": "Green Blue Coin", + "GBCK": "GoldBrick", "GBCR": "Gold BCR", "GBD": "Great Bounty Dealer", "GBE": "Godbex", @@ -6636,6 +6803,7 @@ "GBIT": "GravityBit", "GBK": "Goldblock", "GBL": "Global Token", + "GBNB": "GOLD BNB", "GBO": "Gabro.io", "GBOT": "GBOT", "GBOY": "GameBoy", @@ -6710,6 +6878,9 @@ "GENI": "Genius", "GENIE": "The Genie", "GENIEC": "GenieCoin", + "GENIESWAP": "GenieSwap", + "GENIESWAPV1": "GenieSwap v1", + "GENIFYART": "Genify ART", "GENIX": "Genix", "GENO": "GenomeFi", "GENOME": "GenomesDao", @@ -6761,6 +6932,7 @@ "GGB": "GGEBI", "GGC": "Global Game Coin", "GGCM": "Gold Guaranteed Coin", + "GGEZ1": "GGEZ1", "GGG": "Good Games Guild", "GGGG": "Good Game Gary Gensler", "GGH": "Green Grass Hopper", @@ -6793,7 +6965,8 @@ "GHNY": "Grizzly Honey", "GHO": "GHO", "GHOAD": "GhoadCoin", - "GHOST": "GhostbyMcAfee", + "GHOST": "GhostwareOS", + "GHOSTBY": "GhostbyMcAfee", "GHOSTCOIN": "GhostCoin", "GHOSTM": "GhostMarket", "GHOUL": "Ghoul Coin", @@ -6816,7 +6989,8 @@ "GIGACHAD": "GigaChad", "GIGAG": "GIGAGEEK", "GIGASWAP": "GigaSwap", - "GIGGLE": "Giggle Academy", + "GIGGLE": "Giggle Fund", + "GIGGLEACADEMY": "Giggle Academy", "GIGS": "Climate101", "GIGX": "GigXCoin", "GIKO": "Giko Cat", @@ -6892,7 +7066,7 @@ "GLUE": "Glue", "GLX": "GalaxyCoin", "GLYPH": "GlyphCoin", - "GM": "GM", + "GM": "GOMBLE", "GMA": "Goldchip Mining Asset", "GMAC": "Gemach", "GMAT": "GoWithMi", @@ -6928,6 +7102,7 @@ "GMTT": "GMT Token", "GMUBARAK": "Ghibli Mubarak", "GMUSD": "GND Protocol", + "GMWAGMI": "GM", "GMX": "GMX", "GN": "GN", "GNBT": "Genebank Token", @@ -6958,6 +7133,7 @@ "GOAT": "Goatseus Maximus", "GOATAI": "GOAT AI", "GOATCOIN": "Goat", + "GOATED": "Goat Network", "GOATS": "GOATS", "GOATSE": "GOATSE", "GOB": "gob", @@ -6980,6 +7156,7 @@ "GOFX": "GooseFX", "GOG": "Guild of Guardians", "GOGLZ": "GOGGLES", + "GOGLZV1": "GOGGLES v1", "GOGO": "GOGO Finance", "GOGU": "GOGU Coin", "GOHOME": "GOHOME", @@ -6996,7 +7173,6 @@ "GOLDEN": "Golden Inu", "GOLDENC": "GoldenCat", "GOLDENG": "Golden Goose", - "GOLDENPACT": "GOLDEN PACT", "GOLDEX": "Goldex", "GOLDF": "Gold Fever", "GOLDMIN": "GoldMiner", @@ -7026,6 +7202,7 @@ "GOO": "Gooeys", "GOOCH": "Gooch", "GOOD": "Goodomy", + "GOODBOY": "GoodBoy", "GOODM": "Good Morning!", "GOODMO": "Good Morning", "GOOG": "Googly Cat", @@ -7052,7 +7229,7 @@ "GOS": "Gosama", "GOSS": "GOSSIP-Coin", "GOST": "SoulCoin", - "GOT": "ParkinGo", + "GOT": "GOLDEN PACT", "GOTEM": "gotEM", "GOTG": "Got Guaranteed", "GOTTI": "Gotti Token", @@ -7101,6 +7278,7 @@ "GRANDCOIN": "GrandCoin", "GRANDMA": "Grandma", "GRAPE": "GrapeCoin", + "GRAPHGRAIAI": "GraphGrail AI", "GRASS": "Grass", "GRAV": "Graviton", "GRAVITAS": "Gravitas", @@ -7137,6 +7315,7 @@ "GRIMEX": "SpaceGrime", "GRIN": "Grin", "GRIND": "Self Improving", + "GRIPPY": "GRIPPY", "GRL": "Greelance", "GRLC": "Garlicoin", "GRM": "GridMaster", @@ -7276,6 +7455,7 @@ "GVC": "Global Virtual Coin", "GVE": "Globalvillage Ecosystem", "GVL": "Greever", + "GVNR": "GVNR", "GVR": "Grove [OLD]", "GVRV1": "Grove v1", "GVT": "Genesis Vision", @@ -7307,6 +7487,7 @@ "GZX": "GreenZoneX", "Glo Dollar": "USDGLO", "H": "Humanity", + "H1": "Haven1", "H1DR4": "H1DR4 by Virtuals", "H2O": "H2O Dao", "H2ON": "H2O Securities", @@ -7325,7 +7506,9 @@ "HAGGIS": "New Born Haggis Pygmy Hippo", "HAHA": "Hasaki", "HAI": "Hacken Token", + "HAIO": "HAiO", "HAIR": " HairDAO", + "HAJIMI": "哈基米", "HAKA": "TribeOne", "HAKKA": "Hakka Finance", "HAKU": "HakuSwap", @@ -7346,8 +7529,9 @@ "HAMSTERB": "HamsterBase", "HAMSTR": "Hamster Coin", "HAN": "HanChain", - "HANA": "Hana", + "HANA": "Hana Token", "HANACOIN": "Hanacoin", + "HANAETH": "Hana", "HANAETHCTO": "HANA", "HAND": "ShowHand", "HANDY": "Handy", @@ -7364,6 +7548,7 @@ "HARD": "Kava Lend", "HARE": "Hare Token", "HAREPLUS": "Hare Plus", + "HARIKO": "Inu Hariko", "HAROLD": "Harold", "HAROLDDUCK": "Harold", "HARPER": "Harper", @@ -7385,6 +7570,7 @@ "HATCHY": "Hatchyverse", "HATI": "Hati", "HAUS": "DAOhaus", + "HAVEN": "Haven", "HAVOC": "Havoc", "HAVY": "Havy", "HAW": "Hawk Tuah", @@ -7697,6 +7883,7 @@ "HOSTAI": "Host AI", "HOT": "Holo", "HOTCROSS": "Hot Cross", + "HOTDOGE": "Hot Doge", "HOTKEY": "HotKeySwap", "HOTMOON": "HotMoon Token", "HOTN": "HotNow", @@ -7818,6 +8005,7 @@ "HXXH": "Pioneering D. UTXO-Based NFT Social Protocol", "HYBN": "Hey Bitcoin", "HYBRID": "Hybrid Bank Cash", + "HYBUX": "HYBUX", "HYC": "HYCON", "HYCO": "HYPERCOMIC", "HYD": "HYDRA", @@ -7826,6 +8014,7 @@ "HYDRO": "Hydro", "HYDROMINER": "Hydrominer", "HYDROP": "Hydro Protocol", + "HYDX": "Hydrex", "HYGH": "HYGH", "HYN": "Hyperion", "HYP": "HyperX", @@ -7838,6 +8027,7 @@ "HYPERD": "HyperDAO", "HYPERFLY": "HyperFly", "HYPERIONX": "HyperionX", + "HYPERLEND": "HyperLend", "HYPERS": "HyperSpace", "HYPERSKIDS": "HYPERSKIDS", "HYPERSTAKE": "HyperStake", @@ -7851,6 +8041,7 @@ "HZ": "Horizon", "HZD": "HorizonDollar", "HZM": "HZM Coin", + "HZMV1": "HZM Coin v1", "HZN": "Horizon Protocol", "HZT": "HazMatCoin", "I0C": "I0coin", @@ -7949,6 +8140,7 @@ "IEC": "IvugeoEvolutionCoin", "IETH": "iEthereum", "IF": "Impossible Finance", + "IFAI": "InfluxAI Token", "IFBTC": "Ignition FBTC", "IFC": "Infinite Coin", "IFIT": "CALO INDOOR", @@ -8025,13 +8217,14 @@ "INA": "pepeinatux", "INARI": "Inari", "INB": "Insight Chain", - "INC": "Incrementum", + "INC": "WAT Income token", "INCAKE": "InfinityCAKE", "INCEPT": "Incept", "INCNT": "Incent", "INCO": "InfinitiCoin", "INCORGNITO": "Incorgnito", "INCP": "InceptionCoin", + "INCREMENTUM": "Incrementum", "IND": "Indorse", "INDAY": "Independence Day", "INDEPENDENCEDAY": "Independence Day", @@ -8065,6 +8258,7 @@ "INFTT": "iNFT Token", "INFX": "Influxcoin", "ING": "Infinity Games", + "INI": "InitVerse", "INIT": "Initia", "INJ": "Injective", "INK": "Ink", @@ -8099,7 +8293,7 @@ "INTE": "InteractWith", "INTELLIQUE": "KARASOU", "INTER": "Inter Milan Fan Token", - "INTERN": "Interns", + "INTERN": "intern", "INTL": "Intelly", "INTO": "Influ Token", "INTR": "Interlay", @@ -8150,6 +8344,7 @@ "IP": "Story", "IP3": "Cripco", "IPAD": "Infinity Pad", + "IPAX": "Icopax", "IPC": "IPChain", "IPDN": "IPDnetwork", "IPL": "VouchForMe", @@ -8182,6 +8377,7 @@ "IRONBSC": "Iron BSC", "IRONCOIN": "IRONCOIN", "IRT": "Infinity Rocket", + "IRWA": "IncomRWA", "IRYDE": "iRYDE COIN", "ISA": "Islander", "ISDT": "ISTARDUST", @@ -8211,6 +8407,7 @@ "ITAM": "ITAM Games", "ITAMCUBE": "CUBE", "ITC": "IoT Chain", + "ITE": "Idle Tribe Era", "ITEM": "ITEMVERSE", "ITF": "Intelligent Trading", "ITG": "iTrust Governance", @@ -8247,6 +8444,7 @@ "IWT": "IwToken", "IX": "X-Block", "IXC": "IXcoin", + "IXFI": "IXFI", "IXIR": "IXIR", "IXORA": "IXORAPAD", "IXP": "IMPACTXPRIME", @@ -8258,6 +8456,7 @@ "IZER": "IZEROIUM", "IZI": "Izumi Finance", "IZICHAIN": "IZIChain", + "IZKY": "IZAKAYA", "IZX": "IZX", "IZZY": "Izzy", "InBit": "PrepayWay", @@ -8271,6 +8470,7 @@ "JADE": "Jade Protocol", "JADEC": "Jade Currency", "JAE": "JaeCoin", + "JAGER": "Jager Hunter", "JAGO": "Jagotrack", "JAI": "Japanese Akita Inu", "JAIHO": "Jaiho Crypto", @@ -8285,6 +8485,7 @@ "JANITOR": "Janitor", "JANRO": "Janro The Rat", "JAPAN": "Japan Open Chain", + "JAPANCONTENTT": "Japan Content Token", "JAR": "Jarvis+", "JARED": "Jared From Subway", "JARVIS": "Jarvis AI", @@ -8305,7 +8506,7 @@ "JCG": "JustCarbon Governance", "JCO": "JennyCo", "JCR": "JustCarbon Removal", - "JCT": "Japan Content Token", + "JCT": "Janction", "JDAI": "Dai (TON Bridge)", "JDC": "JustDatingSite", "JDO": "JINDO", @@ -8386,6 +8587,7 @@ "JNX": "Janex", "JNY": "JNY", "JOB": "Jobchain", + "JOBIESS": "JobIess", "JOBS": "JobsCoin", "JOBSEEK": "JobSeek AI", "JOC": "Speed Star JOC", @@ -8558,6 +8760,7 @@ "KASPY": "KASPY", "KASSIAHOME": "Kassia Home", "KASTA": "Kasta", + "KASTER": "King Aster", "KAT": "Karat", "KATA": "Katana Inu", "KATANA": "Katana Finance", @@ -8656,6 +8859,7 @@ "KFX": "KnoxFS", "KGB": "KGB protocol", "KGC": "Krypton Galaxy Coin", + "KGEN": "KGeN", "KGO": "Kiwigo", "KGT": "Kaby Gaming Token", "KHAI": "khai", @@ -8681,6 +8885,7 @@ "KILT": "KILT Protocol", "KIM": "KIM Token", "KIMA": "Kima", + "KIMBA": "The White Lion", "KIMBO": "Kimbo", "KIMCHI": "KIMCHI.finance", "KIMIAI": "Kimi AI Agent", @@ -8732,6 +8937,8 @@ "KITA": "KITA INU", "KITE": "Kite", "KITEAI": "KITEAI", + "KITEHAI": "Kite", + "KITKAT": "Remember KitKat", "KITSU": "Kitsune Inu", "KITTE": "Kittekoin", "KITTENS": "Kitten Coin", @@ -8756,6 +8963,8 @@ "KLEVA": "KLEVA Protocol", "KLICKZIE": "Klickzie", "KLIMA": "KlimaDAO", + "KLINK": "Klink Finance", + "KLIP": "KLIP AI", "KLK": "Klickl Token", "KLKS": "Kalkulus", "KLKSYNC": "KLK Sync Protocol", @@ -8797,6 +9006,7 @@ "KNTO": "Kento", "KNU": "Keanu", "KNUT": "Knut From Zoo", + "KNUXX": "Knuxx Bully of ETH", "KNW": "Knowledge", "KOAI": "KOI", "KOALA": "KOALA", @@ -8824,6 +9034,7 @@ "KOLANA": "KOLANA", "KOLION": "Kolion", "KOLT": "Kolt", + "KOLZ": "KOLZ", "KOM": "Kommunitas", "KOMA": "Koma Inu", "KOMO": "Komoverse", @@ -8974,11 +9185,13 @@ "L3USD": "L3USD", "L7": "L7", "LA": "Lagrange", - "LAB": "Labrys", + "LAB": "LAB", "LABORCRYPTO": "LaborCrypto", "LABRA": "LabraCoin", + "LABRYS": "Labrys", "LABS": "LABS Group", - "LABUBU": "Labubu", + "LABUBUORG": "Labubu", + "LABUBUSOL": "LABUBU", "LABX": "Stakinglab", "LABZ": "Insane Labz", "LABZBASE": "Insane Labz (Base)", @@ -9146,6 +9359,8 @@ "LET": "LinkEye", "LETIT": "Letit", "LETS": "Let's WIN This", + "LETSBONK": "Let's BONK", + "LETSGETHAI": "Let's Get HAI", "LETSGO": "Lets Go Brandon", "LEU": "CryptoLEU", "LEV": "Levante U.D. Fan Token", @@ -9215,8 +9430,9 @@ "LIFETOKEN": "LIFE", "LIFT": "Uplift", "LIGER": "Ligercoin", - "LIGHT": "Light", + "LIGHT": "LIGHT", "LIGHTCHAIN": "LightChain", + "LIGHTHEAVEN": "Light", "LIGHTSPEED": "LightSpeedCoin", "LIGMA": "Ligma Node", "LIGO": "Ligo", @@ -9245,6 +9461,7 @@ "LINK": "Chainlink", "LINKA": "LINKA", "LINKC": "LINKCHAIN", + "LINKCHAIN": "LINK", "LINKFI": "LinkFi", "LINQ": "LINQ", "LINSPIRIT": "linSpirit", @@ -9280,6 +9497,7 @@ "LITION": "Lition", "LITT": "LitLab Games", "LITTLEGUY": "just a little guy", + "LITTLEMANYU": "Little Manyu", "LIV": "LiviaCoin", "LIVE": "TRONbetLive", "LIVENCOIN": "LivenPay", @@ -9320,10 +9538,11 @@ "LMR": "Lumerin", "LMT": "LIMITUS", "LMTOKEN": "LM Token", + "LMTS": "Limitless Official Token", "LMWR": "LimeWire Token", "LMXC": "LimonX", "LMY": "Lunch Money", - "LN": "LINK", + "LN": "Lnfi Network", "LNC": "Blocklancer", "LNCHM": "Launchium", "LND": "Lendingblock", @@ -9384,7 +9603,7 @@ "LONGM": "Long Mao", "LONGSHINE": "LongShine", "LOOBY": "Looby by Stephen Bliss", - "LOOK": "LookCoin", + "LOOK": "LOOK", "LOOKS": "LooksRare", "LOOM": "Loom Network", "LOOMV1": "Loom Network v1", @@ -9551,6 +9770,7 @@ "LUNG": "LunaGens", "LUNR": "Lunr Token", "LUPIN": "LUPIN", + "LUR": "Lumera", "LUS": "Luna Rush", "LUSD": "Liquity USD", "LUSH": "Lush AI", @@ -9590,7 +9810,8 @@ "LYL": "LoyalCoin", "LYM": "Lympo", "LYMPO": "Lympo Market Token", - "LYN": "LYNCHPIN Token", + "LYN": "Everlyn Token", + "LYNCHPIN": "LYNCHPIN Token", "LYNK": "Lynked.World", "LYNX": "Lynex", "LYNXCOIN": "Lynx", @@ -9609,6 +9830,7 @@ "LZM": "LoungeM", "LZUSDC": "LayerZero Bridged USDC (Fantom)", "M": "MemeCore", + "M0": "M by M^0", "M1": "SupplyShock", "M2O": "M2O Token", "M3M3": "M3M3", @@ -9672,6 +9894,7 @@ "MAI": "MAI", "MAIA": "Maia", "MAID": "MaidSafe Coin", + "MAIGA": "MAIGA Token", "MAIL": "CHAINMAIL", "MAINSTON": "Ston", "MAIV": "MAIV", @@ -9713,7 +9936,7 @@ "MANTI": "Mantis", "MANTLE": "Mantle", "MANUSAI": "Manus AI Agent", - "MANYU": "Little Manyu", + "MANYU": "Manyu", "MANYUDOG": "MANYU", "MAO": "Mao", "MAOW": "MAOW", @@ -9723,6 +9946,7 @@ "MAPO": "MAP Protocol", "MAPR": "Maya Preferred 223", "MAPS": "MAPS", + "MAPU": "MatchAwards Platform Utility Token", "MAR3": "Mar3 AI", "MARCO": "MELEGA", "MARCUS": "Marcus Cesar Inu", @@ -9792,6 +10016,7 @@ "MATRIXLABS": "Matrix Labs", "MATT": "Matt Furie", "MATTER": "AntiMatter", + "MATTLE": "MattleFun", "MAU": "MAU", "MAUW": "MAUW", "MAV": "Maverick Protocol", @@ -9877,6 +10102,7 @@ "MCEUR": "Moola Celo EUR", "MCF": "MCFinance", "MCG": "MicroChains Gov Token", + "MCGA": "Make CRO Great Again", "MCH": "Meconcash", "MCHC": "My Crypto Heroes", "MCI": "Musiconomi", @@ -9891,6 +10117,7 @@ "MCONTENT": "MContent", "MCP": "My Crypto Play", "MCPC": "Mobile Crypto Pay Coin", + "MCQ": "Mecha Conquest", "MCRC": "MyCreditChain", "MCRN": "MacronCoin", "MCRT": "MagicCraft", @@ -10008,6 +10235,7 @@ "MEMEX": "Meme Index", "MEMHASH": "Memhash", "MEMORYCOIN": "MemoryCoin", + "MEMUSIC": "MeMusic", "MEN": "METAHUB FINANCE", "MENDI": "Mendi Finance", "MENGO": "Flamengo Fan Token", @@ -10039,7 +10267,7 @@ "MESH": "MeshBox", "MESSI": "MESSI COIN", "MESSU": "Loinel Messu", - "MET": "Metronome", + "MET": "Meteora", "META": "MetaDAO", "METAA": "META ARENA", "METABOT": "Robot Warriors", @@ -10055,6 +10283,7 @@ "METADOGEV1": "MetaDoge V1", "METADOGEV2": "MetaDoge V2", "METAF": "MetaFastest", + "METAFIGHTER": "MetaFighter", "METAG": "MetagamZ", "METAGEAR": "MetaGear", "METAIVERSE": "MetAIverse", @@ -10063,6 +10292,8 @@ "METAMEME": "met a meta metameme", "METAMUSK": "Musk Metaverse", "METAN": "Metan Evolutions", + "METANIA": "METANIA V2", + "METANIAV1": "METANIAGAMES", "METANO": "Metano", "METAPK": "Metapocket", "METAQ": "MetaQ", @@ -10072,6 +10303,7 @@ "METATR": "MetaTrace Utility Token", "METAUFO": "MetaUFO", "METAV": "METAVERSE", + "METAV1": "META v1", "METAVE": "Metaverse Convergence", "METAVERSEM": "MetaVerse-M", "METAVERSEX": "MetaverseX", @@ -10087,7 +10319,9 @@ "METM": "MetaMorph", "METO": "Metafluence", "METRO": "Metropoly", - "METV1": "Metronome", + "METRON": "Metronome", + "METRONV1": "Metronome", + "METYA": "Metya Token", "MEU": "MetaUnit", "MEV": "MEVerse", "MEVETH": "mevETH", @@ -10100,8 +10334,9 @@ "MEX": "MEX", "MEXC": "MEXC Token", "MEXP": "MOJI Experience Points", + "MEY": "Mey Network", "MEZZ": "MEZZ Token", - "MF": "MetaFighter", + "MF": "Moonwalk Fitness", "MF1": "Meta Finance", "MFAM": "Moonwell Apollo", "MFC": "MFCoin", @@ -10116,6 +10351,7 @@ "MFT": "Hifi Finance (Old)", "MFTM": "Fantom (Multichain)", "MFTU": "Mainstream For The Underground", + "MFUN": "MemeMarket", "MFUND": "Memefund", "MFX": "MFChain", "MG": "MinerGate Token", @@ -10130,7 +10366,7 @@ "MGLC": "MetaverseMGL", "MGLD": "Metallurgy", "MGN": "MagnaCoin", - "MGO": "MobileGo", + "MGO": "Mango Network", "MGOD": "MetaGods", "MGP": "MangoChain", "MGPT": "MotoGP Fan Token", @@ -10254,7 +10490,7 @@ "MIODIO": "MIODIOCOIN", "MIOTA": "IOTA", "MIR": "Mirror Protocol", - "MIRA": "Chains of War", + "MIRA": "Mira", "MIRACLE": "MIRACLE", "MIRACLETELE": "Miracle Tele", "MIRAI": "Project MIRAI", @@ -10282,7 +10518,7 @@ "MITO": "Mitosis", "MITTENS": "Mittens", "MITX": "Morpheus Infrastructure Token", - "MIU": "Miu", + "MIUONSOL": "Miu", "MIV": "MakeItViral", "MIVA": "Minerva Wallet", "MIVRS": "Minionverse", @@ -10328,6 +10564,7 @@ "MMAPS": "MapMetrics", "MMATIC": "Wrapped Polygon (Multichain)", "MMC": "Monopoly Millionaire Control", + "MMDAO": "MMDAO", "MMETA": "Duckie Land Multi Metaverse", "MMF": "MMFinance", "MMG": "Monopoly Millionaire Game", @@ -10340,7 +10577,7 @@ "MMS": "Marsverse", "MMSC": "MMSC PLATFORM", "MMSS": "MMSS (Ordinals)", - "MMT": "MeMusic", + "MMT": "Momentum", "MMTM": "Momentum", "MMUI": "MetaMUI", "MMULTI": "Multichain (via Multichain Cross-Chain Router)", @@ -10380,6 +10617,7 @@ "MNTL": "AssetMantle", "MNTO": "Minato", "MNTP": "GoldMint", + "MNTX": "Minutes Network Token", "MNV": "MonetaVerde", "MNVM": "Novam", "MNW": "Morpheus Network", @@ -10397,6 +10635,7 @@ "MOBIC": "Mobility Coin", "MOBIE": "MobieCoin", "MOBILE": "Helium Mobile", + "MOBILEGO": "MobileGo", "MOBIU": "Mobius Money", "MOBU": "MOBU", "MOBX": "MOBIX", @@ -10461,6 +10700,8 @@ "MOMO2025": "momo", "MON": "MON Protocol", "MONA": "MonaCoin", + "MONAD": "Monad", + "MONAI": "MONAI", "MONAIZE": "Monaize", "MONARCH": "TRUEMONARCH", "MONART": "Monart", @@ -10570,6 +10811,7 @@ "MOTHER": "Mother Iggy", "MOTI": "Motion", "MOTION": "motion", + "MOTIONCOIN": "Motion", "MOTO": "Motocoin", "MOUND": "Mound Token", "MOUTAI": "Moutai", @@ -10620,12 +10862,15 @@ "MRBOB": "MR BOB COIN", "MRCH": "MerchDAO", "MRCR": "Mercor Finance", + "MRDN": "Meridian", "MRF": "Moonradar.finance", "MRFOX": "Mr.FOX Token", "MRHB": "MarhabaDeFi", "MRI": "Marshall Inu", "MRK": "MARK.SPACE", "MRKX": "Merck xStock", + "MRLIGHTSPEED": "Mr. Lightspeed Creator Coin", + "MRLN": "Merlin Token", "MRM": "Mr Mint", "MRN": "Mercoin", "MRNA": "Moderna", @@ -10666,6 +10911,7 @@ "MSQ": "MSquare Global", "MSR": "Masari", "MST": "Idle Mystic", + "MSTABLEUSD": "mStable USD", "MSTAR": "MerlinStarter", "MSTETH": "Eigenpie mstETH", "MSTO": "Millennium Sapphire", @@ -10753,7 +10999,7 @@ "MURA": "Murasaki", "MURATIAI": "MuratiAI", "MUSCAT": "MusCat", - "MUSD": "mStable USD", + "MUSD": "MetaMask USD", "MUSDC": "USD Coin (Multichain)", "MUSDCOIN": "MUSDcoin", "MUSE": "Muse DAO", @@ -10797,6 +11043,7 @@ "MWD": "MEW WOOF DAO", "MWETH": "Moonwell Flagship ETH (Morpho Vault)", "MWH": "Melania Wif Hat", + "MWXT": "MWX Token", "MX": "MX Token", "MXC": "Machine Xchange Coin", "MXD": "Denarius", @@ -10832,6 +11079,7 @@ "MYROO": "Myro Dog", "MYROWIF": "MYROWIF", "MYST": "Mysterium", + "MYSTERY": "Mystery", "MYT": "Mytrade", "MYTH": "Mythos", "MYTHTOKEN": "Myth Token", @@ -10850,6 +11098,7 @@ "N1": "NFTify", "N3": "Network3", "N3DR": "NeorderDAO ", + "N3ON": "N3on", "N64": "N64", "N7": "Number7", "N8V": "NativeCoin", @@ -10895,6 +11144,7 @@ "NATI": "IlluminatiCoin", "NATION": "Nation3", "NATIX": "NATIX Network", + "NATO": "The Nation Token", "NATOR": "Pepenator", "NAUSICAA": "Nausicaa-Inu", "NAUT": "Nautilus Coin", @@ -10915,6 +11165,7 @@ "NAZA": "NAZA", "NAZAR": "NAZAR PROTOCOL", "NAZIELON": "NAZI ELON", + "NB": "Nubila Network", "NBABSC": "NBA BSC", "NBAI": "Nebula AI", "NBAR": "NOBAR", @@ -11028,6 +11279,7 @@ "NETRUM": "Netrum", "NETT": "Netswap", "NETVR": "Netvrk", + "NETX": "NetX Token", "NETZ": "MainnetZ", "NETZ1": "NETZERO", "NEU": "Neumark", @@ -11099,6 +11351,7 @@ "NFTART": "NFT Art Finance", "NFTB": "NFTb", "NFTBS": "NFTBooks", + "NFTCHAMPIONS": "NFT Champions", "NFTD": "NFTrade", "NFTE": "NFTEarthOFT", "NFTFI": "NFTfi", @@ -11231,6 +11484,7 @@ "NOGS": "Noggles", "NOHAT": "DogWifNoHat", "NOIA": "Syntropy", + "NOICE": "noice", "NOIS": "Nois Network", "NOIZ": "NOIZ", "NOKA": "Noka Solana AI", @@ -11263,10 +11517,11 @@ "NOTALION": "Not a lion, a...", "NOTC": "NOT", "NOTDOG": "NOTDOG", - "NOTE": "Notional Finance", + "NOTE": "Republic Note", "NOTECANTO": "Note", "NOTHING": "NOTHING", "NOTINU": "NOTCOIN INU", + "NOTIONAL": "Notional Finance", "NOV": "Novara Calcio Fan Token", "NOVA": "Nova Finance", "NOVAAI": "Nova AI", @@ -11355,6 +11610,7 @@ "NUMBERS": "NumbersCoin", "NUMI": "NUMINE Token", "NUMITOR": "Numitor", + "NUNU": "nunu", "NUR": "Nurcoin", "NURA": "Nura Labs", "NUSA": "Nusa", @@ -11450,7 +11706,7 @@ "OBSI": "Obsidium", "OBSR": "OBSERVER Coin", "OBSUSHI": "Sushi (OmniBridge)", - "OBT": "Oobit", + "OBT": "Orbiter Token", "OBTC": "Obitan Chain", "OBVIOUS": "OBVIOUS COIN", "OBX": "OpenBlox", @@ -11509,6 +11765,7 @@ "OFFICI": "OFFICIAL BARRON", "OFFICIA": "Official Elon Coin", "OFFICIALUSA": "Official USA Token", + "OFINTOKEN": "OFIN Token", "OFN": "Openfabric AI", "OFT": "ONFA", "OG": "OG Fan Token", @@ -11612,7 +11869,7 @@ "OMV1": "OM Token (v1)", "OMX": "Project Shivom", "OMZ": "Open Meta City", - "ON": "OFIN Token", + "ON": "Orochi Network Token", "ONAM": "ONAM", "ONC": "One Cash", "ONCH": "OnchainPoints.xyz", @@ -11645,12 +11902,14 @@ "ONTACT": "OnTact", "ONUS": "ONUS", "ONX": "OnX.finance", + "OOB": "Oobit", "OOE": "OpenOcean", "OOFP": "OOFP", "OOGI": "OOGI", "OOKI": "Ooki", "OOKS": "Onooks", "OOM": "OomerBot", + "OOPS": "OOPS", "OORC": "Orbit Bridge Klaytn Orbit Chain", "OORT": "OORT", "OOT": "Utrum", @@ -11674,9 +11933,10 @@ "OPENP": "Open Platform", "OPENRI": "Open Rights Exchange", "OPENSOURCE": "Open Source Network", + "OPENSWAP": "OpenSwap Optimism Token", "OPENVC": "OpenVoiceCoin", "OPENW": "OpenWorld", - "OPENX": "OpenSwap Optimism Token", + "OPENX": "OpenxAI", "OPEPE": "Optimism PEPE", "OPERATOR": "OpenAI Agent", "OPES": "Opes", @@ -11724,6 +11984,7 @@ "ORAO": "ORAO Network", "ORARE": "OneRare", "ORB": "KlayCity ORB", + "ORBD": "OrbitEdge", "ORBI": "Orbs", "ORBIS": "Orbis", "ORBIT": "Orbit Protocol", @@ -11749,8 +12010,10 @@ "ORET": "ORET Token", "ORFY": "Ordify", "ORGA": "Organicco", + "ORGO": "Orgo", "ORGT": "Organic Token", - "ORI": "Origami", + "ORI": "Orizon", + "ORIGAMI": "Origami", "ORIGIN": "Origin Foundation", "ORIGINA": "Original Gangsters", "ORION": "Orion Money", @@ -11816,6 +12079,7 @@ "OUSG": "OUSG", "OUT": "Netscouters", "OUTL": "Outlanders Token", + "OUTLAW": "OUTLAW Crypto Games", "OVATO": "Ovato", "OVC": "OVCODE", "OVER": "OverProtocol", @@ -11825,6 +12089,7 @@ "OVO": "OVO", "OVPP": "OpenVPP", "OVR": "Ovr", + "OWB": "OWB", "OWC": "Oduwa", "OWD": "Owlstand", "OWL": "OWL Token", @@ -11855,7 +12120,7 @@ "OZONE": "Ozone metaverse", "OZONEC": "Ozonechain", "OZP": "OZAPHYRE", - "P": "PUPS•WORLD•PEACE", + "P": "PoP Planet", "P1": "PEPE ONE", "P202": "Project 202", "P2P": "Sentinel", @@ -11900,6 +12165,7 @@ "PALMP": "PalmPay", "PALMV1": "PaLM AI v1", "PALMY": "Palmy", + "PALU": "Palu", "PAM": "PAM", "PAMBI": "Pambicoin", "PAMP": "PAMP Network", @@ -11912,6 +12178,7 @@ "PANDO": "Pando", "PANDOP": "PandoProject", "PANDORA": "Pandora", + "PANDU": "Pandu Pandas", "PANGEA": "PANGEA", "PANIC": "PanicSwap", "PANO": "PanoVerse", @@ -11944,6 +12211,7 @@ "PARETO": "Pareto Network Token", "PARI": "Paribus", "PARKGENE": "PARKGENE", + "PARKINGO": "ParkinGo", "PARLAY": "Parlay", "PARMA": "PARMA Fan Token", "PARQ": "PARQ", @@ -11980,6 +12248,7 @@ "PAXU": "Pax Unitas", "PAXW": "pax.world", "PAY": "TenX", + "PAYAI": "PayAI Network", "PAYB": "Paybswap", "PAYCENT": "Paycent", "PAYCON": "Paycon", @@ -12178,6 +12447,7 @@ "PEPIT": "Pepito", "PEPLO": "Peplo Escobar", "PEPO": "Peepo", + "PEPOC": "Pepoclown", "PEPPA": "PEPPA", "PEPPER": "Pepper Token", "PEPS": "PEPS Coin", @@ -12259,6 +12529,7 @@ "PHM": "Phomeum", "PHMN": "POSTHUMAN", "PHN": "Phayny", + "PHNIX": "Phoenix", "PHNX": "PhoenixDAO", "PHO": "Photon", "PHOENIX": "Phoenix Finance", @@ -12292,14 +12563,16 @@ "PIDOGE": "Pi Network Doge", "PIE": "Persistent Information Exchange", "PIERRE": "sacré bleu", + "PIEVERSE": "Pieverse Token", "PIF": "Pepe Wif Hat", "PIG": "Pig Finance", "PIGC": "Pigcoin", "PIGE": "Pige", "PIGEON": "Pigeon In Yellow Boots", "PIGEONC": "Pigeoncoin", - "PIGGY": "Piggy", + "PIGGY": "Piggycell", "PIGGYCOIN": "Piggy Coin", + "PIGGYFI": "Piggy", "PIGLET": "PIGLET", "PIGONK": "PIGONK", "PIGS": "Elon Vitalik Pigs", @@ -12320,8 +12593,9 @@ "PINE": "Pine", "PINETWORKDEFI": "Pi Network DeFi", "PINEYE": "PinEye", - "PING": "CryptoPing", + "PING": "Ping", "PINGO": "PinGo", + "PINGPONG": "PINGPONG Token", "PINK": "PINK - The Panther", "PINKCOIN": "PinkCoin", "PINKSALE": "PinkSale", @@ -12334,6 +12608,7 @@ "PIO": "Pioneershares", "PIP": "Pip", "PIPA": "Pipa Coin", + "PIPE": "Pipe", "PIPI": "Pippi Finance", "PIPL": "PiplCoin", "PIPO": "Pipo", @@ -12376,6 +12651,7 @@ "PKF": "PolkaFoundry", "PKG": "PKG Token", "PKIN": "PUMPKIN", + "PKM": "Pockemy", "PKN": "Poken", "PKOIN": "Pocketcoin", "PKT": "PKT", @@ -12383,8 +12659,10 @@ "PLAAS": "PLAAS FARMERS TOKEN", "PLAC": "PLANET", "PLACE": "PlaceWar Governance", - "PLAI": "Plair", + "PLAI": "PLAY AI", + "PLAIR": "Plair", "PLAN": "Plancoin", + "PLANCK": "Planck", "PLANE": "Paper Plane", "PLANET": "PLANET", "PLANETCOIN": "PlanetCoin", @@ -12400,6 +12678,7 @@ "PLAYCOIN": "PlayCoin", "PLAYFUN": "PLAYFUN", "PLAYKEY": "Playkey", + "PLAYSOL": "Play Solana", "PLAYTOKEN": "Play Token", "PLB": "Paladeum", "PLBT": "Polybius", @@ -12429,6 +12708,7 @@ "PLNC": "PLNCoin", "PLNX": "Planumex", "PLOT": "PlotX", + "PLPA": "Palapa", "PLQ": "Planq", "PLR": "Pillar", "PLS": "Pulsechain", @@ -12482,6 +12762,7 @@ "PNDR": "Pandora Finance", "PNFT": "Pawn My NFT", "PNG": "Pangolin", + "PNGDA": "Pengda Yellow Panda", "PNGN": "SpacePenguin", "PNIC": "Phoenic", "PNK": "Kleros", @@ -12499,7 +12780,8 @@ "POC": "POC Blockchain", "POCAT": "Polite Cat", "POCC": "POC Chain", - "POCHITA": "Pochita", + "POCHITA": "pochita", + "POCHITAV1": "Pochita", "POCKET": "XPocket", "POCO": "Pocoland", "POD": "Podo Point", @@ -12508,6 +12790,7 @@ "PODO": "Power Of Deep Ocean", "POE": "Portal Network", "POET": "Po.et", + "POFU": "POFU", "POG": "PolygonumOnline", "POGAI": "POGAI", "POGS": "POG", @@ -12576,11 +12859,12 @@ "POOP": "Poopsicle", "POOPC": "Poopcoin", "POOWEL": "Joram Poowel", - "POP": "Popcoin", + "POP": "Zypher Network", "POPC": "PopChest", "POPCAT": "Popcat", "POPCHAIN": "POPCHAIN", "POPCO": "Popcorn", + "POPCOIN": "Popcoin", "POPDOG": "PopDog", "POPE": "PopPepe", "POPECOIN": "Popecoin", @@ -12603,6 +12887,7 @@ "PORT": "Port Finance", "PORT3": "Port3 Network", "PORTAL": "Portal", + "PORTALS": "Portals", "PORTALTOKEN": "Portal", "PORTO": "FC Porto", "PORTU": "Portuma", @@ -12650,6 +12935,7 @@ "PPR": "Papyrus", "PPS": "PopulStay", "PPT": "Pop Token", + "PPX": "Prophex", "PPY": "Peerplays", "PQT": "Prediqt", "PRA": "ProChain", @@ -12679,6 +12965,7 @@ "PRG": "Paragon", "PRI": "PRIVATEUM INITIATIVE", "PRIA": "PRIA", + "PRICELESS": "Priceless", "PRICK": "Pickle Rick", "PRIDE": "Nomad Exiles", "PRIMAL": "PRIMAL", @@ -12705,7 +12992,8 @@ "PROD": "Productivist", "PROFITHUNTERS": "Profit Hunters Coin", "PROGE": "Protector Roge", - "PROJECT89": "Project89", + "PROJECT89": "Project 89", + "PROJECT89V1": "Project89", "PROJECTPAI": "Project Pai", "PROLIFIC": "Prolific Game Studio", "PROM": "Prometeus", @@ -12819,6 +13107,7 @@ "PUMPAI": "PumpAI", "PUMPB": "Pump", "PUMPBTC": "pumpBTC", + "PUMPBTCXYZ": "PumpBTC", "PUMPFUNBAN": "Pump Fun Ban", "PUMPIT": "BOGDANOFF", "PUMPTRUMP": "PUMP TRUMP", @@ -12842,6 +13131,7 @@ "PUPPETS": "Puppets Coin", "PUPPIES": "I love puppies", "PUPS": "PUPS (Ordinals)", + "PUPSWORLD": "PUPS•WORLD•PEACE", "PUPU": "Pepe's Dog", "PURA": "Pura", "PURE": "Puriever", @@ -12918,6 +13208,7 @@ "Q2C": "QubitCoin", "QA": "Quantum Assets", "QAC": "Quasarcoin", + "QACE": "Qace Dynamics", "QAI": "QuantixAI", "QANX": "QANplatform", "QANXV2": "QANplatform v2", @@ -12968,6 +13259,7 @@ "QOOB": "QOOBER", "QORA": "QoraCoin", "QORPO": "QORPO WORLD", + "QPAY": "QPAY SOL", "QQBC": "QQBC IPFS BLOCKCHAIN", "QQQ": "Poseidon Network", "QQQF": "Standard Crypto Fund", @@ -12994,7 +13286,8 @@ "QTK": "QuantCheck", "QTL": "Quatloo", "QTLX": "Quantlytica", - "QTO": "QToken", + "QTO": "Quanto", + "QTOK": "QToken", "QTUM": "QTUM", "QTZ": "Quartz", "QU3": "QU3ai", @@ -13003,6 +13296,7 @@ "QUAC": "QUACK", "QUACK": "Rich Quack", "QUAI": "Quai Network", + "QUAIN": "QUAIN", "QUAM": "Quam Network", "QUAN": "Quant AI", "QUANT": "Quant Finance", @@ -13022,6 +13316,7 @@ "QUICKOLD": "Quickswap", "QUIDD": "Quidd", "QUIL": "Wrapped QUIL", + "QUILL": "InkFinance", "QUIN": "QUINADS", "QUINT": "Quint", "QUIPU": "QuipuSwap Governance Token", @@ -13074,6 +13369,7 @@ "RAID": "Raid Token", "RAIDER": "Crypto Raiders", "RAIF": "RAI Finance", + "RAIIN": "Raiin", "RAIL": "Railgun", "RAIN": "Rainmaker Games", "RAINBOW": "Rainbow Token", @@ -13153,6 +13449,7 @@ "RCG": "Recharge", "RCGE": "RCGE", "RCH": "Rich", + "RCHV": "Archivas", "RCKT": "RocketSwap", "RCM": "READ2N", "RCN": "Ripio", @@ -13161,6 +13458,7 @@ "RCT": "RealChain", "RCX": "RedCrowCoin", "RD": "Round Dollar", + "RDAC": "Redacted Coin", "RDC": "Ordocoin", "RDD": "Reddcoin", "RDDT": "Reddit", @@ -13196,6 +13494,7 @@ "REALUSDV1": "Real USD v1", "REALUSDV2": "Real USD v2", "REALY": "Realy Metaverse", + "REALYN": "Real", "REAP": "ReapChain", "REAPER": "Grim Finance", "REAU": "Vira-lata Finance", @@ -13204,6 +13503,7 @@ "REBUS": "Rebuschain", "REC": "Rec Token (REC)", "RECA": "The Resistance Cat", + "RECALL": "Recall", "RECKOON": "Reckoon", "RECOM": "Recom", "RECON": "RECON", @@ -13244,6 +13544,7 @@ "REGEN": "Regen Network", "REGENT": "REGENT COIN", "REGI": "Resistance Girl", + "REGRET": "Regret", "REHA": "Resistance Hamster", "REHAB": "NFT Rehab", "REI": "REI Network", @@ -13347,6 +13648,7 @@ "RGP": "Rigel Protocol", "RGT": "Rari Governance Token", "RHEA": "Rhea", + "RHEATOKEN": "Rhea", "RHINO": "RHINO", "RHINOMARS": "RhinoMars", "RHOC": "RChain", @@ -13357,7 +13659,7 @@ "RIBB": "Ribbit", "RIBBIT": "Ribbit", "RIC": "Riecoin", - "RICE": "DAOSquare Governance Token", + "RICE": "RICE AI", "RICECOIN": "RiceCoin", "RICEFARM": "RiceFarm", "RICH": "GET RICH QUICK", @@ -13409,6 +13711,8 @@ "RITE": "ritestream", "RITO": "Ritocoin", "RITZ": "Ritz.Game", + "RIVER": "River", + "RIVERPTS": "River Point Reward Token", "RIVUS": "RivusDAO", "RIYA": "Etheriya", "RIZ": "Rivalz Network", @@ -13621,6 +13925,7 @@ "RUNI": "Runesterminal", "RUNNER": "Runner", "RUNNODE": "Run", + "RUNWAGO": "Runwago", "RUNY": "Runy", "RUP": "Rupee", "RUPX": "Rupaya", @@ -13649,6 +13954,7 @@ "RVR": "Revolution VR", "RVST": "Revest Finance", "RVT": "Rivetz", + "RVV": "REVIVE", "RVX": "Rivex", "RWA": "Allo", "RWAECO": "RWA Ecosystem", @@ -13680,6 +13986,7 @@ "RYU": "The Blue Dragon", "RYZ": "Anryze", "RZR": "RazorCoin", + "RZTO": "RZTO Token", "RZUSD": "RZUSD", "RedFlokiCEO": "Red Floki CEO", "S": "Sonic Labs", @@ -13740,14 +14047,16 @@ "SAKAI": "Sakai Vault", "SAKATA": "Sakata Inu", "SAKE": "SakeToken", - "SAL": "SalPay", + "SAL": "Salvium", "SALD": "Salad", "SALE": "DxSale Network", "SALL": "Sallar", "SALLY": "SALAMANDER", "SALMAN": "Mohameme Bit Salman", "SALMON": "Salmon", + "SALPAY": "SalPay", "SALT": "Salt Lending", + "SALUTE": "Salute", "SAM": "Samsunspor Fan Token", "SAMA": "Moonsama", "SAMMY": "Samoyed", @@ -13763,6 +14072,7 @@ "SANI": "Sanin Inu", "SANIN": "Sanin", "SANJI": "Sanji Inu", + "SANSFOREST": "FOREST", "SANSHU": "Sanshu Inu", "SANTA": "SANTA CHRISTMAS INU", "SANTAGROK": "Santa Grok", @@ -13777,6 +14087,7 @@ "SAPPC": "SappChat", "SAR": "Saren", "SARA": "Pulsara", + "SARAH": "Sarah", "SARCO": "Sarcophagus", "SARM": "Stella Armada", "SAROS": "Saros", @@ -13971,6 +14282,7 @@ "SELLC": "Sell Token", "SEM": "Semux", "SEN": "Sentaro", + "SENA": "Ethena Staked ENA", "SENATE": "SENATE", "SENC": "Sentinel Chain", "SEND": "Suilend", @@ -13987,6 +14299,7 @@ "SENT": "Sentinel", "SENTAI": "SentAI", "SENTI": "Sentinel Bot Ai", + "SENTIS": "Sentism AI Token", "SENTR": "Sentre Protocol", "SEON": "Seedon", "SEOR": "SEOR Network", @@ -14044,6 +14357,7 @@ "SFUND": "Seedify.fund", "SFV2": "ShibaFameV2", "SFX": "SUBX FINANCE LAB", + "SFY": "Stakefy", "SG": "SocialGood", "SGA": "Saga", "SGB": "Songbird", @@ -14078,9 +14392,11 @@ "SHAREV1": "Seigniorage Shares v1", "SHARK": "Sharky", "SHARKI": "Sharki", + "SHARKS": "Sharks", "SHARKYSH": "Sharky Sharkx", "SHARP": "Sharp", "SHARPE": "Sharpe Capital", + "SHARPLINK": "SharpLink Gaming", "SHAUN": "SHAUN INU", "SHB4": "Super Heavy Booster 4", "SHC": "School Hack Coin", @@ -14232,6 +14548,7 @@ "SIFT": "Smart Investment Fund Token", "SIFU": "SIFU", "SIG": "Signal", + "SIGM": "Sigma", "SIGMA": "SIGMA", "SIGN": "Sign", "SIGNA": "Signa", @@ -14258,6 +14575,7 @@ "SILVERWAY": "Silverway", "SIM": "Simpson", "SIMBA": "SIMBA The Sloth", + "SIMMI": "Simmi Token", "SIMON": "Simon the Gator", "SIMP": "SO-COL", "SIMPLE": "SimpleChain", @@ -14340,6 +14658,7 @@ "SKT": "Sukhavati Network", "SKU": "Sakura", "SKULL": "Pirate Blocks", + "SKUY": "Token Sekuya", "SKX": "SKPANAX", "SKY": "Sky", "SKYA": "Sekuya Multiverse", @@ -14377,6 +14696,7 @@ "SLINK": "Soft Link", "SLIPPY": "SLIPPY", "SLISBNB": "Lista Staked BNB", + "SLISBNBX": "slisBNBx", "SLK": "SLK", "SLM": "SlimCoin", "SLN": "Smart Layer Network", @@ -14394,7 +14714,7 @@ "SLRS": "Solrise Finance", "SLS": "SaluS", "SLST": "SmartLands", - "SLT": "Salute", + "SLT": "SLT", "SLUGDENG": "SLUG DENG", "SLUMBO": "SLUMBO", "SLVLUSD": "Staked Level USD", @@ -14558,6 +14878,7 @@ "SOFI": "RAI Finance", "SOFTCO": "SOFT COQ INU", "SOFTT": "Wrapped FTT (Sollet)", + "SOGNI": "Sogni AI", "SOGUR": "Sogur Currency", "SOH": "Stohn Coin", "SOHOT": "SOHOTRN", @@ -14630,6 +14951,7 @@ "SOLSCC": "sols", "SOLSPONGE": "Solsponge", "SOLT": "Soltalk AI", + "SOLTAN": "SOLTAN", "SOLTR": "SolTrump", "SOLV": "Solv Protocol", "SOLVBTC": "Solv Protocol SolvBTC", @@ -14776,8 +15098,10 @@ "SPK": "Spark", "SPKL": "SpokLottery", "SPKTR": "Ghost Coin", + "SPKY": "GhostyCash", "SPL": "SocialPal", "SPLA": "SmartPlay", + "SPLD": "Splendor", "SPM": "Supreme", "SPN": "Sapien Network", "SPND": "Spindle", @@ -14803,6 +15127,7 @@ "SPRITZMOON": "SpritzMoon Crypto Token", "SPRKL": "Sparkle Loyalty", "SPROUT": "Sprout", + "SPRSTR": "SprotoStrategy", "SPRT": "Sportium", "SPRTS": "Sprouts", "SPRTZ": "SpritzCoin", @@ -14828,6 +15153,7 @@ "SQGROW": "SquidGrow", "SQL": "Squall Coin", "SQR": "Magic Square", + "SQRL": "Squirrel Swap", "SQT": "SubQuery Network", "SQTS": "Sqts (Ordinals)", "SQU": "SquadSwap", @@ -14899,6 +15225,7 @@ "STA": "STOA Network", "STAB": "STABLE ASSET", "STABLZ": "Stablz", + "STABUL": "Stabull Finance", "STAC": "STAC", "STACK": "StackOS", "STACKS": " STACKS PAY", @@ -14950,6 +15277,7 @@ "STAX": "Staxcoin", "STAY": "NFsTay", "STB": "stabble", + "STBL": "STBL Governance Token", "STBOT": "SolTradingBot", "STBTC": "Lorenzo stBTC", "STBU": "Stobox Token", @@ -15049,10 +15377,12 @@ "STRD": "Stride", "STRDY": "Sturdy", "STREAM": "Streamflow", + "STREAMER": "StreamerCoin", "STREAMIT": "STREAMIT COIN", "STREETH": "STREETH", "STRI": "Strite", - "STRIKE": "Strike", + "STRIKE": "StrikeBit", + "STRIKETOKEN": "Strike", "STRIP": "Stripto", "STRK": "Starknet", "STRM": "StreamCoin", @@ -15062,6 +15392,7 @@ "STRONGX": "StrongX", "STRP": "Strips Finance", "STRS": "STARS", + "STRSZN": "Stream SZN", "STRUMP": "Super Trump", "STRX": "StrikeX", "STS": "SBank", @@ -15099,6 +15430,7 @@ "SUBA": "Yotsuba", "SUBAWU": "Subawu Token", "SUBF": "Super Best Friends", + "SUBHUB": "SUBHUB", "SUBS": "Substratum Network", "SUCR": "Sucre", "SUD": "Sudo Labs", @@ -15132,6 +15464,7 @@ "SUMO": "Sumokoin", "SUN": "Sun Token", "SUNC": "Sunrise", + "SUNCAT": "Suncat", "SUNDAE": "Sundae the Dog", "SUNDOG": "SUNDOG", "SUNEX": "The Sun Exchange", @@ -15151,8 +15484,9 @@ "SUNTRON": "TRON MASCOT", "SUNV1": "Sun Token v1", "SUNWUKONG": "SunWukong", - "SUP": "Supcoin", + "SUP": "Superp", "SUP8EME": "SUP8EME Token", + "SUPCOIN": "Supcoin", "SUPE": "Supe Infinity", "SUPER": "SuperVerse", "SUPERBID": "SuperBid", @@ -15173,6 +15507,7 @@ "SUR": "Suretly", "SURE": "inSure", "SURF": "Surf.Finance", + "SURGE": "Surge", "SURV": "Survival Game Online", "SURVIVING": "Surviving Soldiers", "SUSD": "sUSD", @@ -15194,6 +15529,7 @@ "SVNN": "Savanna Haus", "SVPN": "Shadow Node", "SVS": "GivingToServices SVS", + "SVSA": "SavannaSurvival", "SVT": "Solvent", "SVTS": "Syncvault", "SVX": "Savix", @@ -15291,6 +15627,7 @@ "SYNCG": "SyncGPT", "SYNCN": "Sync Network", "SYNCO": "Synco", + "SYND": "Syndicate", "SYNDOG": "Synthesizer Dog", "SYNK": "Synk", "SYNLEV": "SynLev", @@ -15305,8 +15642,10 @@ "SYRAX": "Syrax AI", "SYRUP": "Syrup", "SYRUPUSDC": "SyrupUSDC", + "SYRUPUSDT": "Syrup USDT", "SYS": "Syscoin", "SZCB": "Zugacoin", + "SZN": "BNB SZN", "T": "Threshold Network Token", "T1": "Trump Mobile", "T23": "T23", @@ -15315,7 +15654,7 @@ "TAAS": "Token as a Service", "TAB": "MollyCoin", "TABOO": "Taboo Token", - "TAC": "Traceability Chain", + "TAC": "TAC", "TACC": "TACC", "TACHYON": "Tachyon Protocol", "TAD": "Tadpole", @@ -15411,6 +15750,7 @@ "TBEER": "TRON BEER", "TBFT": "Türkiye Basketbol Federasyon Token", "TBILL": "OpenEden T-Bills", + "TBILLV1": "OpenEden T-Bills v1", "TBIS": "TBIS token", "TBL": "Tombola", "TBR": "Tuebor", @@ -15422,6 +15762,7 @@ "TBX": "Tokenbox", "TCANDY": "TripCandy", "TCAP": "Total Crypto Market Cap", + "TCAPY": "TonCapy", "TCASH": "Trump Cash", "TCAT": "The Currency Analytics", "TCC": "The ChampCoin", @@ -15445,6 +15786,7 @@ "TD": "The Big Red", "TDAN": "TDAN", "TDC": "Tidecoin", + "TDCCP": "TDCCP", "TDE": "Trade Ecology Token", "TDEFI": "Token Teknoloji A.S. Token DeFi", "TDFB": "TDFB", @@ -15490,6 +15832,7 @@ "TENDIE": "TendieSwap", "TENET": "TENET", "TENFI": "TEN", + "TENGE": "TENGE TENGE", "TENNET": "Tennet", "TENS": "TensorScan", "TENSHI": "Tenshi", @@ -15569,6 +15912,7 @@ "THEFARM": "FARM", "THEG": "The GameHub", "THEHARAMBE": "Harambe", + "THEINTERNS": "Interns", "THEM": "The Meta DAO", "THEMIS": "Themis", "THEN": "THENA", @@ -15590,6 +15934,7 @@ "THINK": "THINK Token", "THINKWAREAI": "ThinkwareAI", "THISISF": "This is Fine", + "THISISMYIGUANA": "This Is My Iguana", "THL": "Thala", "THN": "Throne", "THNX": "ThankYou", @@ -15639,9 +15984,10 @@ "TIM": "TIMTIM GAMES", "TIME": "Chrono.tech", "TIMEFUN": "timefun", + "TIMELESS": "Timeless", "TIMES": "DARKTIMES", "TIMESW": "Timeswap", - "TIMI": "This Is My Iguana", + "TIMI": "TIMI", "TIMICOIN": "Timicoin", "TIN": "Token IN", "TINC": "Tiny Coin", @@ -15728,6 +16074,7 @@ "TODAY": "TodayCoin", "TODD": "TURBO TODD", "TOG": "Token of Games", + "TOILET": "Toilet Dust", "TOK": "Tokai", "TOKA": "Tonka Finance", "TOKABU": "Tokabu", @@ -15787,7 +16134,8 @@ "TOPIA": "Hytopia", "TOPN": "TOP Network", "TOR": "TOR", - "TORA": "TORA NEKO", + "TORA": "Tensora", + "TORAN": "TORA NEKO", "TORCH": "Hercules Token", "TORE": "Toreus Finance", "TORG": "TORG", @@ -15811,6 +16159,7 @@ "TOTHEMOON": "To The Moon", "TOTM": "Totem", "TOTO": "TOTO", + "TOTT": "TOTT", "TOUCHFAN": "TouchFan", "TOUCHG": "Touch Grass", "TOUR": "Tour Billion", @@ -15844,6 +16193,7 @@ "TRAC": "OriginTrail", "TRACE": "Trace Network Labs", "TRACEA": "Trace AI", + "TRACEABILITY": "Traceability Chain", "TRACKEDBIO": "TrackedBio", "TRACN": "trac (Ordinals)", "TRADE": "Polytrade", @@ -15860,6 +16210,7 @@ "TRANQ": "Tranquil Finance", "TRANS": "Trans Pepe", "TRANSFER": "TransferCoin", + "TRASH": "TrashCoin", "TRAT": "Tratok", "TRAVA": "Trava Finance", "TRAXIA": "Traxia Membership Token", @@ -15870,6 +16221,7 @@ "TRC": "Terrace", "TRCB": "TRCB Chain", "TRCL": "Treecle", + "TRCR": "Tracer", "TRCT": "Tracto", "TRDC": "Traders Coin", "TRDL": "Strudel Finance", @@ -15882,6 +16234,7 @@ "TRECENTO": "Trecento Blockchain Capital", "TREE": "Treehouse", "TREEB": "Retreeb", + "TREEINCAT": "Tree stuck in cat", "TREEOFALPHA": "Tree", "TREMP": "Doland Tremp", "TRENCHER": "Trencher", @@ -15945,6 +16298,7 @@ "TRUCE": "WORLD PEACE PROJECT", "TRUE": "True Chain", "TRUEBIT": "Truebit Protocol", + "TRUEZEUSCOIN": "Zeus", "TRUF": "Truflation", "TRUFV1ERC20": "Truflation v1 ERC-20", "TRUM": "TrumpBucks", @@ -15980,6 +16334,7 @@ "TRUMPHAT": "Trump Hat", "TRUMPI": "TRUMP IP", "TRUMPINU": "Trump Inu", + "TRUMPIUS": "Trumpius Maximus", "TRUMPJ": "TRUMPJR", "TRUMPJR": "OFFICIAL TRUMP JR", "TRUMPJRVIP": "TrumpJr", @@ -15999,11 +16354,13 @@ "TRUMPX": "Trump X-Maga", "TRUMPZ": "Trump Zhong", "TRUNK": "Elephant Money", - "TRUST": "TrustDAO", + "TRUST": "Intuition", "TRUSTNFT": "TrustNFT", + "TRUSTPLUS": "TrustDAO", "TRUT": "Truth", - "TRUTH": "TruthGPT", + "TRUTH": "Swarm Network", "TRUTHFI": "Truthfi", + "TRUTHGPT": "TruthGPT", "TRV": "TrustVerse", "TRVC": "Trivechain", "TRVL": "TRVL", @@ -16037,6 +16394,7 @@ "TSR": "Tesra", "TST": "Test", "TSTAI": "Test AI", + "TSTON": "Tonstakers TON", "TSTS": "Test", "TSUBASAUT": "TSUBASA Utility Token", "TSUGT": "Captain Tsubasa", @@ -16078,6 +16436,7 @@ "TURBOS": "Turbos Finance", "TURBOW": "Turbo Wallet", "TURT": "TurtSat", + "TURTLE": "Turtle", "TUS": "Treasure Under Sea", "TUSD": "True USD", "TUSDV1": "True USD v1", @@ -16126,7 +16485,7 @@ "TYBG": "Base God", "TYBGSc": "Base Goddess", "TYC": "Tycoon", - "TYCOON": "CryptoTycoon", + "TYCOON": "Tycoon Token", "TYKE": "Tyke The Elephant", "TYLER": "Tyler", "TYOGHOUL": "TYO GHOUL", @@ -16142,14 +16501,15 @@ "TZKI": "Tsuzuki Inu", "TZPEPE": "Tezos Pepe", "TZU": "Sun Tzu", - "U": "Unidef", + "U": "Union", "U2U": "U2U Network", "U8D": "Universal Dollar", "UA1": "UA1", "UAEC": "United Arab Emirates Coin", "UAHG": "UAHg", + "UAI": "UnifAI", "UAT": "UltrAlpha", - "UB": "UBit Token", + "UB": "Unibase", "UBA": "Unbox.Art", "UBC": "Universal Basic Compute", "UBCOIN": "Ubcoin", @@ -16158,6 +16518,7 @@ "UBI": "Universal Basic Income", "UBIQ": "Ubiqoin", "UBIT": "UBIT", + "UBITTOKEN": "UBit Token", "UBQ": "Ubiq", "UBT": "UniBright", "UBTC": "UnitedBitcoin", @@ -16169,11 +16530,12 @@ "UCA": "UCA Coin", "UCAP": "Unicap.finance", "UCASH": "U.CASH", + "UCCOIN": "UC Coin", "UCG": "Universe Crystal Gene", "UCH": "UChain", "UCJL": "Utility Cjournal", "UCM": "UCROWDME", - "UCN": "UC Coin", + "UCN": "UCHAIN", "UCO": "Uniris", "UCOIN": "Ucoin", "UCON": "YouCoin Metaverse", @@ -16272,7 +16634,9 @@ "UNIBTC": "uniBTC", "UNIC": "Unicly", "UNICE": "UNICE", + "UNICEF": "united normies in crypto extending funds", "UNICORN": "UNICORN Token", + "UNIDEF": "Unidef", "UNIDEXAI": "UniDexAI", "UNIDX": "UniDex", "UNIDXV1": "UniDex v1", @@ -16297,6 +16661,8 @@ "UNITARYSTATUS": "UnitaryStatus Dollar", "UNITE": "Unite", "UNITED": "UnitedCoins", + "UNITEDTRADERS": "United Traders Token", + "UNITPROV2": "Unit Protocol New", "UNITRADE": "UniTrade", "UNITREEAI": "Unitree G1 AI", "UNITREEDOG": "Unitree AI Robot Dog", @@ -16361,12 +16727,14 @@ "USC": "Ultimate Secure Cash", "USCC": "USC", "USCOIN": "USCoin", + "USCR": "United States Crypto Reserve", "USD0": "Usual", "USD1": "World Liberty Financial USD", "USD3": "Web 3 Dollar", - "USDA": "USDa", "USDACC": "USDA", + "USDAI": "USDai", "USDAP": "Bond Appetite USD", + "USDAVALON": "USDa", "USDB": "USD Bancor", "USDBC": "Bridged USDC", "USDBLAST": "USDB Blast", @@ -16402,7 +16770,6 @@ "USDMA": "USD mars", "USDN": "Neutral AI", "USDO": "USD Open Dollar", - "USDONE": "Currency One USD", "USDP": "Pax Dollar", "USDPLUS": "Overnight.fi USD+", "USDQ": "Quantoz USDQ", @@ -16413,6 +16780,7 @@ "USDSTABLY": "StableUSD", "USDT": "Tether", "USDT0": "USDT0", + "USDT1": "USDT1", "USDTB": "USDtb", "USDTBASE": "USDT (Base)", "USDTV": "TetherTV", @@ -16421,6 +16789,7 @@ "USDUC": "Unstable Coin", "USDV": "Verified USD", "USDW": "USD DWIN", + "USDWON": "Won Chang", "USDX": "USDX Stablecoin", "USDXL": "Last USD", "USDY": "Ondo US Dollar Yield", @@ -16471,7 +16840,8 @@ "UTMDOGE": "UltramanDoge", "UTNP": "Universa", "UTON": "uTON", - "UTT": "United Traders Token", + "UTOPIA": "UCOIN", + "UTT": "uTrade", "UTU": "UTU Protocol", "UTX": "UTIX", "UTYA": "Utya", @@ -16485,6 +16855,7 @@ "UWUCOIN": "uwu", "UX": "Umee", "UXLINK": "UXLINK", + "UXLINKV1": "UXLINK v1", "UXOS": "UXOS", "UXP": "UXD Protocol", "UZUMAKI": "Uzumaki Inu", @@ -16508,6 +16879,7 @@ "VALORBIT": "Valorbit", "VALU": "Value", "VALUE": "Value Liquidity", + "VALYR": "Valyr", "VAMPIRE": "Vampire Inu", "VAN": "Vanspor Token", "VANA": "Vana", @@ -16605,6 +16977,7 @@ "VERA": "Vera", "VERI": "Veritaseum", "VERIC": "VeriCoin", + "VERIFY": "Verify", "VERO": "VEROPAD", "VERSA": "Versa Token", "VERSACE": "VERSACE", @@ -16628,9 +17001,13 @@ "VFIL": "Venus Filecoin", "VFOX": "VFOX", "VFT": "Value Finance", + "VFY": "zkVerify", + "VFYV1": "Verify Token", "VG": "Viu Ganhou", + "VGBP": "VNX British Pound", "VGO": "Vagabond", "VGX": "Voyager Token", + "VGXV1": "Voyager v1", "VHC": "Vault Hill City", "VI": "Vid", "VIA": "Octavia AI", @@ -16693,6 +17070,7 @@ "VITALIK": "OFFICIAL VITALIK", "VITAMINS": "Vitamins", "VITARNA": "VitaRNA", + "VITASTEM": "VitaStem", "VITE": "VITE", "VITEX": "ViteX Coin", "VITRA": "Vitra Studios", @@ -16737,6 +17115,7 @@ "VNO": "Veno Finance", "VNST": "VNST Stablecoin", "VNT": "VNT Chain", + "VNTR": "Venture Mind AI", "VNTW": "Value Network Token", "VNX": "VisionX", "VNXAU": "VNX Gold", @@ -16776,6 +17155,7 @@ "VOYACOIN": "Voyacoin", "VP": "Torah Network", "VPAD": "VLaunch", + "VPAY": "VPay by Virtuals", "VPK": "Vulture Peak", "VPND": "VaporNodes", "VPP": "Virtue Poker Points", @@ -16786,7 +17166,9 @@ "VR": "Victoria", "VR1": "VR1", "VRA": "Verasity", + "VRAV1": "Verasity v1", "VRC": "Virtual Coin", + "VRFY": "VERIFY", "VRGW": "Virtual Reality Game World", "VRH": "Versailles Heroes", "VRL": "Virtual X", @@ -16839,9 +17221,11 @@ "VTUSD": "Venus TUSD", "VTX": "Vortex DeFi", "VTY": "Victoriouscoin", + "VU": "Vu", "VUC": "Virta Unique Coin", "VULC": "Vulcano", "VULPEFI": "Vulpe Finance", + "VULT": "Vultisig Token", "VUNI": "Venus UNI", "VUSD": "Virtual USD", "VUZZ": "Vuzz AI", @@ -16974,6 +17358,7 @@ "WBT": "WhiteBIT Token", "WBTC": "Wrapped Bitcoin", "WBTCWXG": "WBTC-WXG", + "WBULL": "BNB Wallstreet Bull", "WBX": "WiBX", "WCA": "WCAPES", "WCANTO": "Wrapped CANTO", @@ -17034,6 +17419,7 @@ "WEL": "Welsh Corgi", "WELA": "Wrapped Elastos", "WELD": "Weld", + "WELF": "welf", "WELL": "Moonwell", "WELL3": "WELL3", "WELLTOKEN": "Well", @@ -17069,6 +17455,7 @@ "WFAI": "WaifuAI", "WFBTC": "Wrapped Fantom Bitcoin", "WFDP": "WFDP", + "WFI": "WeFi", "WFIL": "Wrapped Filecoin", "WFLAMA": "WIFLAMA", "WFLOW": "Wrapped Flow", @@ -17208,6 +17595,7 @@ "WLUNA": "Wrapped LUNA Token", "WLUNC": "Wrapped LUNA Classic", "WLXT": "Wallex Token", + "WM": "WrappedM by M^0", "WMATIC": "Wrapped Matic", "WMB": "WatermelonBlock", "WMC": "Wrapped MistCoin", @@ -17249,6 +17637,7 @@ "WOFM": "World of Masters", "WOID": "WORLD ID", "WOJ": "Wojak Finance", + "WOJA": "Wojak", "WOJAK": "Wojak", "WOJAK2": "Wojak 2.0 Coin", "WOJAKC": "Wojak Coin", @@ -17343,6 +17732,7 @@ "WSI": "WeSendit", "WSIENNA": "Sienna ERC20", "WSM": "Wall Street Memes", + "WSOL": "Wrapped Solana", "WSPP": "Wolf Safe Poor People", "WSTA": "Wrapped Statera", "WSTETH": "Lido wstETH", @@ -17378,6 +17768,7 @@ "WUK": "WUKONG", "WUKONG": "Sun Wukong", "WULFY": "Wulfy", + "WUM": "Unicorn Meat", "WUSD": "Worldwide USD", "WUST": "Wrapped UST Token", "WVG0": "Wrapped Virgin Gen-0 CryptoKittties", @@ -17397,6 +17788,7 @@ "WXDAI": "Wrapped XDAI", "WXDC": "Wrapped XDC", "WXM": "WeatherXM", + "WXPL": "Wrapped XPL", "WXRP": "Wrapped XRP", "WXT": "WXT", "WXTZ": "Wrapped Tezos", @@ -17434,6 +17826,7 @@ "XALGO": "Wrapped ALGO", "XALPHA": "XAlpha AI", "XAMP": "Antiample", + "XAN": "Anoma", "XAND": "Xandeum", "XANK": "Xank", "XAP": "Apollon", @@ -17453,7 +17846,7 @@ "XBB": "BrickBlock", "XBC": "BitcoinPlus", "XBE": "XBE Token", - "XBG": "BitGrin", + "XBG": "XBorg Token", "XBI": "Bitcoin Incognito", "XBL": "Billionaire Token", "XBLAZE": "Trailblaze", @@ -17503,6 +17896,7 @@ "XCT": "C-Bits", "XCUR": "Curate", "XCV": "XCarnival", + "XCX": "Xeleb AI", "XCXT": "CoinonatX", "XD": "Data Transaction Token", "XDAG": "Dagger", @@ -17518,6 +17912,7 @@ "XDG": "Decentral Games Governance", "XDN": "DigitalNote", "XDNA": "XDNA", + "XDOG": "XDOG", "XDOGE": "Xdoge", "XDOT": "DotBased", "XDP": "DogeParty", @@ -17607,7 +18002,7 @@ "XMCC": "Monoeci", "XMETA": "TTX METAVERSE", "XMG": "Coin Magi", - "XMN": "Motion", + "XMN": "xMoney", "XMO": "Monero Original", "XMON": "XMON", "XMOON": "r/CryptoCurrency Moons v1", @@ -17621,6 +18016,7 @@ "XMX": "XMax", "XMY": "MyriadCoin", "XNA": "Neurai", + "XNAP": "SNAPX", "XNB": "Xeonbit", "XNC": "Xenios", "XNET": "XNET Mobile", @@ -17662,6 +18058,7 @@ "XPH": "PharmaCoin", "XPHX": "PhoenixCo Token", "XPI": "XPi", + "XPIN": "XPIN Token", "XPL": "Plasma", "XPLA": "XPLA", "XPLL": "ParallelChain", @@ -17765,6 +18162,7 @@ "XTV": "XTV", "XTX": "Xtock", "XTZ": "Tezos", + "XU3O8": "Uranium", "XUC": "Exchange Union", "XUI": "YouSUI", "XUN": "UltraNote", @@ -17776,6 +18174,7 @@ "XVC": "Vcash", "XVE": "The Vegan Initiative", "XVG": "Verge", + "XVM": "Volt", "XVP": "VirtacoinPlus", "XVR": "Xover", "XVS": "Venus", @@ -17820,8 +18219,10 @@ "YAXIS": "yAxis", "YAY": "YAY Games", "YAYCOIN": "YAYcoin", + "YB": "Yield Basis", "YBC": "YbCoin", "YBDBD": "YBDBD", + "YBNB": "Yellow BNB 4", "YBO": "Young Boys Fan Token", "YBR": "YieldBricks", "YCC": "Yuan Chain Coin", @@ -17892,6 +18293,7 @@ "YMS": "Yeni Malatyaspor Token", "YNE": "yesnoerror", "YNETH": "YieldNest Restaked ETH", + "YNG": "Young", "YO": "Yobit Token", "YOBASE": "All Your Base", "YOC": "YoCoin", @@ -17931,7 +18333,7 @@ "YTJIA": "Jia Yueting", "YTN": "YENTEN", "YTS": "YetiSwap", - "YU": "BOUNTYKINDS", + "YU": "Yala stablecoin", "YUANG": "Yuang Coin", "YUCHEN": "Sun Yuchen", "YUCJ": "Yu Coin", @@ -17991,6 +18393,7 @@ "ZBC": "Zebec Protocol", "ZBCN": "Zebec Network", "ZBIT": "zbit", + "ZBT": "ZEROBASE", "ZBU": "Zeebu", "ZBUV1": "ZEEBU v1", "ZCC": "ZCC Coin", @@ -18037,6 +18440,7 @@ "ZENI": "Zennies", "ZENIQ": "Zeniq Coin", "ZENITH": "Zenith Chain", + "ZENIX": "ZENIX", "ZENPROTOCOL": "Zen Protocol", "ZENQ": "Zenqira", "ZENT": "Zentry", @@ -18044,6 +18448,7 @@ "ZEP": "Zeppelin Dao", "ZEPH": "Zephyr Protocol", "ZER": "Zero", + "ZERA": "ZERA", "ZERC": "zkRace Coin", "ZEREBRO": "Zerebro", "ZERO": "ZeroLend", @@ -18201,6 +18606,7 @@ "ZUM": "ZumCoin", "ZUN": "Zunami Governance Token", "ZUNA": "ZUNA", + "ZUNO": "OFFICIAL ZUNO", "ZUNUSD": "Zunami USD", "ZUR": "Zurcoin", "ZURR": "ZURRENCY", @@ -18235,5 +18641,7 @@ "redBUX": "redBUX", "sOHM": "Staked Olympus", "vXDEFI": "vXDEFI", - "wsOHM": "Wrapped Staked Olympus" + "wsOHM": "Wrapped Staked Olympus", + "修仙": "修仙", + "币安人生": "币安人生" } diff --git a/apps/api/src/dependencies.ts b/apps/api/src/dependencies.ts new file mode 100644 index 000000000..acb7af382 --- /dev/null +++ b/apps/api/src/dependencies.ts @@ -0,0 +1,3 @@ +// Dependencies required by .config/prisma.ts in Docker container +import 'dotenv'; +import 'dotenv-expand'; diff --git a/apps/api/src/events/asset-profile-changed.event.ts b/apps/api/src/events/asset-profile-changed.event.ts new file mode 100644 index 000000000..46a8c5db4 --- /dev/null +++ b/apps/api/src/events/asset-profile-changed.event.ts @@ -0,0 +1,11 @@ +import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; + +export class AssetProfileChangedEvent { + public constructor( + public readonly data: AssetProfileIdentifier & { currency: string } + ) {} + + public static getName(): string { + return 'assetProfile.changed'; + } +} diff --git a/apps/api/src/events/asset-profile-changed.listener.ts b/apps/api/src/events/asset-profile-changed.listener.ts new file mode 100644 index 000000000..ad80ee4a5 --- /dev/null +++ b/apps/api/src/events/asset-profile-changed.listener.ts @@ -0,0 +1,61 @@ +import { OrderService } from '@ghostfolio/api/app/order/order.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 { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; +import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; + +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { AssetProfileChangedEvent } from './asset-profile-changed.event'; + +@Injectable() +export class AssetProfileChangedListener { + public constructor( + private readonly configurationService: ConfigurationService, + private readonly dataGatheringService: DataGatheringService, + private readonly dataProviderService: DataProviderService, + private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly orderService: OrderService + ) {} + + @OnEvent(AssetProfileChangedEvent.getName()) + public async handleAssetProfileChanged(event: AssetProfileChangedEvent) { + Logger.log( + `Asset profile of ${event.data.symbol} (${event.data.dataSource}) has changed`, + 'AssetProfileChangedListener' + ); + + if ( + this.configurationService.get( + 'ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES' + ) === false || + event.data.currency === DEFAULT_CURRENCY + ) { + return; + } + + const existingCurrencies = this.exchangeRateDataService.getCurrencies(); + + if (!existingCurrencies.includes(event.data.currency)) { + Logger.log( + `New currency ${event.data.currency} has been detected`, + 'AssetProfileChangedListener' + ); + + await this.exchangeRateDataService.initialize(); + } + + const { dateOfFirstActivity } = + await this.orderService.getStatisticsByCurrency(event.data.currency); + + if (dateOfFirstActivity) { + await this.dataGatheringService.gatherSymbol({ + dataSource: this.dataProviderService.getDataSourceForExchangeRates(), + date: dateOfFirstActivity, + symbol: `${DEFAULT_CURRENCY}${event.data.currency}` + }); + } + } +} diff --git a/apps/api/src/events/events.module.ts b/apps/api/src/events/events.module.ts index 0e6b25ba4..ece67ebe0 100644 --- a/apps/api/src/events/events.module.ts +++ b/apps/api/src/events/events.module.ts @@ -1,11 +1,24 @@ +import { OrderModule } from '@ghostfolio/api/app/order/order.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.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 { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { Module } from '@nestjs/common'; +import { AssetProfileChangedListener } from './asset-profile-changed.listener'; import { PortfolioChangedListener } from './portfolio-changed.listener'; @Module({ - imports: [RedisCacheModule], - providers: [PortfolioChangedListener] + imports: [ + ConfigurationModule, + DataGatheringModule, + DataProviderModule, + ExchangeRateDataModule, + OrderModule, + RedisCacheModule + ], + providers: [AssetProfileChangedListener, PortfolioChangedListener] }) export class EventsModule {} diff --git a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts index 1600bd137..3931f362c 100644 --- a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts @@ -27,9 +27,23 @@ export class TransformDataSourceInRequestInterceptor if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { if (request.body?.activities) { + const dataSourceGhostfolioDataProvider = this.configurationService.get( + 'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER' + )?.[0]; + request.body.activities = request.body.activities.map((activity) => { if (DataSource[activity.dataSource]) { - return activity; + if ( + activity.dataSource === 'GHOSTFOLIO' && + dataSourceGhostfolioDataProvider + ) { + return { + ...activity, + dataSource: dataSourceGhostfolioDataProvider + }; + } else { + return activity; + } } else { return { ...activity, 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 fcbf3e76e..fea5d6fe6 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 @@ -16,36 +16,56 @@ import { map } from 'rxjs/operators'; export class TransformDataSourceInResponseInterceptor implements NestInterceptor { + private encodedDataSourceMap: { + [dataSource: string]: string; + } = {}; + public constructor( private readonly configurationService: ConfigurationService - ) {} + ) { + if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { + this.encodedDataSourceMap = Object.keys(DataSource).reduce( + (encodedDataSourceMap, dataSource) => { + if (!['GHOSTFOLIO', 'MANUAL'].includes(dataSource)) { + encodedDataSourceMap[dataSource] = encodeDataSource( + DataSource[dataSource] + ); + } + + return encodedDataSourceMap; + }, + {} + ); + } + } public intercept( - _context: ExecutionContext, + context: ExecutionContext, next: CallHandler ): Observable { + const isExportMode = context.getClass().name === 'ExportController'; + return next.handle().pipe( map((data: any) => { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { + const valueMap = this.encodedDataSourceMap; + + if (isExportMode) { + for (const dataSource of this.configurationService.get( + 'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER' + )) { + valueMap[dataSource] = 'GHOSTFOLIO'; + } + } + data = redactAttributes({ + object: data, options: [ { - attribute: 'dataSource', - valueMap: Object.keys(DataSource).reduce( - (valueMap, dataSource) => { - if (!['MANUAL'].includes(dataSource)) { - valueMap[dataSource] = encodeDataSource( - DataSource[dataSource] - ); - } - - return valueMap; - }, - {} - ) + valueMap, + attribute: 'dataSource' } - ], - object: data + ] }); } diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index 892b1ab5e..c958718f6 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -79,6 +79,10 @@ const locales = { '/en/blog/2025/09/hacktoberfest-2025': { featureGraphicPath: 'assets/images/blog/hacktoberfest-2025.png', title: `Hacktoberfest 2025 - ${title}` + }, + '/en/blog/2025/11/black-weeks-2025': { + featureGraphicPath: 'assets/images/blog/black-weeks-2025.jpg', + title: `Black Weeks 2025 - ${title}` } }; diff --git a/apps/api/src/models/interfaces/rule.interface.ts b/apps/api/src/models/interfaces/rule.interface.ts index 5dcd42317..7c794614e 100644 --- a/apps/api/src/models/interfaces/rule.interface.ts +++ b/apps/api/src/models/interfaces/rule.interface.ts @@ -1,5 +1,4 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; import { EvaluationResult } from './evaluation-result.interface'; diff --git a/apps/api/src/models/rule.ts b/apps/api/src/models/rule.ts index 52491a0b7..9c27e0018 100644 --- a/apps/api/src/models/rule.ts +++ b/apps/api/src/models/rule.ts @@ -1,10 +1,10 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { groupBy } from '@ghostfolio/common/helper'; import { PortfolioPosition, PortfolioReportRule, + RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; diff --git a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts index 0601eea9a..0004d394e 100644 --- a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts +++ b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts @@ -1,8 +1,11 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { PortfolioDetails, UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioDetails, + RuleSettings, + UserSettings +} from '@ghostfolio/common/interfaces'; import { Account } from '@prisma/client'; @@ -121,9 +124,14 @@ export class AccountClusterRiskCurrentInvestment extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.5 }; diff --git a/apps/api/src/models/rules/account-cluster-risk/single-account.ts b/apps/api/src/models/rules/account-cluster-risk/single-account.ts index 8890bb767..9988ea3cc 100644 --- a/apps/api/src/models/rules/account-cluster-risk/single-account.ts +++ b/apps/api/src/models/rules/account-cluster-risk/single-account.ts @@ -1,8 +1,11 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { PortfolioDetails, UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioDetails, + RuleSettings, + UserSettings +} from '@ghostfolio/common/interfaces'; export class AccountClusterRiskSingleAccount extends Rule { private accounts: PortfolioDetails['accounts']; @@ -72,8 +75,9 @@ export class AccountClusterRiskSingleAccount extends Rule { }); } - public getSettings({ xRayRules }: UserSettings): RuleSettings { + public getSettings({ locale, xRayRules }: UserSettings): RuleSettings { return { + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true }; } diff --git a/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts b/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts index dab55413e..f70756e91 100644 --- a/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts +++ b/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts @@ -1,8 +1,11 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { PortfolioPosition, UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioPosition, + RuleSettings, + UserSettings +} from '@ghostfolio/common/interfaces'; export class AssetClassClusterRiskEquity extends Rule { private holdings: PortfolioPosition[]; @@ -109,9 +112,14 @@ export class AssetClassClusterRiskEquity extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.82, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.78 diff --git a/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts b/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts index f793ec16f..3bd835e4d 100644 --- a/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts +++ b/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts @@ -1,8 +1,11 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { PortfolioPosition, UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioPosition, + RuleSettings, + UserSettings +} from '@ghostfolio/common/interfaces'; export class AssetClassClusterRiskFixedIncome extends Rule { private holdings: PortfolioPosition[]; @@ -109,9 +112,14 @@ export class AssetClassClusterRiskFixedIncome extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.22, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.18 diff --git a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts index 2c2b6c4d6..d3176582f 100644 --- a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts +++ b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts @@ -1,8 +1,11 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { PortfolioPosition, UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioPosition, + RuleSettings, + UserSettings +} from '@ghostfolio/common/interfaces'; export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { private holdings: PortfolioPosition[]; @@ -97,9 +100,14 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { private holdings: PortfolioPosition[]; @@ -98,9 +101,14 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.5 }; diff --git a/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts b/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts index 7ca7a2d76..df9b78eef 100644 --- a/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts +++ b/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts @@ -1,8 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; export class EconomicMarketClusterRiskDevelopedMarkets extends Rule { private currentValueInBaseCurrency: number; @@ -104,9 +103,14 @@ export class EconomicMarketClusterRiskDevelopedMarkets extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.72, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.68 diff --git a/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts b/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts index cbf9f98b7..4583dc50a 100644 --- a/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts +++ b/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts @@ -1,8 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; export class EconomicMarketClusterRiskEmergingMarkets extends Rule { private currentValueInBaseCurrency: number; @@ -104,9 +103,14 @@ export class EconomicMarketClusterRiskEmergingMarkets extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.32, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.28 diff --git a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts index d97805fa6..b956263f8 100644 --- a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts +++ b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts @@ -1,8 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; export class EmergencyFundSetup extends Rule { private emergencyFund: number; @@ -59,9 +58,14 @@ export class EmergencyFundSetup extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true }; } diff --git a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts index 93c9aafd3..cb85a73ba 100644 --- a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts +++ b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts @@ -1,8 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; export class FeeRatioInitialInvestment extends Rule { private fees: number; @@ -82,9 +81,14 @@ export class FeeRatioInitialInvestment extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.01 }; diff --git a/apps/api/src/models/rules/liquidity/buying-power.ts b/apps/api/src/models/rules/liquidity/buying-power.ts index 539d0a728..541750d7e 100644 --- a/apps/api/src/models/rules/liquidity/buying-power.ts +++ b/apps/api/src/models/rules/liquidity/buying-power.ts @@ -1,8 +1,7 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; export class BuyingPower extends Rule { private buyingPower: number; @@ -40,7 +39,9 @@ export class BuyingPower extends Rule { languageCode: this.getLanguageCode(), placeholders: { baseCurrency: ruleSettings.baseCurrency, - thresholdMin: ruleSettings.thresholdMin + thresholdMin: ruleSettings.thresholdMin.toLocaleString( + ruleSettings.locale + ) } }), value: false @@ -53,7 +54,9 @@ export class BuyingPower extends Rule { languageCode: this.getLanguageCode(), placeholders: { baseCurrency: ruleSettings.baseCurrency, - thresholdMin: ruleSettings.thresholdMin + thresholdMin: ruleSettings.thresholdMin.toLocaleString( + ruleSettings.locale + ) } }), value: true @@ -86,9 +89,14 @@ export class BuyingPower extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0 }; 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 index 5d6cc999a..1242df759 100644 --- 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 @@ -94,9 +94,14 @@ export class RegionalMarketClusterRiskAsiaPacific extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, 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/emerging-markets.ts b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts index fa13a9e57..8486d843b 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts @@ -96,9 +96,14 @@ export class RegionalMarketClusterRiskEmergingMarkets extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.12, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.08 diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts b/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts index 3bbe7af84..459848db4 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts @@ -94,9 +94,14 @@ export class RegionalMarketClusterRiskEurope extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.15, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.11 diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/interfaces/rule-settings.interface.ts b/apps/api/src/models/rules/regional-market-cluster-risk/interfaces/rule-settings.interface.ts index 8b9fddf3a..621b4df0b 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/interfaces/rule-settings.interface.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/interfaces/rule-settings.interface.ts @@ -1,4 +1,4 @@ -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export interface Settings extends RuleSettings { baseCurrency: string; 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 index 952e14795..d9c1cff6b 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts @@ -94,9 +94,14 @@ export class RegionalMarketClusterRiskJapan extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, 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/models/rules/regional-market-cluster-risk/north-america.ts b/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts index f022ecff9..6180a2cc5 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts @@ -94,9 +94,14 @@ export class RegionalMarketClusterRiskNorthAmerica extends Rule { }); } - public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + public getSettings({ + baseCurrency, + locale, + xRayRules + }: UserSettings): Settings { return { baseCurrency, + locale, isActive: xRayRules?.[this.getKey()]?.isActive ?? true, thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.69, thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.65 diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 473d909ee..f37189569 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -40,9 +40,11 @@ export class ConfigurationService { DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER: json({ default: [] }), + ENABLE_FEATURE_AUTH_GOOGLE: bool({ default: false }), + ENABLE_FEATURE_AUTH_TOKEN: bool({ default: true }), ENABLE_FEATURE_FEAR_AND_GREED_INDEX: bool({ default: false }), + ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES: bool({ default: true }), ENABLE_FEATURE_READ_ONLY_MODE: bool({ default: false }), - ENABLE_FEATURE_SOCIAL_LOGIN: bool({ default: false }), ENABLE_FEATURE_STATISTICS: bool({ default: false }), ENABLE_FEATURE_SUBSCRIPTION: bool({ default: false }), ENABLE_FEATURE_SYSTEM_MESSAGE: bool({ default: false }), diff --git a/apps/api/src/services/cron/cron.service.ts b/apps/api/src/services/cron/cron.service.ts index 88fcabce2..ee91a811e 100644 --- a/apps/api/src/services/cron/cron.service.ts +++ b/apps/api/src/services/cron/cron.service.ts @@ -59,7 +59,9 @@ export class CronService { public async runEverySundayAtTwelvePm() { if (await this.isDataGatheringEnabled()) { const assetProfileIdentifiers = - await this.dataGatheringService.getAllActiveAssetProfileIdentifiers(); + await this.dataGatheringService.getActiveAssetProfileIdentifiers({ + maxAge: '60 days' + }); await this.dataGatheringService.addJobsToQueue( assetProfileIdentifiers.map(({ dataSource, symbol }) => { 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 1e8f7eefa..3cf935b1e 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 @@ -7,14 +7,12 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -23,7 +21,7 @@ import { DataSource, SymbolProfile } from '@prisma/client'; import * as Alphavantage from 'alphavantage'; import { format, isAfter, isBefore, parse } from 'date-fns'; -import { IAlphaVantageHistoricalResponse } from './interfaces/interfaces'; +import { AlphaVantageHistoricalResponse } from './interfaces/interfaces'; @Injectable() export class AlphaVantageService implements DataProviderInterface { @@ -68,11 +66,11 @@ export class AlphaVantageService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { const historicalData: { - [symbol: string]: IAlphaVantageHistoricalResponse[]; + [symbol: string]: AlphaVantageHistoricalResponse[]; } = await this.alphaVantage.crypto.daily( symbol .substring(0, symbol.length - DEFAULT_CURRENCY.length) @@ -81,7 +79,7 @@ export class AlphaVantageService implements DataProviderInterface { ); const response: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; response[symbol] = {}; @@ -115,7 +113,7 @@ export class AlphaVantageService implements DataProviderInterface { } public async getQuotes({}: GetQuotesParams): Promise<{ - [symbol: string]: IDataProviderResponse; + [symbol: string]: DataProviderResponse; }> { return {}; } diff --git a/apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts b/apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts index d954f3a75..897351df1 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/interfaces/interfaces.ts @@ -1 +1 @@ -export interface IAlphaVantageHistoricalResponse {} +export interface AlphaVantageHistoricalResponse {} 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 561d7d6db..d0d96acac 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -7,14 +7,12 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupItem, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -109,15 +107,17 @@ export class CoinGeckoService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { + const queryParams = new URLSearchParams({ + from: getUnixTime(from).toString(), + to: getUnixTime(to).toString(), + vs_currency: DEFAULT_CURRENCY.toLowerCase() + }); + const { error, prices, status } = await fetch( - `${ - this.apiUrl - }/coins/${symbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime( - from - )}&to=${getUnixTime(to)}`, + `${this.apiUrl}/coins/${symbol}/market_chart/range?${queryParams.toString()}`, { headers: this.headers, signal: AbortSignal.timeout(requestTimeout) @@ -133,7 +133,7 @@ export class CoinGeckoService implements DataProviderInterface { } const result: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = { [symbol]: {} }; @@ -166,18 +166,21 @@ export class CoinGeckoService implements DataProviderInterface { public async getQuotes({ requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; } try { + const queryParams = new URLSearchParams({ + ids: symbols.join(','), + vs_currencies: DEFAULT_CURRENCY.toLowerCase() + }); + const quotes = await fetch( - `${this.apiUrl}/simple/price?ids=${symbols.join( - ',' - )}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}`, + `${this.apiUrl}/simple/price?${queryParams.toString()}`, { headers: this.headers, signal: AbortSignal.timeout(requestTimeout) @@ -221,10 +224,17 @@ export class CoinGeckoService implements DataProviderInterface { let items: LookupItem[] = []; try { - const { coins } = await fetch(`${this.apiUrl}/search?query=${query}`, { - headers: this.headers, - signal: AbortSignal.timeout(requestTimeout) - }).then((res) => res.json()); + const queryParams = new URLSearchParams({ + query + }); + + const { coins } = await fetch( + `${this.apiUrl}/search?${queryParams.toString()}`, + { + headers: this.headers, + signal: AbortSignal.timeout(requestTimeout) + } + ).then((res) => res.json()); items = coins.map(({ id: symbol, name }) => { return { 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 8b885c013..1e297b93b 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 @@ -4,7 +4,7 @@ import { Holding } from '@ghostfolio/common/interfaces'; import { Country } from '@ghostfolio/common/interfaces/country.interface'; import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; import { countries } from 'countries-list'; @@ -202,7 +202,12 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return undefined; }) - .catch(() => { + .catch(({ message }) => { + Logger.error( + `Failed to search Trackinsight symbol for ${symbol} (${message})`, + 'TrackinsightDataEnhancerService' + ); + return undefined; }); } diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 6d6054287..5a088c0e4 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -1,10 +1,6 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; 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'; @@ -23,6 +19,8 @@ import { } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, + DataProviderHistoricalResponse, + DataProviderResponse, LookupItem, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -215,10 +213,10 @@ export class DataProviderService implements OnModuleInit { from: Date, to: Date ): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { let response: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; if (isEmpty(aItems) || !isValid(from) || !isValid(to)) { @@ -284,7 +282,7 @@ export class DataProviderService implements OnModuleInit { from: Date; to: Date; }): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { if ( @@ -317,11 +315,11 @@ export class DataProviderService implements OnModuleInit { ); const result: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; const promises: Promise<{ - data: { [date: string]: IDataProviderHistoricalResponse }; + data: { [date: string]: DataProviderHistoricalResponse }; symbol: string; }>[] = []; for (const { dataSource, symbol } of assetProfileIdentifiers) { @@ -329,7 +327,7 @@ export class DataProviderService implements OnModuleInit { if (dataProvider.canHandle(symbol)) { if (symbol === `${DEFAULT_CURRENCY}USX`) { const data: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; for (const date of eachDayOfInterval({ end: to, start: from })) { @@ -399,10 +397,10 @@ export class DataProviderService implements OnModuleInit { useCache?: boolean; user?: UserWithSettings; }): Promise<{ - [symbol: string]: IDataProviderResponse; + [symbol: string]: DataProviderResponse; }> { const response: { - [symbol: string]: IDataProviderResponse; + [symbol: string]: DataProviderResponse; } = {}; const startTimeTotal = performance.now(); @@ -716,7 +714,7 @@ export class DataProviderService implements OnModuleInit { }: { allData: { data: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; symbol: string; }[]; @@ -728,7 +726,7 @@ export class DataProviderService implements OnModuleInit { })?.data; const data: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; for (const date in rootData) { 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 c18ec193f..cd20fca44 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 @@ -7,10 +7,6 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DEFAULT_CURRENCY, @@ -18,7 +14,9 @@ import { } from '@ghostfolio/common/config'; import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupItem, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -89,7 +87,7 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }> { symbol = this.convertToEodSymbol(symbol); @@ -98,17 +96,19 @@ export class EodHistoricalDataService implements DataProviderInterface { } try { + const queryParams = new URLSearchParams({ + api_token: this.apiKey, + fmt: 'json', + from: format(from, DATE_FORMAT), + to: format(to, DATE_FORMAT) + }); + const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; const historicalResult = await fetch( - `${this.URL}/div/${symbol}?api_token=${ - this.apiKey - }&fmt=json&from=${format(from, DATE_FORMAT)}&to=${format( - to, - DATE_FORMAT - )}`, + `${this.URL}/div/${symbol}?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -141,18 +141,21 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { symbol = this.convertToEodSymbol(symbol); try { + const queryParams = new URLSearchParams({ + api_token: this.apiKey, + fmt: 'json', + from: format(from, DATE_FORMAT), + period: granularity, + to: format(to, DATE_FORMAT) + }); + const response = await fetch( - `${this.URL}/eod/${symbol}?api_token=${ - this.apiKey - }&fmt=json&from=${format(from, DATE_FORMAT)}&to=${format( - to, - DATE_FORMAT - )}&period=${granularity}`, + `${this.URL}/eod/${symbol}?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -198,8 +201,8 @@ export class EodHistoricalDataService implements DataProviderInterface { public async getQuotes({ requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; @@ -210,10 +213,14 @@ export class EodHistoricalDataService implements DataProviderInterface { }); try { + const queryParams = new URLSearchParams({ + api_token: this.apiKey, + fmt: 'json', + s: eodHistoricalDataSymbols.join(',') + }); + const realTimeResponse = await fetch( - `${this.URL}/real-time/${eodHistoricalDataSymbols[0]}?api_token=${ - this.apiKey - }&fmt=json&s=${eodHistoricalDataSymbols.join(',')}`, + `${this.URL}/real-time/${eodHistoricalDataSymbols[0]}?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -415,8 +422,12 @@ export class EodHistoricalDataService implements DataProviderInterface { })[] = []; try { + const queryParams = new URLSearchParams({ + api_token: this.apiKey + }); + const response = await fetch( - `${this.URL}/search/${query}?api_token=${this.apiKey}`, + `${this.URL}/search/${query}?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } 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 689f59fec..2b4193af5 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 @@ -8,10 +8,6 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DEFAULT_CURRENCY, @@ -19,7 +15,9 @@ import { } from '@ghostfolio/common/config'; import { DATE_FORMAT, isCurrency, parseDate } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupItem, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -43,9 +41,16 @@ import { isSameDay, parseISO } from 'date-fns'; +import { uniqBy } from 'lodash'; @Injectable() export class FinancialModelingPrepService implements DataProviderInterface { + private static countriesMapping = { + 'Korea (the Republic of)': 'South Korea', + 'Russian Federation': 'Russia', + 'Taiwan (Province of China)': 'Taiwan' + }; + private apiKey: string; public constructor( @@ -81,8 +86,13 @@ export class FinancialModelingPrepService implements DataProviderInterface { symbol.length - DEFAULT_CURRENCY.length ); } else if (this.cryptocurrencyService.isCryptocurrency(symbol)) { + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey + }); + const [quote] = await fetch( - `${this.getUrl({ version: 'stable' })}/quote?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/quote?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -95,8 +105,13 @@ export class FinancialModelingPrepService implements DataProviderInterface { ); response.name = quote.name; } else { + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey + }); + const [assetProfile] = await fetch( - `${this.getUrl({ version: 'stable' })}/profile?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/profile?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -116,19 +131,31 @@ export class FinancialModelingPrepService implements DataProviderInterface { assetSubClass === AssetSubClass.ETF || assetSubClass === AssetSubClass.MUTUALFUND ) { + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey + }); + const etfCountryWeightings = await fetch( - `${this.getUrl({ version: 'stable' })}/etf/country-weightings?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/country-weightings?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); - response.countries = etfCountryWeightings.map( - ({ country: countryName, weightPercentage }) => { + response.countries = etfCountryWeightings + .filter(({ country: countryName }) => { + return countryName.toLowerCase() !== 'other'; + }) + .map(({ country: countryName, weightPercentage }) => { let countryCode: string; for (const [code, country] of Object.entries(countries)) { - if (country.name === countryName) { + if ( + country.name === countryName || + country.name === + FinancialModelingPrepService.countriesMapping[countryName] + ) { countryCode = code; break; } @@ -138,11 +165,10 @@ export class FinancialModelingPrepService implements DataProviderInterface { code: countryCode, weight: parseFloat(weightPercentage.slice(0, -1)) / 100 }; - } - ); + }); const etfHoldings = await fetch( - `${this.getUrl({ version: 'stable' })}/etf/holdings?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/holdings?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -161,7 +187,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { ); const [etfInformation] = await fetch( - `${this.getUrl({ version: 'stable' })}/etf/info?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/info?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -172,7 +198,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } const etfSectorWeightings = await fetch( - `${this.getUrl({ version: 'stable' })}/etf/sector-weightings?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/etf/sector-weightings?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -244,12 +270,17 @@ export class FinancialModelingPrepService implements DataProviderInterface { } try { + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey + }); + const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; const dividends = await fetch( - `${this.getUrl({ version: 'stable' })}/dividends?symbol=${symbol}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/dividends?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -289,11 +320,11 @@ export class FinancialModelingPrepService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { const MAX_YEARS_PER_REQUEST = 5; const result: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = { [symbol]: {} }; @@ -309,8 +340,15 @@ export class FinancialModelingPrepService implements DataProviderInterface { ? addYears(currentFrom, MAX_YEARS_PER_REQUEST) : to; + const queryParams = new URLSearchParams({ + symbol, + apikey: this.apiKey, + from: format(currentFrom, DATE_FORMAT), + to: format(currentTo, DATE_FORMAT) + }); + const historical = await fetch( - `${this.getUrl({ version: 'stable' })}/historical-price-eod/full?symbol=${symbol}&apikey=${this.apiKey}&from=${format(currentFrom, DATE_FORMAT)}&to=${format(currentTo, DATE_FORMAT)}`, + `${this.getUrl({ version: 'stable' })}/historical-price-eod/full?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -353,8 +391,8 @@ export class FinancialModelingPrepService implements DataProviderInterface { public async getQuotes({ requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; @@ -365,6 +403,11 @@ export class FinancialModelingPrepService implements DataProviderInterface { [symbol: string]: Pick; } = {}; + const queryParams = new URLSearchParams({ + symbols: symbols.join(','), + apikey: this.apiKey + }); + const [assetProfileResolutions, quotes] = await Promise.all([ this.prismaService.assetProfileResolution.findMany({ where: { @@ -373,7 +416,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } }), fetch( - `${this.getUrl({ version: 'stable' })}/batch-quote-short?symbols=${symbols.join(',')}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/batch-quote-short?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -465,12 +508,18 @@ export class FinancialModelingPrepService implements DataProviderInterface { const assetProfileBySymbolMap: { [symbol: string]: Partial; } = {}; + let items: LookupItem[] = []; try { if (isISIN(query?.toUpperCase())) { + const queryParams = new URLSearchParams({ + apikey: this.apiKey, + isin: query.toUpperCase() + }); + const result = await fetch( - `${this.getUrl({ version: 'stable' })}/search-isin?isin=${query.toUpperCase()}&apikey=${this.apiKey}`, + `${this.getUrl({ version: 'stable' })}/search-isin?${queryParams.toString()}`, { signal: AbortSignal.timeout(requestTimeout) } @@ -496,18 +545,39 @@ export class FinancialModelingPrepService implements DataProviderInterface { }; }); } else { - const result = await fetch( - `${this.getUrl({ version: 'stable' })}/search-symbol?query=${query}&apikey=${this.apiKey}`, - { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + const queryParams = new URLSearchParams({ + query, + apikey: this.apiKey + }); + + const [nameResults, symbolResults] = await Promise.all([ + fetch( + `${this.getUrl({ version: 'stable' })}/search-name?${queryParams.toString()}`, + { + signal: AbortSignal.timeout(requestTimeout) + } + ).then((res) => res.json()), + fetch( + `${this.getUrl({ version: 'stable' })}/search-symbol?${queryParams.toString()}`, + { + signal: AbortSignal.timeout(requestTimeout) + } + ).then((res) => res.json()) + ]); + + const result = uniqBy( + [...nameResults, ...symbolResults], + ({ exchange, symbol }) => { + return `${exchange}-${symbol}`; } - ).then((res) => res.json()); + ); items = result - .filter(({ symbol }) => { - if (includeIndices === false && symbol.startsWith('^')) { + .filter(({ exchange, symbol }) => { + if ( + exchange === 'FOREX' || + (includeIndices === false && symbol.startsWith('^')) + ) { return false; } 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 ca8d72827..2b49e89c2 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -8,10 +8,6 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { HEADER_KEY_TOKEN, @@ -20,7 +16,9 @@ import { import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DataProviderGhostfolioAssetProfileResponse, + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, DividendsResponse, HistoricalResponse, LookupResponse, @@ -111,18 +109,21 @@ export class GhostfolioService implements DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }> { let dividends: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; try { + const queryParams = new URLSearchParams({ + granularity, + from: format(from, DATE_FORMAT), + to: format(to, DATE_FORMAT) + }); + const response = await fetch( - `${this.URL}/v2/data-providers/ghostfolio/dividends/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( - to, - DATE_FORMAT - )}`, + `${this.URL}/v2/data-providers/ghostfolio/dividends/${symbol}?${queryParams.toString()}`, { headers: await this.getRequestHeaders(), signal: AbortSignal.timeout(requestTimeout) @@ -164,14 +165,17 @@ export class GhostfolioService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { + const queryParams = new URLSearchParams({ + granularity, + from: format(from, DATE_FORMAT), + to: format(to, DATE_FORMAT) + }); + const response = await fetch( - `${this.URL}/v2/data-providers/ghostfolio/historical/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( - to, - DATE_FORMAT - )}`, + `${this.URL}/v2/data-providers/ghostfolio/historical/${symbol}?${queryParams.toString()}`, { headers: await this.getRequestHeaders(), signal: AbortSignal.timeout(requestTimeout) @@ -228,17 +232,21 @@ export class GhostfolioService implements DataProviderInterface { requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbols }: GetQuotesParams): Promise<{ - [symbol: string]: IDataProviderResponse; + [symbol: string]: DataProviderResponse; }> { - let quotes: { [symbol: string]: IDataProviderResponse } = {}; + let quotes: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return quotes; } try { + const queryParams = new URLSearchParams({ + symbols: symbols.join(',') + }); + const response = await fetch( - `${this.URL}/v2/data-providers/ghostfolio/quotes?symbols=${symbols.join(',')}`, + `${this.URL}/v2/data-providers/ghostfolio/quotes?${queryParams.toString()}`, { headers: await this.getRequestHeaders(), signal: AbortSignal.timeout(requestTimeout) @@ -290,8 +298,12 @@ export class GhostfolioService implements DataProviderInterface { let searchResult: LookupResponse = { items: [] }; try { + const queryParams = new URLSearchParams({ + query + }); + const response = await fetch( - `${this.URL}/v2/data-providers/ghostfolio/lookup?query=${query}`, + `${this.URL}/v2/data-providers/ghostfolio/lookup?${queryParams.toString()}`, { headers: await this.getRequestHeaders(), signal: AbortSignal.timeout(requestTimeout) 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 111f2d004..ba1e5bbe5 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 @@ -7,15 +7,13 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -60,7 +58,7 @@ export class GoogleSheetsService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { const sheet = await this.getSheet({ @@ -71,7 +69,7 @@ export class GoogleSheetsService implements DataProviderInterface { const rows = await sheet.getRows(); const historicalData: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; rows @@ -104,8 +102,8 @@ export class GoogleSheetsService implements DataProviderInterface { public async getQuotes({ symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; 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 475205a01..a55c9f328 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 @@ -1,9 +1,7 @@ import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; -import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupResponse } from '@ghostfolio/common/interfaces'; import { Granularity } from '@ghostfolio/common/types'; @@ -26,7 +24,7 @@ export interface DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }>; getHistorical({ @@ -36,7 +34,7 @@ export interface DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }>; // TODO: Return only one symbol getMaxNumberOfSymbolsPerRequest?(): number; @@ -46,7 +44,7 @@ export interface DataProviderInterface { getQuotes({ requestTimeout, symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }>; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }>; getTestSymbol(): 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 c411f678b..f18da49ab 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -7,10 +7,6 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { @@ -19,7 +15,9 @@ import { getYesterday } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupResponse, ScraperConfiguration } from '@ghostfolio/common/interfaces'; @@ -77,7 +75,7 @@ export class ManualService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( @@ -88,7 +86,7 @@ export class ManualService implements DataProviderInterface { if (defaultMarketPrice) { const historical: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = { [symbol]: {} }; @@ -132,8 +130,8 @@ export class ManualService implements DataProviderInterface { public async getQuotes({ symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; diff --git a/apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts b/apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts index 995fdb9d3..f87a22639 100644 --- a/apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts +++ b/apps/api/src/services/data-provider/rapid-api/interfaces/interfaces.ts @@ -1 +1 @@ -export interface IRapidApiResponse {} +export interface RapidApiResponse {} 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 824f44328..d6bc8d0e4 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 @@ -7,17 +7,15 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { ghostfolioFearAndGreedIndexSymbol, ghostfolioFearAndGreedIndexSymbolStocks } from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -59,7 +57,7 @@ export class RapidApiService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { if ( @@ -96,7 +94,7 @@ export class RapidApiService implements DataProviderInterface { public async getQuotes({ symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { if (symbols.length <= 0) { return {}; } 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 4ac014a47..79d4a53cd 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 @@ -9,14 +9,12 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { - IDataProviderHistoricalResponse, - IDataProviderResponse -} from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, LookupItem, LookupResponse } from '@ghostfolio/common/interfaces'; @@ -97,7 +95,7 @@ export class YahooFinanceService implements DataProviderInterface { ) ); const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; for (const historicalItem of historicalResult) { @@ -125,7 +123,7 @@ export class YahooFinanceService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { if (isSameDay(from, to)) { to = addDays(to, 1); @@ -146,7 +144,7 @@ export class YahooFinanceService implements DataProviderInterface { ); const response: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; response[symbol] = {}; @@ -184,8 +182,8 @@ export class YahooFinanceService implements DataProviderInterface { public async getQuotes({ symbols - }: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { - const response: { [symbol: string]: IDataProviderResponse } = {}; + }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { + const response: { [symbol: string]: DataProviderResponse } = {}; if (symbols.length <= 0) { return response; diff --git a/apps/api/src/services/demo/demo.service.ts b/apps/api/src/services/demo/demo.service.ts index 8f3658736..a24716d96 100644 --- a/apps/api/src/services/demo/demo.service.ts +++ b/apps/api/src/services/demo/demo.service.ts @@ -7,7 +7,7 @@ import { } from '@ghostfolio/common/config'; import { Injectable } from '@nestjs/common'; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'node:crypto'; @Injectable() export class DemoService { @@ -41,7 +41,7 @@ export class DemoService { accountId: demoAccountId, accountUserId: demoUserId, comment: null, - id: uuidv4(), + id: randomUUID(), userId: demoUserId }; }); diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts index 8f5d1c28a..076375523 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts @@ -17,11 +17,21 @@ export const ExchangeRateDataServiceMock = { '2023-07-10': 0.8854 } }); + } else if (targetCurrency === 'EUR') { + return Promise.resolve({ + EUREUR: { + '2021-12-12': 1 + }, + USDEUR: { + '2021-12-12': 0.8855 + } + }); } else if (targetCurrency === 'USD') { return Promise.resolve({ USDUSD: { '2018-01-01': 1, '2021-11-16': 1, + '2021-12-12': 1, '2023-07-10': 1 } }); diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index 433547c94..8c1ba5b41 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -1,6 +1,6 @@ import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; 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'; @@ -29,7 +29,8 @@ import ms from 'ms'; @Injectable() export class ExchangeRateDataService { private currencies: string[] = []; - private currencyPairs: IDataGatheringItem[] = []; + private currencyPairs: DataGatheringItem[] = []; + private derivedCurrencyFactors: { [currencyPair: string]: number } = {}; private exchangeRates: { [currencyPair: string]: number } = {}; public constructor( @@ -135,8 +136,14 @@ export class ExchangeRateDataService { public async initialize() { this.currencies = await this.prepareCurrencies(); this.currencyPairs = []; + this.derivedCurrencyFactors = {}; this.exchangeRates = {}; + for (const { currency, factor, rootCurrency } of DERIVED_CURRENCIES) { + this.derivedCurrencyFactors[`${currency}${rootCurrency}`] = 1 / factor; + this.derivedCurrencyFactors[`${rootCurrency}${currency}`] = factor; + } + for (const { currency1, currency2, @@ -266,10 +273,14 @@ export class ExchangeRateDataService { return this.toCurrency(aValue, aFromCurrency, aToCurrency); } + const derivedCurrencyFactor = + this.derivedCurrencyFactors[`${aFromCurrency}${aToCurrency}`]; let factor: number; if (aFromCurrency === aToCurrency) { factor = 1; + } else if (derivedCurrencyFactor) { + factor = derivedCurrencyFactor; } else { const dataSource = this.dataProviderService.getDataSourceForExchangeRates(); @@ -357,111 +368,120 @@ export class ExchangeRateDataService { for (const date of dates) { factors[format(date, DATE_FORMAT)] = 1; } - } else { - const dataSource = - this.dataProviderService.getDataSourceForExchangeRates(); - const symbol = `${currencyFrom}${currencyTo}`; - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol - } - ], - dateQuery: { gte: startDate, lt: endDate } - }); + return factors; + } + + const derivedCurrencyFactor = + this.derivedCurrencyFactors[`${currencyFrom}${currencyTo}`]; + + if (derivedCurrencyFactor) { + for (const date of dates) { + factors[format(date, DATE_FORMAT)] = derivedCurrencyFactor; + } - if (marketData?.length > 0) { - for (const { date, marketPrice } of marketData) { - factors[format(date, DATE_FORMAT)] = marketPrice; + return factors; + } + + const dataSource = this.dataProviderService.getDataSourceForExchangeRates(); + const symbol = `${currencyFrom}${currencyTo}`; + + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol } - } else { - // Calculate indirectly via base currency + ], + dateQuery: { gte: startDate, lt: endDate } + }); - const marketPriceBaseCurrencyFromCurrency: { - [dateString: string]: number; - } = {}; - const marketPriceBaseCurrencyToCurrency: { - [dateString: string]: number; - } = {}; + if (marketData?.length > 0) { + for (const { date, marketPrice } of marketData) { + factors[format(date, DATE_FORMAT)] = marketPrice; + } + } else { + // Calculate indirectly via base currency + + const marketPriceBaseCurrencyFromCurrency: { + [dateString: string]: number; + } = {}; + const marketPriceBaseCurrencyToCurrency: { + [dateString: string]: number; + } = {}; + + try { + if (currencyFrom === DEFAULT_CURRENCY) { + for (const date of dates) { + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = 1; + } + } else { + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol: `${DEFAULT_CURRENCY}${currencyFrom}` + } + ], + dateQuery: { gte: startDate, lt: endDate } + }); - try { - if (currencyFrom === DEFAULT_CURRENCY) { - for (const date of dates) { - marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = - 1; - } - } else { - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol: `${DEFAULT_CURRENCY}${currencyFrom}` - } - ], - dateQuery: { gte: startDate, lt: endDate } - }); - - for (const { date, marketPrice } of marketData) { - marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = - marketPrice; - } + for (const { date, marketPrice } of marketData) { + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = + marketPrice; } - } catch {} + } + } catch {} - try { - if (currencyTo === DEFAULT_CURRENCY) { - for (const date of dates) { - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1; - } - } else { - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol: `${DEFAULT_CURRENCY}${currencyTo}` - } - ], - dateQuery: { - gte: startDate, - lt: endDate + try { + if (currencyTo === DEFAULT_CURRENCY) { + for (const date of dates) { + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1; + } + } else { + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol: `${DEFAULT_CURRENCY}${currencyTo}` } - }); - - for (const { date, marketPrice } of marketData) { - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = - marketPrice; + ], + dateQuery: { + gte: startDate, + lt: endDate } + }); + + for (const { date, marketPrice } of marketData) { + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = + marketPrice; } - } catch {} + } + } catch {} - for (const date of dates) { - try { - const factor = - (1 / - marketPriceBaseCurrencyFromCurrency[ - format(date, DATE_FORMAT) - ]) * - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)]; - - if (isNaN(factor)) { - throw new Error('Exchange rate is not a number'); - } else { - factors[format(date, DATE_FORMAT)] = factor; - } - } catch { - let errorMessage = `No exchange rate has been found for ${currencyFrom}${currencyTo} at ${format( - date, - DATE_FORMAT - )}. Please complement market data for ${DEFAULT_CURRENCY}${currencyFrom}`; - - if (DEFAULT_CURRENCY !== currencyTo) { - errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${currencyTo}`; - } + for (const date of dates) { + try { + const factor = + (1 / + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)]) * + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)]; - Logger.error(`${errorMessage}.`, 'ExchangeRateDataService'); + if (isNaN(factor)) { + throw new Error('Exchange rate is not a number'); + } else { + factors[format(date, DATE_FORMAT)] = factor; } + } catch { + let errorMessage = `No exchange rate has been found for ${currencyFrom}${currencyTo} at ${format( + date, + DATE_FORMAT + )}. Please complement market data for ${DEFAULT_CURRENCY}${currencyFrom}`; + + if (DEFAULT_CURRENCY !== currencyTo) { + errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${currencyTo}`; + } + + Logger.error(`${errorMessage}.`, 'ExchangeRateDataService'); } } } diff --git a/apps/api/src/services/i18n/i18n.service.ts b/apps/api/src/services/i18n/i18n.service.ts index 0f1f6239d..cf340d7c6 100644 --- a/apps/api/src/services/i18n/i18n.service.ts +++ b/apps/api/src/services/i18n/i18n.service.ts @@ -65,7 +65,7 @@ export class I18nService { } private parseLanguageCode(aFileName: string) { - const match = aFileName.match(/\.([a-zA-Z]+)\.xlf$/); + const match = /\.([a-zA-Z]+)\.xlf$/.exec(aFileName); return match ? match[1] : DEFAULT_LANGUAGE_CODE; } diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 2f94739fb..3a2ac687c 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -16,9 +16,11 @@ export interface Environment extends CleanedEnvAccessors { DATA_SOURCE_IMPORT: string; DATA_SOURCES: string[]; DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER: string[]; + ENABLE_FEATURE_AUTH_GOOGLE: boolean; + ENABLE_FEATURE_AUTH_TOKEN: boolean; ENABLE_FEATURE_FEAR_AND_GREED_INDEX: boolean; + ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES: boolean; ENABLE_FEATURE_READ_ONLY_MODE: boolean; - ENABLE_FEATURE_SOCIAL_LOGIN: boolean; ENABLE_FEATURE_STATISTICS: boolean; ENABLE_FEATURE_SUBSCRIPTION: boolean; ENABLE_FEATURE_SYSTEM_MESSAGE: boolean; diff --git a/apps/api/src/services/interfaces/interfaces.ts b/apps/api/src/services/interfaces/interfaces.ts index 0eaa149a3..87eaa3a75 100644 --- a/apps/api/src/services/interfaces/interfaces.ts +++ b/apps/api/src/services/interfaces/interfaces.ts @@ -1,23 +1,6 @@ -import { - AssetProfileIdentifier, - DataProviderInfo -} from '@ghostfolio/common/interfaces'; -import { MarketState } from '@ghostfolio/common/types'; +import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; -import { DataSource } from '@prisma/client'; - -export interface IDataProviderHistoricalResponse { - marketPrice: number; -} - -export interface IDataProviderResponse { - currency: string; - dataProviderInfo?: DataProviderInfo; - dataSource: DataSource; - marketPrice: number; - marketState: MarketState; -} - -export interface IDataGatheringItem extends AssetProfileIdentifier { +export interface DataGatheringItem extends AssetProfileIdentifier { date?: Date; + force?: boolean; } diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index 58b9b09ec..87b08e1bd 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -1,7 +1,7 @@ -import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto'; import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface'; -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; +import { UpdateMarketDataDto } from '@ghostfolio/common/dtos'; import { resetHours } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; @@ -30,7 +30,7 @@ export class MarketDataService { dataSource, date = new Date(), symbol - }: IDataGatheringItem): Promise { + }: DataGatheringItem): Promise { return await this.prismaService.marketData.findFirst({ where: { dataSource, @@ -132,6 +132,61 @@ export class MarketDataService { }); } + /** + * Atomically replace market data for a symbol within a date range. + * Deletes existing data in the range and inserts new data within a single + * transaction to prevent data loss if the operation fails. + */ + public async replaceForSymbol({ + data, + dataSource, + symbol + }: AssetProfileIdentifier & { data: Prisma.MarketDataUpdateInput[] }) { + await this.prismaService.$transaction(async (prisma) => { + if (data.length > 0) { + let minTime = Infinity; + let maxTime = -Infinity; + + for (const { date } of data) { + const time = (date as Date).getTime(); + + if (time < minTime) { + minTime = time; + } + + if (time > maxTime) { + maxTime = time; + } + } + + const minDate = new Date(minTime); + const maxDate = new Date(maxTime); + + await prisma.marketData.deleteMany({ + where: { + dataSource, + symbol, + date: { + gte: minDate, + lte: maxDate + } + } + }); + + await prisma.marketData.createMany({ + data: data.map(({ date, marketPrice, state }) => ({ + dataSource, + symbol, + date: date as Date, + marketPrice: marketPrice as number, + state: state as MarketDataState + })), + skipDuplicates: true + }); + } + }); + } + public async updateAssetProfileIdentifier( oldAssetProfileIdentifier: AssetProfileIdentifier, newAssetProfileIdentifier: AssetProfileIdentifier diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts index 9cf6f63e6..1a4038652 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts @@ -1,6 +1,6 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error'; -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { @@ -99,8 +99,8 @@ export class DataGatheringProcessor { ), name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME }) - public async gatherHistoricalMarketData(job: Job) { - const { dataSource, date, symbol } = job.data; + public async gatherHistoricalMarketData(job: Job) { + const { dataSource, date, force, symbol } = job.data; try { let currentDate = parseISO(date as unknown as string); @@ -109,7 +109,7 @@ export class DataGatheringProcessor { `Historical market data gathering has been started for ${symbol} (${dataSource}) at ${format( currentDate, DATE_FORMAT - )}`, + )}${force ? ' (forced update)' : ''}`, `DataGatheringProcessor (${GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME})` ); @@ -157,7 +157,15 @@ export class DataGatheringProcessor { currentDate = addDays(currentDate, 1); } - await this.marketDataService.updateMany({ data }); + if (force) { + await this.marketDataService.replaceForSymbol({ + data, + dataSource, + symbol + }); + } else { + await this.marketDataService.updateMany({ data }); + } Logger.log( `Historical market data gathering has been completed for ${symbol} (${dataSource}) at ${format( 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 dd93e3e47..cec63c3eb 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 @@ -1,8 +1,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; -import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; @@ -29,8 +28,9 @@ import { InjectQueue } from '@nestjs/bull'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { DataSource } from '@prisma/client'; import { JobOptions, Queue } from 'bull'; -import { format, min, subDays, subYears } from 'date-fns'; +import { format, min, subDays, subMilliseconds, subYears } from 'date-fns'; import { isEmpty } from 'lodash'; +import ms, { StringValue } from 'ms'; @Injectable() export class DataGatheringService { @@ -41,7 +41,6 @@ export class DataGatheringService { private readonly dataGatheringQueue: Queue, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, - private readonly marketDataService: MarketDataService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, private readonly symbolProfileService: SymbolProfileService @@ -94,9 +93,7 @@ export class DataGatheringService { }); } - public async gatherSymbol({ dataSource, date, symbol }: IDataGatheringItem) { - await this.marketDataService.deleteMany({ dataSource, symbol }); - + public async gatherSymbol({ dataSource, date, symbol }: DataGatheringItem) { const dataGatheringItems = (await this.getSymbolsMax()) .filter((dataGatheringItem) => { return ( @@ -111,6 +108,7 @@ export class DataGatheringService { await this.gatherSymbols({ dataGatheringItems, + force: true, priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH }); } @@ -163,8 +161,7 @@ export class DataGatheringService { ); if (!assetProfileIdentifiers) { - assetProfileIdentifiers = - await this.getAllActiveAssetProfileIdentifiers(); + assetProfileIdentifiers = await this.getActiveAssetProfileIdentifiers(); } if (assetProfileIdentifiers.length <= 0) { @@ -274,9 +271,11 @@ export class DataGatheringService { public async gatherSymbols({ dataGatheringItems, + force = false, priority }: { - dataGatheringItems: IDataGatheringItem[]; + dataGatheringItems: DataGatheringItem[]; + force?: boolean; priority: number; }) { await this.addJobsToQueue( @@ -285,6 +284,7 @@ export class DataGatheringService { data: { dataSource, date, + force, symbol }, name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME, @@ -301,29 +301,36 @@ export class DataGatheringService { ); } - public async getAllActiveAssetProfileIdentifiers(): Promise< - AssetProfileIdentifier[] - > { - const symbolProfiles = await this.prismaService.symbolProfile.findMany({ - orderBy: [{ symbol: 'asc' }], + /** + * Returns active asset profile identifiers + * + * @param {StringValue} maxAge - Optional. Specifies the maximum allowed age + * of a profile’s last update timestamp. Only asset profiles considered stale + * are returned. + */ + public async getActiveAssetProfileIdentifiers({ + maxAge + }: { + maxAge?: StringValue; + } = {}): Promise { + return this.prismaService.symbolProfile.findMany({ + orderBy: [{ symbol: 'asc' }, { dataSource: 'asc' }], + select: { + dataSource: true, + symbol: true + }, where: { - isActive: true + dataSource: { + notIn: ['MANUAL', 'RAPID_API'] + }, + isActive: true, + ...(maxAge && { + updatedAt: { + lt: subMilliseconds(new Date(), ms(maxAge)) + } + }) } }); - - return symbolProfiles - .filter(({ dataSource }) => { - return ( - dataSource !== DataSource.MANUAL && - dataSource !== DataSource.RAPID_API - ); - }) - .map(({ dataSource, symbol }) => { - return { - dataSource, - symbol - }; - }); } private async getAssetProfileIdentifiersWithCompleteMarketData(): Promise< @@ -348,7 +355,7 @@ export class DataGatheringService { }); } - private async getCurrencies7D(): Promise { + private async getCurrencies7D(): Promise { const assetProfileIdentifiersWithCompleteMarketData = await this.getAssetProfileIdentifiersWithCompleteMarketData(); @@ -376,7 +383,7 @@ export class DataGatheringService { withUserSubscription = false }: { withUserSubscription?: boolean; - }): Promise { + }): Promise { const symbolProfiles = await this.symbolProfileService.getActiveSymbolProfilesByUserSubscription( { @@ -407,7 +414,7 @@ export class DataGatheringService { }); } - private async getSymbolsMax(): Promise { + private async getSymbolsMax(): Promise { const benchmarkAssetProfileIdMap: { [key: string]: boolean } = {}; ( (await this.propertyService.getByKey( diff --git a/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts b/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts index b9f315c5d..3486974f7 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts @@ -1,7 +1,7 @@ import { Filter } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; -export interface IPortfolioSnapshotQueueJob { +export interface PortfolioSnapshotQueueJob { calculationType: PerformanceCalculationType; filters: Filter[]; userCurrency: string; diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts index 6a2a3114e..75a3a8631 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts @@ -16,7 +16,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { Job } from 'bull'; import { addMilliseconds } from 'date-fns'; -import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; +import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; @Injectable() @Processor(PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE) @@ -37,9 +37,7 @@ export class PortfolioSnapshotProcessor { ), name: PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME }) - public async calculatePortfolioSnapshot( - job: Job - ) { + public async calculatePortfolioSnapshot(job: Job) { try { const startTime = performance.now(); diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts index 59fdc5855..898718106 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts @@ -1,13 +1,13 @@ import { Job, JobOptions } from 'bull'; import { setTimeout } from 'timers/promises'; -import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; +import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; export const PortfolioSnapshotServiceMock = { addJobToQueue({ opts }: { - data: IPortfolioSnapshotQueueJob; + data: PortfolioSnapshotQueueJob; name: string; opts?: JobOptions; }): Promise> { diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts index 9dba9275e..d7449a9cc 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.ts @@ -4,7 +4,7 @@ import { InjectQueue } from '@nestjs/bull'; import { Injectable } from '@nestjs/common'; import { JobOptions, Queue } from 'bull'; -import { IPortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; +import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; @Injectable() export class PortfolioSnapshotService { @@ -18,7 +18,7 @@ export class PortfolioSnapshotService { name, opts }: { - data: IPortfolioSnapshotQueueJob; + data: PortfolioSnapshotQueueJob; name: string; opts?: JobOptions; }) { diff --git a/apps/client-e2e/.eslintrc.json b/apps/client-e2e/.eslintrc.json deleted file mode 100644 index dbedf6bd4..000000000 --- a/apps/client-e2e/.eslintrc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "parserOptions": { - "project": ["apps/client-e2e/tsconfig.*?.json"] - }, - "rules": {} - }, - { - "files": ["src/plugins/index.js"], - "rules": { - "@typescript-eslint/no-var-requires": "off", - "no-undef": "off" - } - } - ] -} diff --git a/apps/client-e2e/cypress.json b/apps/client-e2e/cypress.json deleted file mode 100644 index a8219f0fe..000000000 --- a/apps/client-e2e/cypress.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "fileServerFolder": ".", - "fixturesFolder": "./src/fixtures", - "integrationFolder": "./src/integration", - "modifyObstructiveCode": false, - "pluginsFile": "./src/plugins/index", - "supportFile": "./src/support/index.ts", - "video": true, - "videosFolder": "../../dist/cypress/apps/client-e2e/videos", - "screenshotsFolder": "../../dist/cypress/apps/client-e2e/screenshots", - "chromeWebSecurity": false -} diff --git a/apps/client-e2e/project.json b/apps/client-e2e/project.json deleted file mode 100644 index 92e2f09ef..000000000 --- a/apps/client-e2e/project.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "client-e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/client-e2e/src", - "projectType": "application", - "tags": [], - "implicitDependencies": ["client"], - "targets": { - "e2e": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/client-e2e/cypress.json", - "devServerTarget": "client:serve" - }, - "configurations": { - "production": { - "devServerTarget": "client:serve:production" - } - } - } - } -} diff --git a/apps/client-e2e/src/fixtures/example.json b/apps/client-e2e/src/fixtures/example.json deleted file mode 100644 index 294cbed6c..000000000 --- a/apps/client-e2e/src/fixtures/example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io" -} diff --git a/apps/client-e2e/src/integration/app.spec.ts b/apps/client-e2e/src/integration/app.spec.ts deleted file mode 100644 index b194092d7..000000000 --- a/apps/client-e2e/src/integration/app.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getGreeting } from '../support/app.po'; - -describe('client', () => { - beforeEach(() => cy.visit('/')); - - it('should display welcome message', () => { - // Custom command example, see `../support/commands.ts` file - cy.login('my-email@something.com', 'myPassword'); - - // Function helper example, see `../support/app.po.ts` file - getGreeting().contains('Welcome to client!'); - }); -}); diff --git a/apps/client-e2e/src/plugins/index.js b/apps/client-e2e/src/plugins/index.js deleted file mode 100644 index 63aa33cbe..000000000 --- a/apps/client-e2e/src/plugins/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor'); - -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - - // Preprocess Typescript file using Nx helper - on('file:preprocessor', preprocessTypescript(config)); -}; diff --git a/apps/client-e2e/src/support/app.po.ts b/apps/client-e2e/src/support/app.po.ts deleted file mode 100644 index 329342469..000000000 --- a/apps/client-e2e/src/support/app.po.ts +++ /dev/null @@ -1 +0,0 @@ -export const getGreeting = () => cy.get('h1'); diff --git a/apps/client-e2e/src/support/commands.ts b/apps/client-e2e/src/support/commands.ts deleted file mode 100644 index 36c834059..000000000 --- a/apps/client-e2e/src/support/commands.ts +++ /dev/null @@ -1,31 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -declare namespace Cypress { - interface Chainable { - login(email: string, password: string): void; - } -} -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/client-e2e/src/support/index.ts b/apps/client-e2e/src/support/index.ts deleted file mode 100644 index fad130159..000000000 --- a/apps/client-e2e/src/support/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** -// Import commands.js using ES2015 syntax: -import './commands'; diff --git a/apps/client-e2e/tsconfig.e2e.json b/apps/client-e2e/tsconfig.e2e.json deleted file mode 100644 index 9dc3660a7..000000000 --- a/apps/client-e2e/tsconfig.e2e.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "sourceMap": false, - "outDir": "../../dist/out-tsc", - "allowJs": true, - "types": ["cypress", "node"] - }, - "include": ["src/**/*.ts", "src/**/*.js"] -} diff --git a/apps/client-e2e/tsconfig.json b/apps/client-e2e/tsconfig.json deleted file mode 100644 index 08841a7f5..000000000 --- a/apps/client-e2e/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.e2e.json" - } - ] -} diff --git a/apps/client/project.json b/apps/client/project.json index 7c6c3d260..29549eaaf 100644 --- a/apps/client/project.json +++ b/apps/client/project.json @@ -61,30 +61,30 @@ }, "targets": { "build": { - "executor": "@nx/angular:webpack-browser", + "executor": "@nx/angular:browser-esbuild", "options": { - "deleteOutputPath": false, - "localize": true, - "outputPath": "dist/apps/client", "index": "apps/client/src/index.html", "main": "apps/client/src/main.ts", - "polyfills": "apps/client/src/polyfills.ts", + "outputPath": "dist/apps/client", "tsConfig": "apps/client/tsconfig.app.json", + "buildOptimizer": false, + "deleteOutputPath": false, + "extractLicenses": false, + "localize": true, + "namedChunks": true, + "ngswConfigPath": "apps/client/ngsw-config.json", + "optimization": false, + "polyfills": "apps/client/src/polyfills.ts", + "scripts": ["node_modules/marked/marked.min.js"], + "serviceWorker": true, + "sourceMap": true, "styles": [ "apps/client/src/assets/fonts/inter.css", "apps/client/src/styles/theme.scss", "apps/client/src/styles.scss", "node_modules/open-color/open-color.css" ], - "scripts": ["node_modules/marked/marked.min.js"], - "vendorChunk": true, - "extractLicenses": false, - "buildOptimizer": false, - "sourceMap": true, - "optimization": false, - "namedChunks": true, - "serviceWorker": true, - "ngswConfigPath": "apps/client/ngsw-config.json" + "vendorChunk": true }, "configurations": { "development-ca": { @@ -136,19 +136,6 @@ "localize": ["zh"] }, "production": { - "fileReplacements": [ - { - "replace": "apps/client/src/environments/environment.ts", - "with": "apps/client/src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -160,7 +147,20 @@ "maximumWarning": "6kb", "maximumError": "10kb" } - ] + ], + "buildOptimizer": true, + "extractLicenses": true, + "fileReplacements": [ + { + "replace": "apps/client/src/environments/environment.ts", + "with": "apps/client/src/environments/environment.prod.ts" + } + ], + "namedChunks": false, + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "vendorChunk": false } }, "outputs": ["{options.outputPath}"], diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index bddd7d3da..de82c7d9c 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -1,5 +1,3 @@ -import { GfHoldingDetailDialogComponent } from '@ghostfolio/client/components/holding-detail-dialog/holding-detail-dialog.component'; -import { HoldingDetailDialogParams } from '@ghostfolio/client/components/holding-detail-dialog/interfaces/interfaces'; import { getCssVariable } from '@ghostfolio/common/helper'; import { InfoItem, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -22,7 +20,9 @@ import { ActivatedRoute, NavigationEnd, PRIMARY_OUTLET, - Router + Router, + RouterLink, + RouterOutlet } from '@angular/router'; import { DataSource } from '@prisma/client'; import { addIcons } from 'ionicons'; @@ -31,6 +31,10 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; +import { GfFooterComponent } from './components/footer/footer.component'; +import { GfHeaderComponent } from './components/header/header.component'; +import { GfHoldingDetailDialogComponent } from './components/holding-detail-dialog/holding-detail-dialog.component'; +import { HoldingDetailDialogParams } from './components/holding-detail-dialog/interfaces/interfaces'; import { NotificationService } from './core/notification/notification.service'; import { DataService } from './services/data.service'; import { ImpersonationStorageService } from './services/impersonation-storage.service'; @@ -38,13 +42,13 @@ import { TokenStorageService } from './services/token-storage.service'; import { UserService } from './services/user/user.service'; @Component({ - selector: 'gf-root', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './app.component.html', + imports: [GfFooterComponent, GfHeaderComponent, RouterLink, RouterOutlet], + selector: 'gf-root', styleUrls: ['./app.component.scss'], - standalone: false + templateUrl: './app.component.html' }) -export class AppComponent implements OnDestroy, OnInit { +export class GfAppComponent implements OnDestroy, OnInit { @HostBinding('class.has-info-message') get getHasMessage() { return this.hasInfoMessage; } @@ -106,10 +110,6 @@ export class AppComponent implements OnDestroy, OnInit { this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.info = this.dataService.fetchInfo(); - this.hasPromotion = - !!this.info?.subscriptionOffer?.coupon || - !!this.info?.subscriptionOffer?.durationExtension; - this.impersonationStorageService .onChangeHasImpersonation() .pipe(takeUntil(this.unsubscribeSubject)) @@ -213,9 +213,11 @@ export class AppComponent implements OnDestroy, OnInit { this.hasInfoMessage = this.canCreateAccount || !!this.user?.systemMessage; - this.hasPromotion = - !!this.user?.subscription?.offer?.coupon || - !!this.user?.subscription?.offer?.durationExtension; + this.hasPromotion = this.user + ? !!this.user.subscription?.offer?.coupon || + !!this.user.subscription?.offer?.durationExtension + : !!this.info?.subscriptionOffer?.coupon || + !!this.info?.subscriptionOffer?.durationExtension; this.initializeTheme(this.user?.settings.colorScheme); @@ -276,7 +278,10 @@ export class AppComponent implements OnDestroy, OnInit { .subscribe((user) => { this.user = user; - const dialogRef = this.dialog.open(GfHoldingDetailDialogComponent, { + const dialogRef = this.dialog.open< + GfHoldingDetailDialogComponent, + HoldingDetailDialogParams + >(GfHoldingDetailDialogComponent, { autoFocus: false, data: { dataSource, @@ -302,7 +307,7 @@ export class AppComponent implements OnDestroy, OnInit { hasPermission(this.user?.permissions, permissions.updateOrder) && !this.user?.settings?.isRestrictedView, locale: this.user?.settings?.locale - } as HoldingDetailDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : '80vh', width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); diff --git a/apps/client/src/app/app.module.ts b/apps/client/src/app/app.module.ts deleted file mode 100644 index 63de8fca7..000000000 --- a/apps/client/src/app/app.module.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Platform } from '@angular/cdk/platform'; -import { - provideHttpClient, - withInterceptorsFromDi -} from '@angular/common/http'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; -import { MatChipsModule } from '@angular/material/chips'; -import { - DateAdapter, - MAT_DATE_FORMATS, - MAT_DATE_LOCALE, - MatNativeDateModule -} from '@angular/material/core'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ServiceWorkerModule } from '@angular/service-worker'; -import { provideIonicAngular } from '@ionic/angular/standalone'; -import { provideMarkdown } from 'ngx-markdown'; -import { provideNgxSkeletonLoader } from 'ngx-skeleton-loader'; -import { NgxStripeModule, STRIPE_PUBLISHABLE_KEY } from 'ngx-stripe'; - -import { environment } from '../environments/environment'; -import { CustomDateAdapter } from './adapter/custom-date-adapter'; -import { DateFormats } from './adapter/date-formats'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { GfFooterComponent } from './components/footer/footer.component'; -import { GfHeaderComponent } from './components/header/header.component'; -import { authInterceptorProviders } from './core/auth.interceptor'; -import { httpResponseInterceptorProviders } from './core/http-response.interceptor'; -import { LanguageService } from './core/language.service'; -import { GfNotificationModule } from './core/notification/notification.module'; - -export function NgxStripeFactory(): string { - return environment.stripePublicKey; -} - -@NgModule({ - bootstrap: [AppComponent], - declarations: [AppComponent], - imports: [ - AppRoutingModule, - BrowserAnimationsModule, - BrowserModule, - GfFooterComponent, - GfHeaderComponent, - GfNotificationModule, - MatAutocompleteModule, - MatChipsModule, - MatNativeDateModule, - MatSnackBarModule, - MatTooltipModule, - NgxStripeModule.forRoot(environment.stripePublicKey), - ServiceWorkerModule.register('ngsw-worker.js', { - enabled: environment.production, - registrationStrategy: 'registerImmediately' - }) - ], - providers: [ - authInterceptorProviders, - httpResponseInterceptorProviders, - LanguageService, - provideHttpClient(withInterceptorsFromDi()), - provideIonicAngular(), - provideMarkdown(), - provideNgxSkeletonLoader(), - { - provide: DateAdapter, - useClass: CustomDateAdapter, - deps: [LanguageService, MAT_DATE_LOCALE, Platform] - }, - { provide: MAT_DATE_FORMATS, useValue: DateFormats }, - { - provide: STRIPE_PUBLISHABLE_KEY, - useFactory: NgxStripeFactory - } - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class AppModule {} diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app.routes.ts similarity index 81% rename from apps/client/src/app/app-routing.module.ts rename to apps/client/src/app/app.routes.ts index fb045a174..9588cee68 100644 --- a/apps/client/src/app/app-routing.module.ts +++ b/apps/client/src/app/app.routes.ts @@ -1,13 +1,10 @@ -import { publicRoutes, internalRoutes } from '@ghostfolio/common/routes/routes'; +import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes, TitleStrategy } from '@angular/router'; +import { Routes } from '@angular/router'; import { AuthGuard } from './core/auth.guard'; -import { ModulePreloadService } from './core/module-preload.service'; -import { PageTitleStrategy } from './services/page-title.strategy'; -const routes: Routes = [ +export const routes: Routes = [ { path: publicRoutes.about.path, loadChildren: () => @@ -147,24 +144,3 @@ const routes: Routes = [ pathMatch: 'full' } ]; - -@NgModule({ - imports: [ - RouterModule.forRoot( - routes, - // Preload all lazy loaded modules with the attribute preload === true - { - anchorScrolling: 'enabled', - // enableTracing: true, // <-- debugging purposes only - preloadingStrategy: ModulePreloadService, - scrollPositionRestoration: 'top' - } - ) - ], - providers: [ - ModulePreloadService, - { provide: TitleStrategy, useClass: PageTitleStrategy } - ], - exports: [RouterModule] -}) -export class AppRoutingModule {} diff --git a/apps/client/src/app/components/access-table/access-table.component.html b/apps/client/src/app/components/access-table/access-table.component.html index abeda6de8..cb41904d3 100644 --- a/apps/client/src/app/components/access-table/access-table.component.html +++ b/apps/client/src/app/components/access-table/access-table.component.html @@ -73,7 +73,7 @@ } diff --git a/apps/client/src/app/components/access-table/access-table.component.ts b/apps/client/src/app/components/access-table/access-table.component.ts index 645d43253..1127e5629 100644 --- a/apps/client/src/app/components/access-table/access-table.component.ts +++ b/apps/client/src/app/components/access-table/access-table.component.ts @@ -1,5 +1,5 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { Access, User } from '@ghostfolio/common/interfaces'; import { publicRoutes } from '@ghostfolio/common/routes/routes'; diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index bdc2424f5..d8f08ecc2 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -1,14 +1,12 @@ -import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/create-account-balance.dto'; -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; -import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component'; -import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { NUMERICAL_PRECISION_THRESHOLD_6_FIGURES } from '@ghostfolio/common/config'; +import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos'; import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper'; import { AccountBalancesResponse, + Activity, HistoricalDataItem, PortfolioPosition, User @@ -18,6 +16,8 @@ import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { OrderWithAccount } from '@ghostfolio/common/types'; import { GfAccountBalancesComponent } from '@ghostfolio/ui/account-balances'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; +import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; +import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -43,9 +43,9 @@ import { Big } from 'big.js'; import { format, parseISO } from 'date-fns'; import { addIcons } from 'ionicons'; import { + albumsOutline, cashOutline, - swapVerticalOutline, - walletOutline + swapVerticalOutline } from 'ionicons/icons'; import { isNumber } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -129,7 +129,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { } }); - addIcons({ cashOutline, swapVerticalOutline, walletOutline }); + addIcons({ albumsOutline, cashOutline, swapVerticalOutline }); } public ngOnInit() { diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html index 335238e1c..b734f3208 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html @@ -98,7 +98,7 @@ > - +
Holdings

diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.ts b/apps/client/src/app/components/admin-platform/admin-platform.component.ts index 845c7f375..64e7ff7cf 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.ts +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.ts @@ -1,10 +1,9 @@ -import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; -import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.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'; +import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { @@ -34,6 +33,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, takeUntil } from 'rxjs'; import { GfCreateOrUpdatePlatformDialogComponent } from './create-or-update-platform-dialog/create-or-update-platform-dialog.component'; +import { CreateOrUpdatePlatformDialogParams } from './create-or-update-platform-dialog/interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -50,7 +50,7 @@ import { GfCreateOrUpdatePlatformDialogComponent } from './create-or-update-plat styleUrls: ['./admin-platform.component.scss'], templateUrl: './admin-platform.component.html' }) -export class GfAdminPlatformComponent implements OnInit, OnDestroy { +export class GfAdminPlatformComponent implements OnDestroy, OnInit { @ViewChild(MatSort) sort: MatSort; public dataSource = new MatTableDataSource(); @@ -153,19 +153,20 @@ export class GfAdminPlatformComponent implements OnInit, OnDestroy { } private openCreatePlatformDialog() { - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfCreateOrUpdatePlatformDialogComponent, - { - data: { - platform: { - name: null, - url: null - } - }, - height: this.deviceType === 'mobile' ? '98vh' : undefined, - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + CreateOrUpdatePlatformDialogParams + >(GfCreateOrUpdatePlatformDialogComponent, { + data: { + platform: { + id: null, + name: null, + url: null + } + }, + height: this.deviceType === 'mobile' ? '98vh' : undefined, + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() @@ -191,21 +192,29 @@ export class GfAdminPlatformComponent implements OnInit, OnDestroy { }); } - private openUpdatePlatformDialog({ id, name, url }) { - const dialogRef = this.dialog.open( + private openUpdatePlatformDialog({ + id, + name, + url + }: { + id: string; + name: string; + url: string; + }) { + const dialogRef = this.dialog.open< GfCreateOrUpdatePlatformDialogComponent, - { - data: { - platform: { - id, - name, - url - } - }, - height: this.deviceType === 'mobile' ? '98vh' : undefined, - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + CreateOrUpdatePlatformDialogParams + >(GfCreateOrUpdatePlatformDialogComponent, { + data: { + platform: { + id, + name, + url + } + }, + height: this.deviceType === 'mobile' ? '98vh' : undefined, + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() diff --git a/apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts b/apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts index 48a6ca432..dfcf300c1 100644 --- a/apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts +++ b/apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts @@ -1,6 +1,5 @@ -import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; -import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto'; -import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; +import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.ts b/apps/client/src/app/components/admin-settings/admin-settings.component.ts index 899aadc6c..ec44b6e65 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.ts +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.ts @@ -1,12 +1,12 @@ import { GfAdminPlatformComponent } from '@ghostfolio/client/components/admin-platform/admin-platform.component'; import { GfAdminTagComponent } from '@ghostfolio/client/components/admin-tag/admin-tag.component'; import { GfDataProviderStatusComponent } from '@ghostfolio/client/components/data-provider-status/data-provider-status.component'; -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'; import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { DataProviderGhostfolioStatusResponse, diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.html b/apps/client/src/app/components/admin-tag/admin-tag.component.html index 5979d2778..86377c937 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.html +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.html @@ -38,7 +38,7 @@ @@ -64,7 +64,7 @@
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 de4c8cedc..a891baa45 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,9 +1,8 @@ -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 { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { ChangeDetectionStrategy, @@ -32,6 +31,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, takeUntil } from 'rxjs'; import { GfCreateOrUpdateTagDialogComponent } from './create-or-update-tag-dialog/create-or-update-tag-dialog.component'; +import { CreateOrUpdateTagDialogParams } from './create-or-update-tag-dialog/interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -47,7 +47,7 @@ import { GfCreateOrUpdateTagDialogComponent } from './create-or-update-tag-dialo styleUrls: ['./admin-tag.component.scss'], templateUrl: './admin-tag.component.html' }) -export class GfAdminTagComponent implements OnInit, OnDestroy { +export class GfAdminTagComponent implements OnDestroy, OnInit { @ViewChild(MatSort) sort: MatSort; public dataSource = new MatTableDataSource(); @@ -149,9 +149,13 @@ export class GfAdminTagComponent implements OnInit, OnDestroy { } private openCreateTagDialog() { - const dialogRef = this.dialog.open(GfCreateOrUpdateTagDialogComponent, { + const dialogRef = this.dialog.open< + GfCreateOrUpdateTagDialogComponent, + CreateOrUpdateTagDialogParams + >(GfCreateOrUpdateTagDialogComponent, { data: { tag: { + id: null, name: null } }, @@ -183,8 +187,11 @@ export class GfAdminTagComponent implements OnInit, OnDestroy { }); } - private openUpdateTagDialog({ id, name }) { - const dialogRef = this.dialog.open(GfCreateOrUpdateTagDialogComponent, { + private openUpdateTagDialog({ id, name }: { id: string; name: string }) { + const dialogRef = this.dialog.open< + GfCreateOrUpdateTagDialogComponent, + CreateOrUpdateTagDialogParams + >(GfCreateOrUpdateTagDialogComponent, { data: { tag: { id, 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 336fb9b22..323609a48 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,6 +1,5 @@ -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 { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { ChangeDetectionStrategy, diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts index bd7214786..4b7f83e93 100644 --- a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts @@ -1,5 +1,5 @@ import { Tag } from '@prisma/client'; export interface CreateOrUpdateTagDialogParams { - tag: Tag; + tag: Pick; } diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 84b82d111..6c366a16c 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -1,11 +1,23 @@ +import { UserDetailDialogParams } from '@ghostfolio/client/components/user-detail-dialog/interfaces/interfaces'; +import { GfUserDetailDialogComponent } from '@ghostfolio/client/components/user-detail-dialog/user-detail-dialog.component'; +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 { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getDateFnsLocale, getDateFormatString, getEmojiFlag } from '@ghostfolio/common/helper'; -import { AdminUsers, InfoItem, User } from '@ghostfolio/common/interfaces'; +import { + AdminUsersResponse, + InfoItem, + User +} from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -19,6 +31,7 @@ import { ViewChild } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; import { MatPaginator, @@ -26,6 +39,7 @@ import { PageEvent } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { ActivatedRoute, Router } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { differenceInSeconds, @@ -37,19 +51,14 @@ import { contractOutline, ellipsisHorizontal, keyOutline, + personOutline, trashOutline } from 'ionicons/icons'; +import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { ConfirmationDialogType } from '../../core/notification/confirmation-dialog/confirmation-dialog.type'; -import { NotificationService } from '../../core/notification/notification.service'; -import { AdminService } from '../../services/admin.service'; -import { DataService } from '../../services/data.service'; -import { ImpersonationStorageService } from '../../services/impersonation-storage.service'; -import { UserService } from '../../services/user/user.service'; - @Component({ imports: [ CommonModule, @@ -69,8 +78,9 @@ import { UserService } from '../../services/user/user.service'; export class GfAdminUsersComponent implements OnDestroy, OnInit { @ViewChild(MatPaginator) paginator: MatPaginator; - public dataSource = new MatTableDataSource(); + public dataSource = new MatTableDataSource(); public defaultDateFormat: string; + public deviceType: string; public displayedColumns: string[] = []; public getEmojiFlag = getEmojiFlag; public hasPermissionForSubscription: boolean; @@ -87,11 +97,16 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { private adminService: AdminService, private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, + private deviceService: DeviceDetectorService, + private dialog: MatDialog, private impersonationStorageService: ImpersonationStorageService, private notificationService: NotificationService, + private route: ActivatedRoute, + private router: Router, private tokenStorageService: TokenStorageService, private userService: UserService ) { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.info = this.dataService.fetchInfo(); this.hasPermissionForSubscription = hasPermission( @@ -121,6 +136,14 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { ]; } + this.route.queryParams + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((params) => { + if (params['userDetailDialog'] && params['userId']) { + this.openUserDetailDialog(params['userId']); + } + }); + this.userService.stateChanged .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((state) => { @@ -138,7 +161,13 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { } }); - addIcons({ contractOutline, ellipsisHorizontal, keyOutline, trashOutline }); + addIcons({ + contractOutline, + ellipsisHorizontal, + keyOutline, + personOutline, + trashOutline + }); } public ngOnInit() { @@ -161,6 +190,12 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { return ''; } + public onChangePage(page: PageEvent) { + this.fetchUsers({ + pageIndex: page.pageIndex + }); + } + public onDeleteUser(aId: string) { this.notificationService.confirm({ confirmFn: () => { @@ -212,9 +247,9 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { window.location.reload(); } - public onChangePage(page: PageEvent) { - this.fetchUsers({ - pageIndex: page.pageIndex + public onOpenUserDetailDialog(userId: string) { + this.router.navigate([], { + queryParams: { userId, userDetailDialog: true } }); } @@ -245,4 +280,28 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { this.changeDetectorRef.markForCheck(); }); } + + private openUserDetailDialog(aUserId: string) { + const dialogRef = this.dialog.open< + GfUserDetailDialogComponent, + UserDetailDialogParams + >(GfUserDetailDialogComponent, { + autoFocus: false, + data: { + deviceType: this.deviceType, + hasPermissionForSubscription: this.hasPermissionForSubscription, + locale: this.user?.settings?.locale, + userId: aUserId + }, + height: this.deviceType === 'mobile' ? '98vh' : '60vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); + + dialogRef + .afterClosed() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.router.navigate(['.'], { relativeTo: this.route }); + }); + } } diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index 4e58abf08..eb63f8aa6 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -216,6 +216,17 @@ + @if (hasPermissionToImpersonateAllUsers) { + } + @if ( + dataSource?.data.length > 0 && + data.hasPermissionToReportDataGlitch === true + ) { + Report Data Glitch... + } @if (data.hasPermissionToAccessAdminControl) { ... } - @if ( - dataSource?.data.length > 0 && - data.hasPermissionToReportDataGlitch === true - ) { - Report Data Glitch... - } diff --git a/apps/client/src/app/components/home-summary/home-summary.component.ts b/apps/client/src/app/components/home-summary/home-summary.component.ts index d49f9c26f..845d1b448 100644 --- a/apps/client/src/app/components/home-summary/home-summary.component.ts +++ b/apps/client/src/app/components/home-summary/home-summary.component.ts @@ -18,6 +18,7 @@ import { } from '@angular/core'; import { MatCardModule } from '@angular/material/card'; import { MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar'; +import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -29,6 +30,7 @@ import { takeUntil } from 'rxjs/operators'; templateUrl: './home-summary.html' }) export class GfHomeSummaryComponent implements OnDestroy, OnInit { + public deviceType: string; public hasImpersonationId: boolean; public hasPermissionForSubscription: boolean; public hasPermissionToUpdateUserSettings: boolean; @@ -43,6 +45,7 @@ export class GfHomeSummaryComponent implements OnDestroy, OnInit { public constructor( private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, + private deviceService: DeviceDetectorService, private impersonationStorageService: ImpersonationStorageService, private userService: UserService ) { @@ -70,6 +73,8 @@ export class GfHomeSummaryComponent implements OnDestroy, OnInit { } public ngOnInit() { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; + this.impersonationStorageService .onChangeHasImpersonation() .pipe(takeUntil(this.unsubscribeSubject)) diff --git a/apps/client/src/app/components/home-summary/home-summary.html b/apps/client/src/app/components/home-summary/home-summary.html index 9434f7018..064923a04 100644 --- a/apps/client/src/app/components/home-summary/home-summary.html +++ b/apps/client/src/app/components/home-summary/home-summary.html @@ -6,6 +6,8 @@ (); diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts index 4c0b614c0..ab43e54dd 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts @@ -149,17 +149,17 @@ export class GfHomeWatchlistComponent implements OnDestroy, OnInit { .subscribe((user) => { this.user = user; - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfCreateWatchlistItemDialogComponent, - { - autoFocus: false, - data: { - deviceType: this.deviceType, - locale: this.user?.settings?.locale - } as CreateWatchlistItemDialogParams, - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + CreateWatchlistItemDialogParams + >(GfCreateWatchlistItemDialogComponent, { + autoFocus: false, + data: { + deviceType: this.deviceType, + locale: this.user?.settings?.locale + }, + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() diff --git a/apps/client/src/app/components/login-with-access-token-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/login-with-access-token-dialog/interfaces/interfaces.ts new file mode 100644 index 000000000..c7c4ab3fd --- /dev/null +++ b/apps/client/src/app/components/login-with-access-token-dialog/interfaces/interfaces.ts @@ -0,0 +1,6 @@ +export interface LoginWithAccessTokenDialogParams { + accessToken: string; + hasPermissionToUseAuthGoogle: boolean; + hasPermissionToUseAuthToken: boolean; + title: string; +} diff --git a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts index 3812a18b9..0e297bc29 100644 --- a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts +++ b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts @@ -1,10 +1,8 @@ -import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; -import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { KEY_STAY_SIGNED_IN, SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; -import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; +import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; @@ -21,11 +19,12 @@ import { } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { Router } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { eyeOffOutline, eyeOutline } from 'ionicons/icons'; +import { LoginWithAccessTokenDialogParams } from './interfaces/interfaces'; + @Component({ changeDetection: ChangeDetectionStrategy.OnPush, imports: [ @@ -51,12 +50,9 @@ export class GfLoginWithAccessTokenDialogComponent { public isAccessTokenHidden = true; public constructor( - @Inject(MAT_DIALOG_DATA) public data: any, + @Inject(MAT_DIALOG_DATA) public data: LoginWithAccessTokenDialogParams, public dialogRef: MatDialogRef, - private internetIdentityService: InternetIdentityService, - private router: Router, - private settingsStorageService: SettingsStorageService, - private tokenStorageService: TokenStorageService + private settingsStorageService: SettingsStorageService ) { addIcons({ eyeOffOutline, eyeOutline }); } @@ -79,14 +75,4 @@ export class GfLoginWithAccessTokenDialogComponent { }); } } - - public async onLoginWithInternetIdentity() { - try { - const { authToken } = await this.internetIdentityService.login(); - - this.tokenStorageService.saveToken(authToken); - this.dialogRef.close(); - this.router.navigate(['/']); - } catch {} - } } diff --git a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html index e19d190c4..bc232cfb7 100644 --- a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html +++ b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -2,54 +2,53 @@
-
- - Security Token - - - + + + } + + @if ( + data.hasPermissionToUseAuthGoogle && data.hasPermissionToUseAuthToken + ) { +
or
+ } + + @if (data.hasPermissionToUseAuthGoogle) { + + }
- @if (data.hasPermissionToUseSocialLogin) { -
or
-
- - Sign in with Google -
- }
+
- + @if (data.hasPermissionToUseAuthToken) { + + }
diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html index b20b6b263..ed9507f89 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html @@ -16,7 +16,7 @@ other {activities} }
@@ -34,6 +34,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.totalBuy" /> @@ -46,6 +47,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.totalSell" /> @@ -61,6 +63,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.committedFunds" /> @@ -73,6 +76,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]=" isLoading ? undefined : summary?.grossPerformanceWithCurrencyEffect @@ -90,6 +94,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.fees" /> @@ -105,6 +110,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]=" isLoading ? undefined : summary?.netPerformanceWithCurrencyEffect @@ -116,7 +122,7 @@
Net Performance (ROAI) @@ -147,13 +153,28 @@ position="end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.currentValueInBaseCurrency" />
-
Emergency Fund
+
+ Emergency Fund + @if ( + !hasImpersonationId && + summary?.totalValueInBaseCurrency > 0 && + user?.settings?.isExperimentalFeatures + ) { + + } +
@@ -194,6 +216,7 @@ position="end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.emergencyFund?.cash" /> @@ -207,30 +230,59 @@ position="end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.emergencyFund?.assets" />
-
Buying Power
-
+
+ Buying Power + @if ( + !hasImpersonationId && + summary?.totalValueInBaseCurrency > 0 && + user?.settings?.isExperimentalFeatures + ) { + + } +
+
-
Excluded from Analysis
-
+
+ Excluded from Analysis + @if ( + !hasImpersonationId && + summary?.totalValueInBaseCurrency > 0 && + user?.settings?.isExperimentalFeatures + ) { + + } +
+
@@ -252,6 +304,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.liabilitiesInBaseCurrency" /> @@ -267,6 +320,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.totalValueInBaseCurrency" /> @@ -301,6 +355,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.interestInBaseCurrency" /> @@ -313,6 +368,7 @@ class="justify-content-end" [isCurrency]="true" [locale]="locale" + [precision]="precision" [unit]="baseCurrency" [value]="isLoading ? undefined : summary?.dividendInBaseCurrency" /> diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts index 849f018ad..7eab9172e 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts @@ -1,4 +1,5 @@ import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; +import { NUMERICAL_PRECISION_THRESHOLD_6_FIGURES } from '@ghostfolio/common/config'; import { getDateFnsLocale, getLocale } from '@ghostfolio/common/helper'; import { PortfolioSummary, User } from '@ghostfolio/common/interfaces'; import { translate } from '@ghostfolio/ui/i18n'; @@ -31,6 +32,8 @@ import { }) export class GfPortfolioSummaryComponent implements OnChanges { @Input() baseCurrency: string; + @Input() deviceType: string; + @Input() hasImpersonationId: boolean; @Input() hasPermissionToUpdateUserSettings: boolean; @Input() isLoading: boolean; @Input() language: string; @@ -43,14 +46,44 @@ export class GfPortfolioSummaryComponent implements OnChanges { public buyAndSellActivitiesTooltip = translate( 'BUY_AND_SELL_ACTIVITIES_TOOLTIP' ); + + public precision = 2; public timeInMarket: string; + public get buyingPowerPercentage() { + return this.summary?.totalValueInBaseCurrency + ? this.summary.cash / this.summary.totalValueInBaseCurrency + : 0; + } + + public get emergencyFundPercentage() { + return this.summary?.totalValueInBaseCurrency + ? (this.summary.emergencyFund?.total || 0) / + this.summary.totalValueInBaseCurrency + : 0; + } + + public get excludedFromAnalysisPercentage() { + return this.summary?.totalValueInBaseCurrency + ? this.summary.excludedAccountsAndActivities / + this.summary.totalValueInBaseCurrency + : 0; + } + public constructor(private notificationService: NotificationService) { addIcons({ ellipsisHorizontalCircleOutline, informationCircleOutline }); } public ngOnChanges() { if (this.summary) { + if ( + this.deviceType === 'mobile' && + this.summary.totalValueInBaseCurrency >= + NUMERICAL_PRECISION_THRESHOLD_6_FIGURES + ) { + this.precision = 0; + } + if (this.user.dateOfFirstActivity) { this.timeInMarket = formatDistanceToNow(this.user.dateOfFirstActivity, { locale: getDateFnsLocale(this.language) diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts index 51c2b8951..90a0039cf 100644 --- a/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts @@ -3,7 +3,7 @@ import { XRayRulesSettings } from '@ghostfolio/common/interfaces'; -export interface IRuleSettingsDialogParams { +export interface RuleSettingsDialogParams { categoryName: string; locale: string; rule: PortfolioReportRule; 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 f8ce13e0d..65300c6d8 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 @@ -12,7 +12,7 @@ import { } from '@angular/material/dialog'; import { MatSliderModule } from '@angular/material/slider'; -import { IRuleSettingsDialogParams } from './interfaces/interfaces'; +import { RuleSettingsDialogParams } from './interfaces/interfaces'; @Component({ imports: [ @@ -31,7 +31,7 @@ export class GfRuleSettingsDialogComponent { public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment']; public constructor( - @Inject(MAT_DIALOG_DATA) public data: IRuleSettingsDialogParams, + @Inject(MAT_DIALOG_DATA) public data: RuleSettingsDialogParams, public dialogRef: MatDialogRef ) {} } diff --git a/apps/client/src/app/components/rule/rule.component.ts b/apps/client/src/app/components/rule/rule.component.ts index c38de8bbb..e2ffc1cf6 100644 --- a/apps/client/src/app/components/rule/rule.component.ts +++ b/apps/client/src/app/components/rule/rule.component.ts @@ -1,7 +1,7 @@ -import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; -import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; +import { UpdateUserSettingDto } from '@ghostfolio/common/dtos'; import { PortfolioReportRule, + RuleSettings, XRayRulesSettings } from '@ghostfolio/common/interfaces'; @@ -31,7 +31,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { Subject, takeUntil } from 'rxjs'; -import { IRuleSettingsDialogParams } from './rule-settings-dialog/interfaces/interfaces'; +import { RuleSettingsDialogParams } from './rule-settings-dialog/interfaces/interfaces'; import { GfRuleSettingsDialogComponent } from './rule-settings-dialog/rule-settings-dialog.component'; @Component({ @@ -51,6 +51,7 @@ export class GfRuleComponent implements OnInit { @Input() categoryName: string; @Input() hasPermissionToUpdateUserSettings: boolean; @Input() isLoading: boolean; + @Input() locale: string; @Input() rule: PortfolioReportRule; @Input() settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment']; @@ -78,12 +79,16 @@ export class GfRuleComponent implements OnInit { } public onCustomizeRule(rule: PortfolioReportRule) { - const dialogRef = this.dialog.open(GfRuleSettingsDialogComponent, { + const dialogRef = this.dialog.open< + GfRuleSettingsDialogComponent, + RuleSettingsDialogParams + >(GfRuleSettingsDialogComponent, { data: { rule, categoryName: this.categoryName, + locale: this.locale, settings: this.settings - } as IRuleSettingsDialogParams, + }, width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); diff --git a/apps/client/src/app/components/rules/rules.component.html b/apps/client/src/app/components/rules/rules.component.html index d0cf7ece5..0c3153c52 100644 --- a/apps/client/src/app/components/rules/rules.component.html +++ b/apps/client/src/app/components/rules/rules.component.html @@ -12,6 +12,7 @@ [hasPermissionToUpdateUserSettings]=" hasPermissionToUpdateUserSettings " + [locale]="locale" [rule]="rule" [settings]="settings?.[rule.key]" (ruleUpdated)="onRuleUpdated($event)" diff --git a/apps/client/src/app/components/rules/rules.component.ts b/apps/client/src/app/components/rules/rules.component.ts index 6379a40fb..22e1718f8 100644 --- a/apps/client/src/app/components/rules/rules.component.ts +++ b/apps/client/src/app/components/rules/rules.component.ts @@ -1,5 +1,5 @@ -import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; import { GfRuleComponent } from '@ghostfolio/client/components/rule/rule.component'; +import { UpdateUserSettingDto } from '@ghostfolio/common/dtos'; import { PortfolioReportRule, XRayRulesSettings @@ -26,6 +26,7 @@ export class GfRulesComponent { @Input() categoryName: string; @Input() hasPermissionToUpdateUserSettings: boolean; @Input() isLoading: boolean; + @Input() locale: string; @Input() rules: PortfolioReportRule[]; @Input() settings: XRayRulesSettings; diff --git a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts index 315f86244..9aa07feee 100644 --- a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts +++ b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts @@ -1,8 +1,7 @@ -import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto'; -import { UpdateAccessDto } from '@ghostfolio/api/app/access/update-access.dto'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { DataService } from '@ghostfolio/client/services/data.service'; -import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; +import { CreateAccessDto, UpdateAccessDto } from '@ghostfolio/common/dtos'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { ChangeDetectionStrategy, diff --git a/apps/client/src/app/components/user-account-access/user-account-access.component.ts b/apps/client/src/app/components/user-account-access/user-account-access.component.ts index bdb9af6ed..da2e8f508 100644 --- a/apps/client/src/app/components/user-account-access/user-account-access.component.ts +++ b/apps/client/src/app/components/user-account-access/user-account-access.component.ts @@ -1,10 +1,10 @@ -import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto'; import { GfAccessTableComponent } from '@ghostfolio/client/components/access-table/access-table.component'; -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { CreateAccessDto } from '@ghostfolio/common/dtos'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { Access, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; @@ -31,6 +31,7 @@ import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; import { GfCreateOrUpdateAccessDialogComponent } from './create-or-update-access-dialog/create-or-update-access-dialog.component'; +import { CreateOrUpdateAccessDialogParams } from './create-or-update-access-dialog/interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -187,10 +188,15 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit { } private openCreateAccessDialog() { - const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialogComponent, { + const dialogRef = this.dialog.open< + GfCreateOrUpdateAccessDialogComponent, + CreateOrUpdateAccessDialogParams + >(GfCreateOrUpdateAccessDialogComponent, { data: { access: { alias: '', + grantee: null, + id: null, permissions: ['READ_RESTRICTED'], type: 'PRIVATE' } @@ -219,12 +225,15 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit { return; } - const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialogComponent, { + const dialogRef = this.dialog.open< + GfCreateOrUpdateAccessDialogComponent, + CreateOrUpdateAccessDialogParams + >(GfCreateOrUpdateAccessDialogComponent, { data: { access: { alias: access.alias, - id: access.id, grantee: access.grantee === 'Public' ? null : access.grantee, + id: access.id, permissions: access.permissions, type: access.type } diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts index f2f63b32b..ae9183c13 100644 --- a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts +++ b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts @@ -1,7 +1,7 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -108,7 +108,10 @@ export class GfUserAccountMembershipComponent implements OnDestroy { public onCheckout() { this.dataService - .createCheckoutSession({ couponId: this.couponId, priceId: this.priceId }) + .createStripeCheckoutSession({ + couponId: this.couponId, + priceId: this.priceId + }) .pipe( catchError((error) => { this.notificationService.alert({ @@ -117,7 +120,7 @@ export class GfUserAccountMembershipComponent implements OnDestroy { throw error; }), - switchMap(({ sessionId }: { sessionId: string }) => { + switchMap(({ sessionId }) => { return this.stripeService.redirectToCheckout({ sessionId }); }) ) diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.html b/apps/client/src/app/components/user-account-membership/user-account-membership.html index eadf85612..351d5608a 100644 --- a/apps/client/src/app/components/user-account-membership/user-account-membership.html +++ b/apps/client/src/app/components/user-account-membership/user-account-membership.html @@ -37,8 +37,11 @@
- Limited Offer! Get - {{ durationExtension }} extra + Limited Offer! +   + Get {{ durationExtension }} extra
} @@ -67,7 +70,7 @@
} @else {
- No auto-renewal. + No auto-renewal on membership.
}
diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts index a36ff3229..32e3d132e 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1,4 +1,3 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { @@ -9,6 +8,7 @@ import { import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { downloadAsFile } from '@ghostfolio/common/helper'; import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; diff --git a/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts new file mode 100644 index 000000000..b922e7a54 --- /dev/null +++ b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts @@ -0,0 +1,6 @@ +export interface UserDetailDialogParams { + deviceType: string; + hasPermissionForSubscription: boolean; + locale: string; + userId: string; +} diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.scss b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.scss new file mode 100644 index 000000000..b63df0134 --- /dev/null +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.scss @@ -0,0 +1,7 @@ +:host { + display: block; + + .mat-mdc-dialog-content { + max-height: unset; + } +} diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts new file mode 100644 index 000000000..57ccf0f18 --- /dev/null +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts @@ -0,0 +1,79 @@ +import { AdminService } from '@ghostfolio/client/services/admin.service'; +import { AdminUserResponse } from '@ghostfolio/common/interfaces'; +import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; +import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; +import { GfValueComponent } from '@ghostfolio/ui/value'; + +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + CUSTOM_ELEMENTS_SCHEMA, + Inject, + OnDestroy, + OnInit +} from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { MatDialogModule } from '@angular/material/dialog'; +import { EMPTY, Subject } from 'rxjs'; +import { catchError, takeUntil } from 'rxjs/operators'; + +import { UserDetailDialogParams } from './interfaces/interfaces'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'd-flex flex-column h-100' }, + imports: [ + CommonModule, + GfDialogFooterComponent, + GfDialogHeaderComponent, + GfValueComponent, + MatButtonModule, + MatDialogModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + selector: 'gf-user-detail-dialog', + styleUrls: ['./user-detail-dialog.component.scss'], + templateUrl: './user-detail-dialog.html' +}) +export class GfUserDetailDialogComponent implements OnDestroy, OnInit { + public user: AdminUserResponse; + + private unsubscribeSubject = new Subject(); + + public constructor( + private adminService: AdminService, + private changeDetectorRef: ChangeDetectorRef, + @Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams, + public dialogRef: MatDialogRef + ) {} + + public ngOnInit() { + this.adminService + .fetchUserById(this.data.userId) + .pipe( + takeUntil(this.unsubscribeSubject), + catchError(() => { + this.dialogRef.close(); + + return EMPTY; + }) + ) + .subscribe((user) => { + this.user = user; + + this.changeDetectorRef.markForCheck(); + }); + } + + public onClose() { + this.dialogRef.close(); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } +} diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html new file mode 100644 index 000000000..60f6a2585 --- /dev/null +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html @@ -0,0 +1,109 @@ + + +
+
+
+
+ User ID +
+
+ Role +
+
+ +
+
+ Registration Date +
+
+ Authentication +
+
+ + @if (data.hasPermissionForSubscription) { +
+
+ Membership +
+
+ Country +
+
+ } + +
+
+ Accounts +
+
+ Activities +
+
+ + @if (data.hasPermissionForSubscription) { +
+
+ Engagement per Day +
+
+ API Requests Today +
+
+ } +
+
+ + diff --git a/apps/client/src/app/core/module-preload.service.ts b/apps/client/src/app/core/module-preload.service.ts index fcba48c52..85d9c5e33 100644 --- a/apps/client/src/app/core/module-preload.service.ts +++ b/apps/client/src/app/core/module-preload.service.ts @@ -7,7 +7,7 @@ export class ModulePreloadService implements PreloadingStrategy { /** * Preloads all lazy loading modules with the attribute 'preload' set to true */ - preload(route: Route, load: Function): Observable { + preload(route: Route, load: () => Observable): Observable { return route.data?.preload ? load() : of(null); } } 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 98b6043eb..33d26c493 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 @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { IAlertDialogParams } from './interfaces/interfaces'; +import { AlertDialogParams } from './interfaces/interfaces'; @Component({ imports: [MatButtonModule, MatDialogModule], @@ -17,7 +17,7 @@ export class GfAlertDialogComponent { public constructor(public dialogRef: MatDialogRef) {} - public initialize(aParams: IAlertDialogParams) { + public initialize(aParams: AlertDialogParams) { this.discardLabel = aParams.discardLabel; this.message = aParams.message; this.title = aParams.title; diff --git a/apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts b/apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts index 7cff077a7..835056ba7 100644 --- a/apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/core/notification/alert-dialog/interfaces/interfaces.ts @@ -1,4 +1,4 @@ -export interface IAlertDialogParams { +export interface AlertDialogParams { confirmLabel?: string; discardLabel?: string; message?: string; 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 88e5113d7..a3bc053ee 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,9 +1,10 @@ +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; + import { Component, HostListener } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { ConfirmationDialogType } from './confirmation-dialog.type'; -import { IConfirmDialogParams } from './interfaces/interfaces'; +import { ConfirmDialogParams } from './interfaces/interfaces'; @Component({ imports: [MatButtonModule, MatDialogModule], @@ -29,7 +30,7 @@ export class GfConfirmationDialogComponent { } } - public initialize(aParams: IConfirmDialogParams) { + public initialize(aParams: ConfirmDialogParams) { this.confirmLabel = aParams.confirmLabel; this.confirmType = aParams.confirmType; this.discardLabel = aParams.discardLabel; diff --git a/apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts b/apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts index 834988ceb..449201a76 100644 --- a/apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts @@ -1,6 +1,6 @@ -import { ConfirmationDialogType } from '../confirmation-dialog.type'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; -export interface IConfirmDialogParams { +export interface ConfirmDialogParams { confirmLabel?: string; confirmType: ConfirmationDialogType; discardLabel?: string; diff --git a/apps/client/src/app/core/notification/interfaces/interfaces.ts b/apps/client/src/app/core/notification/interfaces/interfaces.ts index f3546d457..071597691 100644 --- a/apps/client/src/app/core/notification/interfaces/interfaces.ts +++ b/apps/client/src/app/core/notification/interfaces/interfaces.ts @@ -1,13 +1,13 @@ -import { ConfirmationDialogType } from '../confirmation-dialog/confirmation-dialog.type'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; -export interface IAlertParams { +export interface AlertParams { discardFn?: () => void; discardLabel?: string; message?: string; title: string; } -export interface IConfirmParams { +export interface ConfirmParams { confirmFn: () => void; confirmLabel?: string; confirmType?: ConfirmationDialogType; @@ -18,7 +18,7 @@ export interface IConfirmParams { title: string; } -export interface IPromptParams { +export interface PromptParams { confirmFn: (value: string) => void; confirmLabel?: string; defaultValue?: string; diff --git a/apps/client/src/app/core/notification/notification.service.ts b/apps/client/src/app/core/notification/notification.service.ts index 1b58db64a..849f91288 100644 --- a/apps/client/src/app/core/notification/notification.service.ts +++ b/apps/client/src/app/core/notification/notification.service.ts @@ -1,3 +1,4 @@ +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { translate } from '@ghostfolio/ui/i18n'; import { Injectable } from '@angular/core'; @@ -6,11 +7,10 @@ import { isFunction } from 'lodash'; import { GfAlertDialogComponent } from './alert-dialog/alert-dialog.component'; import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component'; -import { ConfirmationDialogType } from './confirmation-dialog/confirmation-dialog.type'; import { - IAlertParams, - IConfirmParams, - IPromptParams + AlertParams, + ConfirmParams, + PromptParams } from './interfaces/interfaces'; import { GfPromptDialogComponent } from './prompt-dialog/prompt-dialog.component'; @@ -21,7 +21,7 @@ export class NotificationService { public constructor(private matDialog: MatDialog) {} - public alert(aParams: IAlertParams) { + public alert(aParams: AlertParams) { if (!aParams.discardLabel) { aParams.discardLabel = translate('CLOSE'); } @@ -45,7 +45,7 @@ export class NotificationService { }); } - public confirm(aParams: IConfirmParams) { + public confirm(aParams: ConfirmParams) { if (!aParams.confirmLabel) { aParams.confirmLabel = translate('YES'); } @@ -78,7 +78,7 @@ export class NotificationService { }); } - public prompt(aParams: IPromptParams) { + public prompt(aParams: PromptParams) { if (!aParams.confirmLabel) { aParams.confirmLabel = translate('OK'); } 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 4085498a9..185becba7 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 @@ -175,7 +175,7 @@
-
+
}
+ + @if (user?.subscription?.type !== 'Premium') { +
+
+

Sponsors

+
+ Browser testing via +
+ + LambdaTest Logo + +
+
+
+ }
diff --git a/apps/client/src/app/pages/accounts/accounts-page.component.ts b/apps/client/src/app/pages/accounts/accounts-page.component.ts index f09901e45..2bb6457a5 100644 --- a/apps/client/src/app/pages/accounts/accounts-page.component.ts +++ b/apps/client/src/app/pages/accounts/accounts-page.component.ts @@ -1,12 +1,14 @@ -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 { GfAccountDetailDialogComponent } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component'; import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { + CreateAccountDto, + TransferBalanceDto, + UpdateAccountDto +} from '@ghostfolio/common/dtos'; import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfAccountsTableComponent } from '@ghostfolio/ui/accounts-table'; @@ -23,6 +25,8 @@ import { EMPTY, Subject, Subscription } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; import { GfCreateOrUpdateAccountDialogComponent } from './create-or-update-account-dialog/create-or-update-account-dialog.component'; +import { CreateOrUpdateAccountDialogParams } from './create-or-update-account-dialog/interfaces/interfaces'; +import { TransferBalanceDialogParams } from './transfer-balance/interfaces/interfaces'; import { GfTransferBalanceDialogComponent } from './transfer-balance/transfer-balance-dialog.component'; @Component({ @@ -179,7 +183,10 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit { name, platformId }: AccountModel) { - const dialogRef = this.dialog.open(GfCreateOrUpdateAccountDialogComponent, { + const dialogRef = this.dialog.open< + GfCreateOrUpdateAccountDialogComponent, + CreateOrUpdateAccountDialogParams + >(GfCreateOrUpdateAccountDialogComponent, { data: { account: { balance, @@ -227,7 +234,10 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit { } private openAccountDetailDialog(aAccountId: string) { - const dialogRef = this.dialog.open(GfAccountDetailDialogComponent, { + const dialogRef = this.dialog.open< + GfAccountDetailDialogComponent, + AccountDetailDialogParams + >(GfAccountDetailDialogComponent, { autoFocus: false, data: { accountId: aAccountId, @@ -237,7 +247,7 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit { !this.hasImpersonationId && hasPermission(this.user?.permissions, permissions.createOrder) && !this.user?.settings?.isRestrictedView - } as AccountDetailDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : '80vh', width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); @@ -253,12 +263,16 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit { } private openCreateAccountDialog() { - const dialogRef = this.dialog.open(GfCreateOrUpdateAccountDialogComponent, { + const dialogRef = this.dialog.open< + GfCreateOrUpdateAccountDialogComponent, + CreateOrUpdateAccountDialogParams + >(GfCreateOrUpdateAccountDialogComponent, { data: { account: { balance: 0, comment: null, currency: this.user?.settings?.baseCurrency, + id: null, isExcluded: false, name: null, platformId: null @@ -295,7 +309,10 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit { } private openTransferBalanceDialog() { - const dialogRef = this.dialog.open(GfTransferBalanceDialogComponent, { + const dialogRef = this.dialog.open< + GfTransferBalanceDialogComponent, + TransferBalanceDialogParams + >(GfTransferBalanceDialogComponent, { data: { accounts: this.accounts }, diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts index beb815e0c..5e18f25cf 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts @@ -1,7 +1,6 @@ -import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; -import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; import { DataService } from '@ghostfolio/client/services/data.service'; -import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; +import { CreateAccountDto, UpdateAccountDto } from '@ghostfolio/common/dtos'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts index ffe4f14f6..a3e6272f8 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts @@ -1,5 +1,5 @@ import { Account } from '@prisma/client'; export interface CreateOrUpdateAccountDialogParams { - account: Account; + account: Omit; } diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts index 368c7f2f0..85d2e60bf 100644 --- a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts @@ -1,4 +1,4 @@ -import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto'; +import { TransferBalanceDto } from '@ghostfolio/common/dtos'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { diff --git a/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.component.ts b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.component.ts new file mode 100644 index 000000000..c5947abf4 --- /dev/null +++ b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.component.ts @@ -0,0 +1,18 @@ +import { publicRoutes } from '@ghostfolio/common/routes/routes'; +import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; + +import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { RouterModule } from '@angular/router'; + +@Component({ + host: { class: 'page' }, + imports: [GfPremiumIndicatorComponent, MatButtonModule, RouterModule], + selector: 'gf-black-weeks-2025-page', + templateUrl: './black-weeks-2025-page.html' +}) +export class BlackWeeks2025PageComponent { + public routerLinkBlog = publicRoutes.blog.routerLink; + public routerLinkFeatures = publicRoutes.features.routerLink; + public routerLinkPricing = publicRoutes.pricing.routerLink; +} diff --git a/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html new file mode 100644 index 000000000..c92616d66 --- /dev/null +++ b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html @@ -0,0 +1,159 @@ +
+
+
+
+
+

Black Weeks 2025

+
2025-11-16
+ Black Week 2025 Teaser +
+
+

+ Save 25% on the + Ghostfolio Premium + + + annual plan and get 3 extra months on top with our + exclusive Black Weeks offer. +

+
+
+

+ Ghostfolio + unifies your finances in one place and gives you a clear overview of + your portfolio across stocks, ETFs, cryptocurrencies or other + assets. Real time analytics and smart evaluations help you + understand your financial situation quickly and make confident + decisions. +

+
+
+

+ Grab this limited Black Weeks deal to optimize your financial + future. +

+

+ Get the offer +

+

+ More details are available on the + pricing page. +

+
+
+
    +
  • + 2025 +
  • +
  • + Black Friday +
  • +
  • + Black Weeks +
  • +
  • + Cryptocurrency +
  • +
  • + Deal +
  • +
  • + Discount +
  • +
  • + ETF +
  • +
  • + Finance +
  • +
  • + Fintech +
  • +
  • + Ghostfolio +
  • +
  • + Ghostfolio Premium +
  • +
  • + Investment +
  • +
  • + Open Source +
  • +
  • + OSS +
  • +
  • + Personal Finance +
  • +
  • + Portfolio +
  • +
  • + Portfolio Tracker +
  • +
  • + Pricing +
  • +
  • + Promotion +
  • +
  • + SaaS +
  • +
  • + Sale +
  • +
  • + Savings +
  • +
  • + Software +
  • +
  • + Stock +
  • +
  • + Subscription +
  • +
  • + Wealth +
  • +
  • + Wealth Management +
  • +
+
+ +
+
+
+
diff --git a/apps/client/src/app/pages/blog/blog-page.html b/apps/client/src/app/pages/blog/blog-page.html index 88b685d33..e84cb303d 100644 --- a/apps/client/src/app/pages/blog/blog-page.html +++ b/apps/client/src/app/pages/blog/blog-page.html @@ -8,6 +8,32 @@ finance + @if (hasPermissionForSubscription) { + + + + + + }
diff --git a/apps/client/src/app/pages/blog/blog-page.routes.ts b/apps/client/src/app/pages/blog/blog-page.routes.ts index 2b5a4be64..36d111c19 100644 --- a/apps/client/src/app/pages/blog/blog-page.routes.ts +++ b/apps/client/src/app/pages/blog/blog-page.routes.ts @@ -209,5 +209,14 @@ export const routes: Routes = [ './2025/09/hacktoberfest-2025/hacktoberfest-2025-page.component' ).then((c) => c.Hacktoberfest2025PageComponent), title: 'Hacktoberfest 2025' + }, + { + canActivate: [AuthGuard], + path: '2025/11/black-weeks-2025', + loadComponent: () => + import('./2025/11/black-weeks-2025/black-weeks-2025-page.component').then( + (c) => c.BlackWeeks2025PageComponent + ), + title: 'Black Weeks 2025' } ]; 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 bc468fe96..f44759124 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 @@ -184,7 +184,9 @@
  • Open the Admin Control panel
  • Navigate to the Market Data section
  • Choose an asset profile
  • -
  • In the dialog, check the Benchmark box
  • +
  • + In the dialog, check the Include in Benchmark / Markets box +
  • @@ -212,12 +214,13 @@ How do I set up Markets? -

    The Markets list is derived from your Benchmarks.

    1. Open the Admin Control panel
    2. Navigate to the Market Data section
    3. Choose an asset profile
    4. -
    5. In the dialog, check the Benchmark box
    6. +
    7. + In the dialog, check the Include in Benchmark / Markets box +

    Please note: Data is cached, meaning changes may take a few minutes diff --git a/apps/client/src/app/pages/features/features-page.component.ts b/apps/client/src/app/pages/features/features-page.component.ts index dc9d30f07..dc2dfaf42 100644 --- a/apps/client/src/app/pages/features/features-page.component.ts +++ b/apps/client/src/app/pages/features/features-page.component.ts @@ -25,6 +25,7 @@ import { Subject, takeUntil } from 'rxjs'; }) export class GfFeaturesPageComponent implements OnDestroy { public hasPermissionForSubscription: boolean; + public hasPermissionToCreateUser: boolean; public info: InfoItem; public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkResources = publicRoutes.resources.routerLink; @@ -55,6 +56,11 @@ export class GfFeaturesPageComponent implements OnDestroy { this.info?.globalPermissions, permissions.enableSubscription ); + + this.hasPermissionToCreateUser = hasPermission( + this.info?.globalPermissions, + permissions.createUserAccount + ); } public ngOnDestroy() { diff --git a/apps/client/src/app/pages/features/features-page.html b/apps/client/src/app/pages/features/features-page.html index 7d8f3eda0..d172347f7 100644 --- a/apps/client/src/app/pages/features/features-page.html +++ b/apps/client/src/app/pages/features/features-page.html @@ -309,7 +309,7 @@

    - @if (!user) { + @if (hasPermissionToCreateUser && !user) {
    Get Started - Get Started - } @if (hasPermissionForDemo) { @if (hasPermissionToCreateUser) { @@ -342,9 +341,8 @@ i18n mat-flat-button [routerLink]="routerLinkRegister" + >Get Started - Get Started - @if (hasPermissionForDemo) {
    or
    (GfImportActivitiesDialogComponent, { data: { deviceType: this.deviceType, user: this.user - } as ImportActivitiesDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : undefined, width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); @@ -268,12 +274,15 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public onImportDividends() { - const dialogRef = this.dialog.open(GfImportActivitiesDialogComponent, { + const dialogRef = this.dialog.open< + GfImportActivitiesDialogComponent, + ImportActivitiesDialogParams + >(GfImportActivitiesDialogComponent, { data: { activityTypes: ['DIVIDEND'], deviceType: this.deviceType, user: this.user - } as ImportActivitiesDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : undefined, width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); @@ -306,18 +315,18 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public openUpdateActivityDialog(aActivity: Activity) { - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfCreateOrUpdateActivityDialogComponent, - { - data: { - activity: aActivity, - accounts: this.user?.accounts, - user: this.user - }, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + CreateOrUpdateActivityDialogParams + >(GfCreateOrUpdateActivityDialogComponent, { + data: { + activity: aActivity, + accounts: this.user?.accounts, + user: this.user + }, + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() @@ -350,26 +359,26 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { .subscribe((user) => { this.updateUser(user); - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfCreateOrUpdateActivityDialogComponent, - { - data: { - accounts: this.user?.accounts, - activity: { - ...aActivity, - accountId: aActivity?.accountId, - date: new Date(), - id: null, - fee: 0, - type: aActivity?.type ?? 'BUY', - unitPrice: null - }, - user: this.user + CreateOrUpdateActivityDialogParams + >(GfCreateOrUpdateActivityDialogComponent, { + data: { + accounts: this.user?.accounts, + activity: { + ...aActivity, + accountId: aActivity?.accountId, + date: new Date(), + id: null, + fee: 0, + type: aActivity?.type ?? 'BUY', + unitPrice: null }, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + user: this.user + }, + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 3261e9752..01b389789 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -1,13 +1,13 @@ -import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; -import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { ASSET_CLASS_MAPPING } from '@ghostfolio/common/config'; +import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { AssetClassSelectorOption, LookupItem } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { translate } from '@ghostfolio/ui/i18n'; import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete'; @@ -49,7 +49,6 @@ import { EMPTY, Subject } from 'rxjs'; import { catchError, delay, takeUntil } from 'rxjs/operators'; import { DataService } from '../../../../services/data.service'; -import { validateObjectForForm } from '../../../../util/form.util'; import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces'; import { ActivityType } from './types/activity-type.type'; diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts index 60a39d361..5206aacf9 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts @@ -1,10 +1,8 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; -import { User } from '@ghostfolio/common/interfaces'; +import { Activity, User } from '@ghostfolio/common/interfaces'; import { Account } from '@prisma/client'; export interface CreateOrUpdateActivityDialogParams { - accountId: string; accounts: Account[]; activity: Activity; user: User; diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 0c0054e9b..582ab8e25 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -1,15 +1,16 @@ -import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; -import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; -import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component'; -import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive'; -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service'; -import { PortfolioPosition } from '@ghostfolio/common/interfaces'; +import { + CreateAccountWithBalancesDto, + CreateAssetProfileWithMarketDataDto, + CreateTagDto +} from '@ghostfolio/common/dtos'; +import { Activity, PortfolioPosition } from '@ghostfolio/common/interfaces'; +import { GfSymbolPipe } from '@ghostfolio/common/pipes'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; +import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; +import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { StepperOrientation, diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts index a2131db88..051345e60 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts @@ -3,7 +3,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { Type } from '@prisma/client'; export interface ImportActivitiesDialogParams { - activityTypes: Type[]; + activityTypes?: Type[]; deviceType: string; user: User; } 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 da909a78d..b4de51701 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 @@ -558,7 +558,10 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit { } private openAccountDetailDialog(aAccountId: string) { - const dialogRef = this.dialog.open(GfAccountDetailDialogComponent, { + const dialogRef = this.dialog.open< + GfAccountDetailDialogComponent, + AccountDetailDialogParams + >(GfAccountDetailDialogComponent, { autoFocus: false, data: { accountId: aAccountId, @@ -568,7 +571,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit { !this.hasImpersonationId && hasPermission(this.user?.permissions, permissions.createOrder) && !this.user?.settings?.isRestrictedView - } as AccountDetailDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : '80vh', width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.scss b/apps/client/src/app/pages/portfolio/fire/fire-page.scss index 2892885c9..3a0618ed6 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.scss +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.scss @@ -1,9 +1,21 @@ +@use '../../../../styles/variables.scss' as variables; + +@mixin select-arrow($color) { + background-image: url("data:image/svg+xml;utf8,"); +} + :host { display: block; .safe-withdrawal-rate-select { + @include select-arrow(variables.$dark-primary-text); + + appearance: none; background-color: transparent; + background-position: right 0 center; + background-repeat: no-repeat; color: rgb(var(--dark-primary-text)); + padding: 0 0.75rem 0 0.25rem; &:focus { box-shadow: none; @@ -14,6 +26,8 @@ :host-context(.theme-dark) { .safe-withdrawal-rate-select { + @include select-arrow(variables.$light-primary-text); + color: rgb(var(--light-primary-text)); } } 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 d4820b59e..af74137d1 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 @@ -76,6 +76,7 @@ !hasImpersonationId && hasPermissionToUpdateUserSettings " [isLoading]="isLoading" + [locale]="user?.settings?.locale" [rules]="category.rules" [settings]="user?.settings?.xRayRules" (rulesUpdated)="onRulesUpdated($event)" @@ -90,6 +91,7 @@ !hasImpersonationId && hasPermissionToUpdateUserSettings " [isLoading]="isLoading" + [locale]="user?.settings?.locale" [rules]="inactiveRules" [settings]="user?.settings?.xRayRules" (rulesUpdated)="onRulesUpdated($event)" diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts index 364564383..0bf869238 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts @@ -1,8 +1,8 @@ -import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; import { GfRulesComponent } from '@ghostfolio/client/components/rules/rules.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { UpdateUserSettingDto } from '@ghostfolio/common/dtos'; import { PortfolioReportResponse, PortfolioReportRule diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts index 8bc3e3a67..3d8170b06 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.component.ts +++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts @@ -52,6 +52,7 @@ export class GfPricingPageComponent implements OnDestroy, OnInit { public coupon: number; public couponId: string; public durationExtension: StringValue; + public hasPermissionToCreateUser: boolean; public hasPermissionToUpdateUserSettings: boolean; public importAndExportTooltipBasic = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC' @@ -100,11 +101,18 @@ export class GfPricingPageComponent implements OnDestroy, OnInit { } public ngOnInit() { - const { baseCurrency, subscriptionOffer } = this.dataService.fetchInfo(); - this.baseCurrency = baseCurrency; + const { baseCurrency, globalPermissions, subscriptionOffer } = + this.dataService.fetchInfo(); + this.baseCurrency = baseCurrency; this.coupon = subscriptionOffer?.coupon; this.durationExtension = subscriptionOffer?.durationExtension; + + this.hasPermissionToCreateUser = hasPermission( + globalPermissions, + permissions.createUserAccount + ); + this.label = subscriptionOffer?.label; this.price = subscriptionOffer?.price; @@ -134,9 +142,12 @@ export class GfPricingPageComponent implements OnDestroy, OnInit { public onCheckout() { this.dataService - .createCheckoutSession({ couponId: this.couponId, priceId: this.priceId }) + .createStripeCheckoutSession({ + couponId: this.couponId, + priceId: this.priceId + }) .pipe( - switchMap(({ sessionId }: { sessionId: string }) => { + switchMap(({ sessionId }) => { return this.stripeService.redirectToCheckout({ sessionId }); }), catchError((error) => { diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html index ee006b2d6..3cc0e460a 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.html +++ b/apps/client/src/app/pages/pricing/pricing-page.html @@ -27,6 +27,9 @@ own infrastructure.

    - } @else if (!user) { + } @else if (hasPermissionToCreateUser && !user) {
    Get Started - Get Started -

    It’s free.

    diff --git a/apps/client/src/app/pages/pricing/pricing-page.scss b/apps/client/src/app/pages/pricing/pricing-page.scss index c92ca364f..64d4bba80 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.scss +++ b/apps/client/src/app/pages/pricing/pricing-page.scss @@ -14,6 +14,10 @@ } .mat-mdc-card { + transition: + border-color 0.5s ease, + box-shadow 0.5s ease; + &:hover, &.active { border-color: rgba(var(--palette-primary-500), 1); diff --git a/apps/client/src/app/pages/register/register-page.component.ts b/apps/client/src/app/pages/register/register-page.component.ts index eff4e308b..d37a91f8e 100644 --- a/apps/client/src/app/pages/register/register-page.component.ts +++ b/apps/client/src/app/pages/register/register-page.component.ts @@ -1,5 +1,4 @@ import { DataService } from '@ghostfolio/client/services/data.service'; -import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { InfoItem, LineChartItem } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -31,7 +30,8 @@ import { GfUserAccountRegistrationDialogComponent } from './user-account-registr }) export class GfRegisterPageComponent implements OnDestroy, OnInit { public deviceType: string; - public hasPermissionForSocialLogin: boolean; + public hasPermissionForAuthGoogle: boolean; + public hasPermissionForAuthToken: boolean; public hasPermissionForSubscription: boolean; public hasPermissionToCreateUser: boolean; public historicalDataItems: LineChartItem[]; @@ -43,7 +43,6 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit { private dataService: DataService, private deviceService: DeviceDetectorService, private dialog: MatDialog, - private internetIdentityService: InternetIdentityService, private router: Router, private tokenStorageService: TokenStorageService ) { @@ -57,9 +56,14 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit { this.deviceType = this.deviceService.getDeviceInfo().deviceType; - this.hasPermissionForSocialLogin = hasPermission( + this.hasPermissionForAuthGoogle = hasPermission( globalPermissions, - permissions.enableSocialLogin + permissions.enableAuthGoogle + ); + + this.hasPermissionForAuthToken = hasPermission( + globalPermissions, + permissions.enableAuthToken ); this.hasPermissionForSubscription = hasPermission( @@ -73,29 +77,19 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit { ); } - public async onLoginWithInternetIdentity() { - try { - const { authToken } = await this.internetIdentityService.login(); - - this.tokenStorageService.saveToken(authToken); - - await this.router.navigate(['/']); - } catch {} - } - public openShowAccessTokenDialog() { - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfUserAccountRegistrationDialogComponent, - { - data: { - deviceType: this.deviceType, - needsToAcceptTermsOfService: this.hasPermissionForSubscription - } as UserAccountRegistrationDialogParams, - disableClose: true, - height: this.deviceType === 'mobile' ? '98vh' : undefined, - width: this.deviceType === 'mobile' ? '100vw' : '30rem' - } - ); + UserAccountRegistrationDialogParams + >(GfUserAccountRegistrationDialogComponent, { + data: { + deviceType: this.deviceType, + needsToAcceptTermsOfService: this.hasPermissionForSubscription + }, + disableClose: true, + height: this.deviceType === 'mobile' ? '98vh' : undefined, + width: this.deviceType === 'mobile' ? '100vw' : '30rem' + }); dialogRef .afterClosed() diff --git a/apps/client/src/app/pages/register/register-page.html b/apps/client/src/app/pages/register/register-page.html index eee49083a..a95a02ce0 100644 --- a/apps/client/src/app/pages/register/register-page.html +++ b/apps/client/src/app/pages/register/register-page.html @@ -18,30 +18,20 @@
    - - @if (hasPermissionForSocialLogin) { + @if (hasPermissionForAuthToken) { + + } + @if (hasPermissionForAuthToken && hasPermissionForAuthGoogle) {
    or
    - @if (false) { - - } + } + @if (hasPermissionForAuthGoogle) {
    -

    Buy and Hold

    +

    Buy and Hold

    Buy and hold is a passive investment strategy where you buy assets and hold them for a long period regardless of fluctuations in the @@ -22,7 +22,7 @@
    -

    Deflation

    +

    Deflation

    Deflation is a decrease of the general price level for goods and services in an economy over a period of time. @@ -38,7 +38,7 @@
    -

    Dollar-Cost Averaging (DCA)

    +

    Dollar-Cost Averaging (DCA)

    Dollar-cost averaging is an investment strategy where you split the total amount to be invested across periodic purchases of a @@ -56,7 +56,7 @@
    -

    Financial Independence

    +

    Financial Independence

    Financial independence is the status of having enough income, for example with a passive income like dividends, to cover your living @@ -73,7 +73,7 @@
    -

    FIRE

    +

    FIRE

    FIRE is a movement that promotes saving and investing to achieve financial independence and early retirement. @@ -85,7 +85,7 @@
    -

    Inflation

    +

    Inflation

    Inflation is an increase of the general price level for goods and services in an economy over a period of time. @@ -102,11 +102,11 @@ @if (hasPermissionForSubscription) {
    -

    Personal Finance Tools

    +

    Personal Finance Tools

    Personal finance tools are software applications that help - individuals manage their money, track expenses, set budgets, - monitor investments, and make informed financial decisions. + manage your money, track expenses, set budgets, monitor + investments, and make informed financial decisions.
    -

    Stagflation

    +

    Stagflation

    Stagflation describes a situation in which there is a stagnant economy with high unemployment and high inflation. @@ -134,7 +134,7 @@
    -

    Stealth Wealth

    +

    Stealth Wealth

    Stealth wealth is a lifestyle choice where you don’t openly show off your wealth, but instead live quietly to maintain privacy and diff --git a/apps/client/src/app/pages/resources/guides/resources-guides.component.html b/apps/client/src/app/pages/resources/guides/resources-guides.component.html index 3bd8efec6..54b3d1f3e 100644 --- a/apps/client/src/app/pages/resources/guides/resources-guides.component.html +++ b/apps/client/src/app/pages/resources/guides/resources-guides.component.html @@ -5,7 +5,7 @@
    -

    Boringly Getting Rich

    +

    Boringly Getting Rich

    The Boringly Getting Rich guide supports you to get started with investing. It introduces a strategy utilizing a broadly @@ -21,7 +21,7 @@
    -

    How do I get my finances in order?

    +

    How do I get my finances in order?

    Before you can think of long-term investing, you have to get your finances in order. Learn how you can reach your financial goals diff --git a/apps/client/src/app/pages/resources/markets/resources-markets.component.html b/apps/client/src/app/pages/resources/markets/resources-markets.component.html index 74d4bb82b..ce780aedf 100644 --- a/apps/client/src/app/pages/resources/markets/resources-markets.component.html +++ b/apps/client/src/app/pages/resources/markets/resources-markets.component.html @@ -3,7 +3,7 @@
    -

    Crypto Coins Heatmap

    +

    Crypto Coins Heatmap

    With the Crypto Coins Heatmap you can track the daily market movements of cryptocurrencies as a visual snapshot. @@ -17,7 +17,7 @@
    -

    Fear & Greed Index

    +

    Fear & Greed Index

    The fear and greed index was developed by CNNMoney to measure the primary emotions (fear and greed) that influence how much @@ -32,7 +32,7 @@
    -

    Inflation Chart

    +

    Inflation Chart

    Inflation Chart helps you find the intrinsic value of stock markets, stock prices, goods and services by adjusting them to the @@ -48,7 +48,7 @@
    -

    Stock Heatmap

    +

    Stock Heatmap

    With the Stock Heatmap you can track the daily market movements of stocks as a visual snapshot. diff --git a/apps/client/src/app/pages/resources/overview/resources-overview.component.html b/apps/client/src/app/pages/resources/overview/resources-overview.component.html index 39d7c1e62..3a6f18d40 100644 --- a/apps/client/src/app/pages/resources/overview/resources-overview.component.html +++ b/apps/client/src/app/pages/resources/overview/resources-overview.component.html @@ -5,7 +5,7 @@
    @for (item of overviewItems; track item) {
    -

    {{ item.title }}

    +

    {{ item.title }}

    {{ item.description }}

    Explore {{ item.title }} →
    diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html index 3c5c97558..a71ca0038 100644 --- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html +++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -330,9 +330,9 @@ Ghostfolio.

    diff --git a/apps/client/src/app/pages/zen/zen-page.component.ts b/apps/client/src/app/pages/zen/zen-page.component.ts index 0224c13c7..5ed9fe09c 100644 --- a/apps/client/src/app/pages/zen/zen-page.component.ts +++ b/apps/client/src/app/pages/zen/zen-page.component.ts @@ -8,7 +8,7 @@ import { MatTabsModule } from '@angular/material/tabs'; import { RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; -import { analyticsOutline, walletOutline } from 'ionicons/icons'; +import { albumsOutline, analyticsOutline } from 'ionicons/icons'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -43,7 +43,7 @@ export class GfZenPageComponent implements OnDestroy, OnInit { routerLink: internalRoutes.zen.routerLink }, { - iconName: 'wallet-outline', + iconName: 'albums-outline', label: internalRoutes.zen.subRoutes.holdings.title, routerLink: internalRoutes.zen.subRoutes.holdings.routerLink } @@ -54,7 +54,7 @@ export class GfZenPageComponent implements OnDestroy, OnInit { } }); - addIcons({ analyticsOutline, walletOutline }); + addIcons({ albumsOutline, analyticsOutline }); } public ngOnInit() { diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 6a90bc4df..10804aac9 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -1,19 +1,22 @@ -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 { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { + DEFAULT_PAGE_SIZE, HEADER_KEY_SKIP_INTERCEPTOR, HEADER_KEY_TOKEN } from '@ghostfolio/common/config'; -import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { - AssetProfileIdentifier, + CreatePlatformDto, + UpdateAssetProfileDto, + UpdatePlatformDto +} from '@ghostfolio/common/dtos'; +import { AdminData, AdminJobs, AdminMarketData, - AdminUsers, + AdminUserResponse, + AdminUsersResponse, + AssetProfileIdentifier, DataProviderGhostfolioStatusResponse, + DataProviderHistoricalResponse, EnhancedSymbolProfile, Filter } from '@ghostfolio/common/interfaces'; @@ -142,6 +145,10 @@ export class AdminService { return this.http.get('/api/v1/platform'); } + public fetchUserById(id: string) { + return this.http.get(`/api/v1/admin/user/${id}`); + } + public fetchUsers({ skip, take = DEFAULT_PAGE_SIZE @@ -154,7 +161,7 @@ export class AdminService { params = params.append('skip', skip); params = params.append('take', take); - return this.http.get('/api/v1/admin/user', { params }); + return this.http.get('/api/v1/admin/user', { params }); } public gather7Days() { @@ -208,7 +215,7 @@ export class AdminService { }) { const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; - return this.http.get(url); + return this.http.get(url); } public patchAssetProfile( diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 3cb5a8c75..4c324fe03 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -1,39 +1,40 @@ -import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto'; -import { UpdateAccessDto } from '@ghostfolio/api/app/access/update-access.dto'; -import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/create-account-balance.dto'; -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 { CreateWatchlistItemDto } from '@ghostfolio/api/app/endpoints/watchlist/create-watchlist-item.dto'; -import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { - Activities, - Activity -} from '@ghostfolio/api/app/order/interfaces/activities.interface'; -import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; -import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface'; -import { DeleteOwnUserDto } from '@ghostfolio/api/app/user/delete-own-user.dto'; -import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface'; -import { UpdateOwnAccessTokenDto } from '@ghostfolio/api/app/user/update-own-access-token.dto'; -import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { PropertyDto } from '@ghostfolio/api/services/property/property.dto'; + CreateAccessDto, + CreateAccountBalanceDto, + CreateAccountDto, + CreateOrderDto, + CreateTagDto, + CreateWatchlistItemDto, + DeleteOwnUserDto, + TransferBalanceDto, + UpdateAccessDto, + UpdateAccountDto, + UpdateBulkMarketDataDto, + UpdateOrderDto, + UpdateOwnAccessTokenDto, + UpdatePropertyDto, + UpdateTagDto, + UpdateUserSettingDto +} from '@ghostfolio/common/dtos'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { Access, AccessTokenResponse, AccountBalancesResponse, + AccountResponse, AccountsResponse, + ActivitiesResponse, + ActivityResponse, AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, + AssetResponse, BenchmarkMarketDataDetailsResponse, BenchmarkResponse, + CreateStripeCheckoutSessionResponse, DataProviderHealthResponse, - Export, + DataProviderHistoricalResponse, + ExportResponse, Filter, ImportResponse, InfoItem, @@ -49,12 +50,13 @@ import { PortfolioPerformanceResponse, PortfolioReportResponse, PublicPortfolioResponse, + SymbolItem, User, + UserItem, WatchlistResponse } from '@ghostfolio/common/interfaces'; import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; import type { - AccountWithValue, AiPromptMode, DateRange, GroupBy @@ -169,21 +171,24 @@ export class DataService { return params; } - public createCheckoutSession({ + public createStripeCheckoutSession({ couponId, priceId }: { couponId?: string; priceId: string; }) { - return this.http.post('/api/v1/subscription/stripe/checkout-session', { - couponId, - priceId - }); + return this.http.post( + '/api/v1/subscription/stripe/checkout-session', + { + couponId, + priceId + } + ); } public fetchAccount(aAccountId: string) { - return this.http.get(`/api/v1/account/${aAccountId}`); + return this.http.get(`/api/v1/account/${aAccountId}`); } public fetchAccountBalances(aAccountId: string) { @@ -212,7 +217,7 @@ export class DataService { sortColumn?: string; sortDirection?: SortDirection; take?: number; - }): Observable { + }): Observable { let params = this.buildFiltersAsQueryParams({ filters }); if (range) { @@ -247,7 +252,7 @@ export class DataService { } public fetchActivity(aActivityId: string) { - return this.http.get(`/api/v1/order/${aActivityId}`).pipe( + return this.http.get(`/api/v1/order/${aActivityId}`).pipe( map((activity) => { activity.createdAt = parseISO(activity.createdAt as unknown as string); activity.date = parseISO(activity.date as unknown as string); @@ -291,7 +296,7 @@ export class DataService { date: Date; symbol: string; }) { - return this.http.get( + return this.http.get( `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}` ); } @@ -345,7 +350,7 @@ export class DataService { public fetchAsset({ dataSource, symbol - }: AssetProfileIdentifier): Observable { + }: AssetProfileIdentifier): Observable { return this.http.get(`/api/v1/asset/${dataSource}/${symbol}`).pipe( map((data) => { for (const item of data.marketData) { @@ -406,7 +411,7 @@ export class DataService { params = params.append('activityIds', activityIds.join(',')); } - return this.http.get('/api/v1/export', { + return this.http.get('/api/v1/export', { params }); } @@ -804,7 +809,7 @@ export class DataService { return this.http.put(`/api/v1/account/${aAccount.id}`, aAccount); } - public putAdminSetting(key: string, aData: PropertyDto) { + public putAdminSetting(key: string, aData: UpdatePropertyDto) { return this.http.put(`/api/v1/admin/settings/${key}`, aData); } diff --git a/apps/client/src/app/services/ics/ics.service.ts b/apps/client/src/app/services/ics/ics.service.ts index b94b2dee3..a3235380e 100644 --- a/apps/client/src/app/services/ics/ics.service.ts +++ b/apps/client/src/app/services/ics/ics.service.ts @@ -1,5 +1,5 @@ import { capitalize } from '@ghostfolio/common/helper'; -import { Export } from '@ghostfolio/common/interfaces'; +import { ExportResponse } from '@ghostfolio/common/interfaces'; import { Injectable } from '@angular/core'; import { Type } from '@prisma/client'; @@ -13,7 +13,7 @@ export class IcsService { private readonly ICS_LINE_BREAK = '\r\n'; public transformActivitiesToIcsContent( - aActivities: Export['activities'] + aActivities: ExportResponse['activities'] ): string { const header = [ 'BEGIN:VCALENDAR', diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 323f07a5b..94d8470f7 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -1,9 +1,11 @@ -import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; -import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; -import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; +import { + CreateAccountWithBalancesDto, + CreateAssetProfileWithMarketDataDto, + CreateOrderDto, + CreateTagDto +} from '@ghostfolio/common/dtos'; import { parseDate as parseDateHelper } from '@ghostfolio/common/helper'; +import { Activity } from '@ghostfolio/common/interfaces'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; @@ -76,12 +78,8 @@ export class ImportActivitiesService { updateAccountBalance: false }); - if ( - dataSource === DataSource.MANUAL && - !['FEE', 'INTEREST', 'LIABILITY'].includes(type) - ) { + if (dataSource === DataSource.MANUAL) { // Create synthetic asset profile for MANUAL data source - // (except for FEE, INTEREST, and LIABILITY which don't require asset profiles) assetProfiles.push({ currency, symbol, diff --git a/apps/client/src/app/services/internet-identity.service.ts b/apps/client/src/app/services/internet-identity.service.ts deleted file mode 100644 index 30ae13679..000000000 --- a/apps/client/src/app/services/internet-identity.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { OAuthResponse } from '@ghostfolio/common/interfaces'; - -import { HttpClient } from '@angular/common/http'; -import { Injectable, OnDestroy } from '@angular/core'; -import { AuthClient } from '@dfinity/auth-client'; -import { EMPTY, Subject } from 'rxjs'; -import { catchError, takeUntil } from 'rxjs/operators'; - -@Injectable({ - providedIn: 'root' -}) -export class InternetIdentityService implements OnDestroy { - private unsubscribeSubject = new Subject(); - - public constructor(private http: HttpClient) {} - - public async login(): Promise { - const authClient = await AuthClient.create({ - idleOptions: { - disableDefaultIdleCallback: true, - disableIdle: true - } - }); - - return new Promise((resolve, reject) => { - authClient.login({ - onError: async () => { - return reject(); - }, - onSuccess: () => { - const principalId = authClient.getIdentity().getPrincipal(); - - this.http - .post(`/api/v1/auth/internet-identity`, { - principalId: principalId.toText() - }) - .pipe( - catchError(() => { - reject(); - return EMPTY; - }), - takeUntil(this.unsubscribeSubject) - ) - .subscribe((response) => { - resolve(response); - }); - } - }); - }); - } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } -} diff --git a/apps/client/src/app/services/user/user.service.ts b/apps/client/src/app/services/user/user.service.ts index f52a52975..bd9d7d04c 100644 --- a/apps/client/src/app/services/user/user.service.ts +++ b/apps/client/src/app/services/user/user.service.ts @@ -116,18 +116,18 @@ export class UserService extends ObservableStore { permissions.enableSubscriptionInterstitial ) ) { - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfSubscriptionInterstitialDialogComponent, - { - autoFocus: false, - data: { - user - } as SubscriptionInterstitialDialogParams, - disableClose: true, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + SubscriptionInterstitialDialogParams + >(GfSubscriptionInterstitialDialogComponent, { + autoFocus: false, + data: { + user + }, + disableClose: true, + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() diff --git a/apps/client/src/app/services/web-authn.service.ts b/apps/client/src/app/services/web-authn.service.ts index 3885b2f94..95c264310 100644 --- a/apps/client/src/app/services/web-authn.service.ts +++ b/apps/client/src/app/services/web-authn.service.ts @@ -1,9 +1,9 @@ -import { AuthDeviceDto } from '@ghostfolio/api/app/auth-device/auth-device.dto'; +import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; +import { AuthDeviceDto } from '@ghostfolio/common/dtos'; import { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON -} from '@ghostfolio/api/app/auth/interfaces/simplewebauthn'; -import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; +} from '@ghostfolio/common/interfaces'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; diff --git a/apps/client/src/assets/icons/internet-computer.svg b/apps/client/src/assets/icons/internet-computer.svg deleted file mode 100644 index 6a1bf6c86..000000000 --- a/apps/client/src/assets/icons/internet-computer.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/apps/client/src/assets/images/blog/black-weeks-2025.jpg b/apps/client/src/assets/images/blog/black-weeks-2025.jpg new file mode 100644 index 000000000..c71827c5e Binary files /dev/null and b/apps/client/src/assets/images/blog/black-weeks-2025.jpg differ diff --git a/apps/client/src/assets/images/logo-oss-gallery.svg b/apps/client/src/assets/images/logo-oss-gallery.svg new file mode 100644 index 000000000..e7fc2ffa8 --- /dev/null +++ b/apps/client/src/assets/images/logo-oss-gallery.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/client/src/assets/images/logo-selfhostedhub.svg b/apps/client/src/assets/images/logo-selfhostedhub.svg new file mode 100644 index 000000000..72fccdc07 --- /dev/null +++ b/apps/client/src/assets/images/logo-selfhostedhub.svg @@ -0,0 +1,2 @@ + + diff --git a/apps/client/src/assets/images/sponsors/logo-lambdatest.png b/apps/client/src/assets/images/sponsors/logo-lambdatest.png new file mode 100644 index 000000000..9e05b1cde Binary files /dev/null and b/apps/client/src/assets/images/sponsors/logo-lambdatest.png differ diff --git a/apps/client/src/assets/oss-friends.json b/apps/client/src/assets/oss-friends.json index 827b56c3a..8a64650fe 100644 --- a/apps/client/src/assets/oss-friends.json +++ b/apps/client/src/assets/oss-friends.json @@ -1,5 +1,5 @@ { - "createdAt": "2025-09-17T00:00:00.000Z", + "createdAt": "2025-11-21T00:00:00.000Z", "data": [ { "name": "Activepieces", @@ -16,6 +16,11 @@ "description": "Argos provides the developer tools to debug tests and detect visual regressions.", "href": "https://argos-ci.com" }, + { + "name": "Bifrost", + "description": "Fastest LLM gateway with adaptive load balancer, cluster mode, guardrails, 1000+ models support & <100 µs overhead at 5k RPS.", + "href": "https://www.getmaxim.ai/bifrost" + }, { "name": "Cal.com", "description": "Cal.com is a scheduling tool that helps you schedule meetings without the back-and-forth emails.", @@ -56,11 +61,6 @@ "description": "Inbox Zero makes it easy to clean up your inbox and reach inbox zero fast. It provides bulk newsletter unsubscribe, cold email blocking, email analytics, and AI automations.", "href": "https://getinboxzero.com" }, - { - "name": "Infisical", - "description": "Open source, end-to-end encrypted platform that lets you securely manage secrets and configs across your team, devices, and infrastructure.", - "href": "https://infisical.com" - }, { "name": "KeepHQ", "description": "Keep is an open-source AIOps (AI for IT operations) platform", @@ -111,6 +111,11 @@ "description": "Open-source solution to deploy, scale, and operate your multiplayer game.", "href": "https://rivet.gg" }, + { + "name": "Rybbit", + "description": "Next-gen, open source, lightweight, cookieless web & product analytics for everyone.", + "href": "https://rybbit.com" + }, { "name": "Shelf.nu", "description": "Open Source Asset and Equipment tracking software that lets you create QR asset labels, manage and overview your assets across locations.", diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 65a748748..e2740945e 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -38,7 +38,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -70,7 +70,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -295,7 +295,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -358,6 +358,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Realment vol revocar aquest accés? @@ -398,14 +406,6 @@ 86 - - Holdings - En cartera - - libs/ui/src/lib/assistant/assistant.html - 110 - - Cash Balances Balanç de Caixa @@ -471,7 +471,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -523,7 +527,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -559,11 +563,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -599,11 +603,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -635,11 +639,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -651,7 +655,7 @@ Realment vol suprimir aquest compte? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -671,7 +675,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -691,7 +695,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -831,7 +835,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -874,6 +878,14 @@ 96 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETFs sense País @@ -1003,7 +1015,7 @@ Oooh! No s’han pogut recopilar les dades históriques. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -1065,6 +1077,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -1075,7 +1091,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1095,7 +1111,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1107,7 +1123,7 @@ Mapatge de Símbols apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -1123,7 +1139,7 @@ Configuració del Proveïdor de Dades apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -1131,7 +1147,7 @@ Prova apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -1139,11 +1155,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1159,7 +1175,7 @@ Notes apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1307,7 +1323,7 @@ Recollida de Dades apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -1467,7 +1483,7 @@ Està segur que vol eliminar aquest usuari? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1482,6 +1498,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Implicació per Dia @@ -1489,6 +1513,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -1503,7 +1531,7 @@ Actuar com un altre Usuari apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1511,7 +1539,7 @@ Eliminar Usuari apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1559,7 +1587,7 @@ Punt de Referència apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1599,7 +1627,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -1615,7 +1643,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -1638,24 +1666,16 @@ 5 - - Get started - Primers Passos - - apps/client/src/app/components/header/header.component.html - 432 - - Oops! Incorrect Security Token. Oooh! El testimoni de seguretat és incorrecte. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1691,7 +1711,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1715,7 +1735,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -1739,7 +1759,7 @@ Informar d’un Problema amb les Dades apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1947,7 +1967,7 @@ Fitxa de seguretat apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -1979,15 +1999,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1999,31 +2019,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Inicieu la sessió amb la identitat d’Internet - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Inicieu la sessió amb Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -2031,7 +2043,7 @@ Manteniu la sessió iniciada apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -2071,7 +2083,7 @@ Rendiment brut absolut apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -2079,7 +2091,7 @@ Rendiment net absolut apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -2087,7 +2099,7 @@ Rendiment net apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -2095,7 +2107,7 @@ Actius totals apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -2103,7 +2115,7 @@ Actius apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -2111,7 +2123,7 @@ Poder adquisitiu apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -2119,7 +2131,7 @@ Exclòs de l’anàlisi apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2127,7 +2139,7 @@ Passius apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -2139,7 +2151,7 @@ Valor net apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -2147,7 +2159,7 @@ Rendiment anualitzat apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -2155,7 +2167,7 @@ Definiu l’import del vostre fons d’emergència. apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -2183,11 +2195,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -2203,11 +2215,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -2219,11 +2231,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -2235,11 +2247,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -2251,7 +2263,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -2263,11 +2275,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -2307,7 +2319,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -2319,7 +2331,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -2331,7 +2343,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -2343,7 +2355,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -2355,7 +2367,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -2363,7 +2375,7 @@ Vaja! No s’ha pogut concedir l’accés. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -2409,6 +2421,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -2423,7 +2439,7 @@ Introduïu el vostre codi de cupó. apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -2431,7 +2447,7 @@ No s’ha pogut bescanviar el codi de cupó apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -2439,7 +2455,7 @@ El codi del cupó s’ha bescanviat apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -2447,7 +2463,7 @@ Torna a carregar apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -2463,7 +2479,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -2471,7 +2487,7 @@ Prova Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -2479,7 +2495,7 @@ Bescanviar el cupó apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -2510,6 +2526,14 @@ 280 + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 + + Oops! There was an error setting up biometric authentication. Ups! Hi ha hagut un error en configurar l’autenticació biomètrica. @@ -2563,7 +2587,7 @@ Localització apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2699,7 +2723,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2719,7 +2743,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2727,7 +2751,7 @@ D’acord apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -2735,7 +2759,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -2873,6 +2897,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -2881,13 +2909,17 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Oops, cash balance transfer has failed. Vaja, la transferència del saldo en efectiu ha fallat. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -3129,6 +3161,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -3326,14 +3362,34 @@ Get Started Comença + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Holdings @@ -3358,6 +3414,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -3374,6 +3434,10 @@ Markets Mercats + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -3471,32 +3535,12 @@ 11 - - Get Started - Comença - - apps/client/src/app/pages/landing/landing-page.html - 42 - - - apps/client/src/app/pages/landing/landing-page.html - 346 - - - apps/client/src/app/pages/pricing/pricing-page.html - 378 - - - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 - - Monthly Active Users Usuaris actius mensuals apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -3504,7 +3548,7 @@ Estrelles a GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -3516,7 +3560,7 @@ Activa el Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -3528,7 +3572,7 @@ Com es veu a apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -3536,7 +3580,7 @@ Protegeix els teus actius. Refina la teva estratègia d’inversió personal. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -3544,7 +3588,7 @@ Ghostfolio permet a la gent ocupada fer un seguiment d’accions, ETF o criptomonedes sense ser rastrejada. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -3552,7 +3596,7 @@ Vista de 360° apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -3560,7 +3604,7 @@ Obtingueu la imatge completa de les vostres finances personals en múltiples plataformes. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -3568,7 +3612,7 @@ Web3 llest apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -3576,7 +3620,7 @@ Utilitza Ghostfolio de manera anònima i sigues propietari de les teves dades financeres. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -3584,7 +3628,7 @@ Beneficia’t de millores contínues gràcies a una comunitat forta. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -3600,7 +3644,7 @@ Per què Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -3608,7 +3652,7 @@ Ghostfolio és per a tu si ets... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -3616,7 +3660,7 @@ negociar accions, ETF o criptomonedes en múltiples plataformes apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -3624,7 +3668,7 @@ perseguint una compra & mantenir l’estratègia apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -3632,7 +3676,7 @@ interessat a obtenir informació sobre la composició de la vostra cartera apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -3640,7 +3684,7 @@ valorant la privadesa i la propietat de les dades apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -3648,7 +3692,7 @@ al minimalisme apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -3656,7 +3700,7 @@ preocupant-se per diversificar els seus recursos econòmics apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -3664,7 +3708,7 @@ interessada en la independència financera apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -3672,7 +3716,7 @@ dir no als fulls de càlcul apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -3680,7 +3724,7 @@ encara llegint aquesta llista apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -3688,7 +3732,7 @@ Més informació sobre Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -3696,7 +3740,7 @@ Que nostre usuaris estan dient apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -3704,7 +3748,7 @@ Membres de tot el món estan utilitzant Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -3712,7 +3756,7 @@ Com ho fa Ghostfolio treballar? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -3720,7 +3764,7 @@ Comença en només 3 passos apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -3736,7 +3780,7 @@ Registra’t de manera anònima* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -3744,7 +3788,7 @@ * no es requereix cap adreça de correu electrònic ni targeta de crèdit apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -3752,7 +3796,7 @@ Afegiu qualsevol de les vostres transaccions històriques apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -3760,7 +3804,7 @@ Obteniu informació valuosa sobre la composició de la vostra cartera apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -3768,7 +3812,7 @@ Són tu llest? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -3776,7 +3820,7 @@ Uneix-te ara o consulteu el compte d’exemple apps/client/src/app/pages/landing/landing-page.html - 334 + 333 @@ -3808,7 +3852,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -3922,6 +3966,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -4012,7 +4060,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -4020,7 +4068,7 @@ Activitats d’importació apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -4028,7 +4076,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -4036,7 +4084,7 @@ Importar dividends apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -4044,7 +4092,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -4052,7 +4100,7 @@ S’estan important dades... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -4060,7 +4108,7 @@ La importació s’ha completat apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -4076,7 +4124,7 @@ S’estan validant les dades... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -4103,8 +4151,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -4364,7 +4412,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -4380,7 +4428,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -4408,7 +4456,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -4428,7 +4476,7 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 @@ -4447,6 +4495,14 @@ 91 + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 + + Absolute Asset Performance Rendiment absolut dels actius @@ -4636,15 +4692,11 @@ Transaccions il·limitades apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -4652,15 +4704,11 @@ Comptes il·limitats apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -4668,15 +4716,11 @@ Rendiment de la cartera apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -4684,15 +4728,11 @@ Importació i exportació de dades apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -4700,7 +4740,7 @@ Suport comunitari apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -4708,7 +4748,7 @@ S’allotja automàticament, actualitza manualment. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -4716,11 +4756,11 @@ Gratuït apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -4728,7 +4768,7 @@ Per a nous inversors que acaben de començar a operar. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -4736,11 +4776,11 @@ Oferta al núvol Ghostfolio totalment gestionada. apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 @@ -4748,7 +4788,7 @@ Per a inversors ambiciosos que necessiten la imatge completa dels seus actius financers. apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -4756,7 +4796,7 @@ Suport per correu electrònic i xat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -4764,7 +4804,7 @@ Pagament únic, sense renovació automàtica. apps/client/src/app/pages/pricing/pricing-page.html - 303 + 288 @@ -4772,7 +4812,7 @@ És gratuït. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -4827,20 +4867,12 @@ 281 - - Continue with Internet Identity - Continueu amb Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Continueu amb Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -5187,6 +5219,10 @@ Membership Pertinença + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5201,7 +5237,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5253,7 +5289,7 @@ Realment voleu eliminar el saldo d’aquest compte? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5265,7 +5301,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -5277,7 +5313,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -5285,7 +5321,7 @@ Suprimeix les activitats libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -5293,7 +5329,7 @@ Esborrany libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -5301,7 +5337,7 @@ Clonar libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -5309,7 +5345,7 @@ Exporta l’esborrany com a ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -5317,7 +5353,7 @@ De veritat vols suprimir aquestes activitats? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -5325,7 +5361,7 @@ Realment vols suprimir aquesta activitat? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -5333,7 +5369,7 @@ Setmana fins avui libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5345,7 +5381,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5353,7 +5389,7 @@ Mes fins a la data libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5365,7 +5401,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5373,7 +5409,7 @@ Any fins a la data libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5393,7 +5429,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5405,7 +5441,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -5415,29 +5451,33 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Date Range Interval de dates libs/ui/src/lib/assistant/assistant.html - 171 + 170 - + Reset Filters Restableix els filtres libs/ui/src/lib/assistant/assistant.html - 266 + 204 - + Apply Filters Aplicar filtres libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5485,7 +5525,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -5553,7 +5593,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -5609,14 +5649,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -5651,14 +5691,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -5733,7 +5773,7 @@ Fons d’emergència apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -5809,7 +5849,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -5867,14 +5907,14 @@ Tag Etiqueta - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Year @@ -5884,6 +5924,18 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years Anys @@ -5913,7 +5965,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5941,7 +5993,7 @@ Vendre apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -5953,7 +6005,7 @@ Efectiu apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -6004,6 +6056,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Bona @@ -6177,11 +6237,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -6297,7 +6357,7 @@ Open Source apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canada @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Close apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Yes @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Copy link to clipboard @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - No auto-renewal. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year This year @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Could not generate an API key apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Set this API key in your self-hosted environment: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Ghostfolio Premium Data Provider API Key apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Do you really want to generate a new API key? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Save apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Default Market Price apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Security token apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Do you really want to generate a new security token for this user? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Generate Security Token apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ with API access for apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Do you really want to delete this item? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Name - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Quick Links @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Asset Profiles - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Live Demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Limited Offer! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Get extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Do you really want to generate a new security token? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Manage Asset Profile apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Average Unit Price apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Follow Ghostfolio on LinkedIn diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index c9a772b60..65e71fbd8 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -10,7 +10,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -42,7 +42,7 @@ bitte apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -62,7 +62,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -89,6 +89,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Möchtest du diese Zugangsberechtigung wirklich widerrufen? @@ -142,7 +150,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -198,11 +210,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -238,11 +250,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -274,11 +286,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -290,7 +302,7 @@ Möchtest du dieses Konto wirklich löschen? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -376,6 +388,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -386,7 +402,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -434,7 +450,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -638,7 +654,7 @@ Möchtest du diesen Benutzer wirklich löschen? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -653,6 +669,14 @@ 231 + + No auto-renewal on membership. + Keine automatische Erneuerung der Mitgliedschaft. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Engagement pro Tag @@ -660,6 +684,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -692,14 +720,34 @@ Get Started Registrieren + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Sign in @@ -710,7 +758,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -730,11 +778,11 @@ Ups! Falsches Sicherheits-Token. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -766,7 +814,7 @@ Sicherheits-Token apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -798,15 +846,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -818,31 +866,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Einloggen mit Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Einloggen mit Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -850,7 +890,7 @@ Eingeloggt bleiben apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -866,7 +906,7 @@ Absolute Brutto Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -874,7 +914,7 @@ Absolute Netto Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -882,7 +922,7 @@ Netto Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -890,7 +930,7 @@ Gesamtanlagevermögen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -898,7 +938,7 @@ Kaufkraft apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -906,7 +946,7 @@ Gesamtvermögen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -914,7 +954,7 @@ Performance pro Jahr apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -922,7 +962,7 @@ Bitte setze den Betrag deines Notfallfonds. apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -934,7 +974,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -954,7 +994,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -982,7 +1022,7 @@ Datenfehler melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1022,7 +1062,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1034,7 +1074,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1046,7 +1086,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1058,7 +1098,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1070,7 +1110,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1078,7 +1118,7 @@ Okay apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1086,7 +1126,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1146,7 +1186,7 @@ Bitte gebe deinen Gutscheincode ein. apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1154,7 +1194,7 @@ Gutscheincode konnte nicht eingelöst werden apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1162,7 +1202,7 @@ Gutscheincode wurde eingelöst apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1170,7 +1210,7 @@ Neu laden apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1194,7 +1234,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1202,7 +1242,7 @@ Premium ausprobieren apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1210,7 +1250,7 @@ Gutschein einlösen apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1234,7 +1274,7 @@ Lokalität apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1280,6 +1320,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1328,6 +1372,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1336,6 +1384,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1382,7 +1434,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -1536,6 +1588,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1632,6 +1688,10 @@ Markets Märkte + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1832,10 +1892,6 @@ libs/common/src/lib/routes/routes.ts 167 - - - Holdings - Positionen libs/ui/src/lib/assistant/assistant.html 110 @@ -1874,7 +1930,7 @@ Verkauf apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -1914,7 +1970,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1930,7 +1986,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -1938,7 +1994,7 @@ Kommentar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1980,6 +2036,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -1998,7 +2058,7 @@ Daten importieren... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -2006,7 +2066,7 @@ Der Import wurde abgeschlossen apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2109,20 +2169,12 @@ 281 - - Continue with Internet Identity - Weiter mit Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Weiter mit Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2186,7 +2238,7 @@ Geplant libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2194,7 +2246,7 @@ Aktivitäten importieren apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2202,7 +2254,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2214,7 +2266,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2226,7 +2278,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2234,7 +2286,7 @@ Kopieren libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2242,7 +2294,7 @@ Geplante Aktivität als ICS exportieren libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2250,7 +2302,7 @@ Möchtest du diese Aktivität wirklich löschen? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2266,7 +2318,7 @@ kontaktiere uns apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2301,14 +2353,6 @@ 56 - - Get started - Registrieren - - apps/client/src/app/components/header/header.component.html - 432 - - This feature is currently unavailable. Diese Funktion ist derzeit nicht verfügbar. @@ -2330,7 +2374,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2342,7 +2386,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2388,6 +2432,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Exclude from Analysis @@ -2478,7 +2526,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2594,7 +2642,7 @@ Benchmark apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -2614,7 +2662,7 @@ Von der Analyse ausgenommen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2686,14 +2734,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2720,14 +2768,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Symbol @@ -2760,21 +2808,21 @@ Tag Tag - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Bargeld apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -2817,6 +2865,14 @@ 51 + + Authentication + Authentifizierung + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Anleihe @@ -2878,7 +2934,7 @@ Notfallfonds apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2898,7 +2954,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2918,11 +2974,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3026,7 +3082,7 @@ Symbol Zuordnung apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -3042,7 +3098,7 @@ Suchst du nach einem Studentenrabatt? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3058,7 +3114,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3118,7 +3174,7 @@ Daten validieren... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3177,8 +3233,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3202,7 +3258,7 @@ Dividenden importieren apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3210,7 +3266,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3314,11 +3370,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3330,11 +3386,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3346,11 +3402,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3362,11 +3418,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3414,7 +3470,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3430,15 +3486,11 @@ Unlimitierte Transaktionen apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3446,15 +3498,11 @@ Unlimitierte Accounts apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3462,15 +3510,11 @@ Portfolio Performance apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3478,7 +3522,7 @@ Selbst gehostet, manuelles Update. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3486,11 +3530,11 @@ Kostenlos apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3498,7 +3542,7 @@ Für Einsteiger, die gerade mit dem Börsenhandel beginnen. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -3506,47 +3550,27 @@ Vollständig verwaltetes Ghostfolio Cloud-Angebot. apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Für ambitionierte Anleger, die den vollständigen Überblick über ihr Anlagevermögen benötigen. - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Einmalige Zahlung, keine automatische Erneuerung. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Jetzt loslegen - - apps/client/src/app/pages/landing/landing-page.html - 42 + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Für ambitionierte Anleger, die den vollständigen Überblick über ihr Anlagevermögen benötigen. apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Einmalige Zahlung, keine automatische Erneuerung. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3554,7 +3578,7 @@ Es ist kostenlos. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3566,7 +3590,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3582,11 +3606,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3602,15 +3626,11 @@ Datenimport und -export apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3626,7 +3646,7 @@ Community Support apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3634,7 +3654,7 @@ E-Mail und Chat Support apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3686,7 +3706,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3710,7 +3730,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3726,7 +3746,7 @@ Benutzer verwenden apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3734,7 +3754,7 @@ Benutzer löschen apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3742,7 +3762,7 @@ Möchtest du diese Aktivitäten wirklich löschen? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3782,11 +3802,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3981,12 +4001,24 @@ 32 + + View Details + Details anzeigen + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Verbindlichkeiten apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4142,7 +4174,7 @@ Scraper Konfiguration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4373,6 +4405,14 @@ 43 + + Everything in + Alles von + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETFs ohne Länder @@ -4394,7 +4434,7 @@ Anlagevermögen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4574,7 +4614,7 @@ Sterne auf GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4586,7 +4626,7 @@ Downloads auf Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4706,7 +4746,7 @@ Monatlich aktive Nutzer apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4714,7 +4754,7 @@ Bekannt aus apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4722,7 +4762,7 @@ Schütze dein Vermögen. Optimiere deine persönliche Anlagestrategie. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4730,7 +4770,7 @@ Ghostfolio ermöglicht es geschäftigen Leuten, den Überblick über Aktien, ETFs oder Kryptowährungen zu behalten, ohne überwacht zu werden. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4738,7 +4778,7 @@ 360° Ansicht apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4746,7 +4786,7 @@ Web3 ready apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4754,7 +4794,7 @@ Nutze Ghostfolio ganz anonym und behalte deine Finanzdaten. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4762,7 +4802,7 @@ Profitiere von kontinuierlichen Verbesserungen durch eine aktive Community. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4778,7 +4818,7 @@ Warum Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4786,7 +4826,7 @@ Ghostfolio ist für dich geeignet, wenn du... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4794,7 +4834,7 @@ Aktien, ETFs oder Kryptowährungen auf unterschiedlichen Plattformen handelst apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4802,7 +4842,7 @@ eine Buy & Hold Strategie verfolgst apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4810,7 +4850,7 @@ dich für die Zusammensetzung deines Portfolios interessierst apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4818,7 +4858,7 @@ Privatsphäre und Datenhoheit wertschätzt apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4826,7 +4866,7 @@ zum Frugalismus oder Minimalismus neigst apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4834,7 +4874,7 @@ dich um die Diversifizierung deiner finanziellen Mittel kümmerst apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4842,7 +4882,7 @@ Interesse an finanzieller Freiheit hast apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4850,7 +4890,7 @@ Nein sagst zu Excel-Tabellen im Jahr apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4858,7 +4898,7 @@ diese Liste bis zum Ende liest apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4866,7 +4906,7 @@ Erfahre mehr über Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4874,7 +4914,7 @@ Was unsere Nutzer sagen apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4882,7 +4922,7 @@ Nutzer aus aller Welt verwenden Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4890,7 +4930,7 @@ Wie funktioniert Ghostfolio ? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4898,7 +4938,7 @@ Registriere dich anonym* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4906,7 +4946,7 @@ * Keine E-Mail-Adresse oder Kreditkarte erforderlich apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4914,7 +4954,7 @@ Füge historische Transaktionen hinzu apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4922,7 +4962,7 @@ Erhalte nützliche Erkenntnisse über die Zusammensetzung deines Portfolios apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4930,7 +4970,7 @@ Bist du bereit? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4938,7 +4978,7 @@ Verschaffe dir einen vollständigen Überblick deiner persönlichen Finanzen über mehrere Plattformen hinweg. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4946,7 +4986,7 @@ Beginne mit nur 3 Schritten apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5252,7 +5292,7 @@ mit deiner Universitäts-E-Mail-Adresse apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5324,7 +5364,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5418,6 +5458,10 @@ Membership Mitgliedschaft + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5432,7 +5476,7 @@ Fordere ihn an apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5544,7 +5588,7 @@ Ups, der Cash-Bestand Transfer ist fehlgeschlagen. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5576,7 +5620,7 @@ Ups! Die historischen Daten konnten nicht geparsed werden. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5628,7 +5672,7 @@ Möchtest du diesen Cash-Bestand wirklich löschen? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5652,7 +5696,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5660,7 +5704,7 @@ Zeitraum libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5692,7 +5736,7 @@ Ups! Der Zugang konnte nicht gewährt werden. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5736,7 +5780,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5756,7 +5800,15 @@ hier apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Position abschliessen + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5812,7 +5864,7 @@ Seit Wochenbeginn libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5824,7 +5876,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5832,7 +5884,7 @@ Seit Monatsbeginn libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5844,7 +5896,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5852,7 +5904,7 @@ Seit Jahresbeginn libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5875,12 +5927,12 @@ 8 - + Reset Filters Filter zurücksetzen libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5900,7 +5952,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5912,15 +5964,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Filter anwenden libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5928,7 +5980,7 @@ Finanzmarktdaten synchronisieren apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6089,7 +6141,7 @@ Aktivitäten löschen libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6145,7 +6197,15 @@ Melde dich jetzt an oder probiere die Live Demo aus apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Berücksichtigen in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6321,7 +6381,7 @@ Open Source apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6420,6 +6480,14 @@ 83 + + View Holding + Position ansehen + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Kanada @@ -6561,7 +6629,7 @@ Ups! Der Zugang konnte nicht bearbeitet werden. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6577,7 +6645,7 @@ Inaktiv apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6589,7 +6657,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6641,7 +6709,7 @@ Schliessen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6680,6 +6748,14 @@ 11 + + Role + Rolle + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Ja @@ -6688,14 +6764,6 @@ 34 - - Accounts - Konten - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Link in die Zwischenablage kopieren @@ -6725,7 +6793,7 @@ Wenn du die Eröffnung eines Kontos planst bei apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6768,14 +6836,6 @@ 69 - - No auto-renewal. - Keine automatische Erneuerung. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Dieses Jahr @@ -6837,7 +6897,7 @@ um unseren Empfehlungslink zu verwenden und ein Ghostfolio Premium-Abonnement für ein Jahr zu erhalten apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7089,13 +7149,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key API-Schlüssel konnte nicht erstellt werden apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7103,7 +7167,7 @@ Setze diesen API-Schlüssel in deiner selbst gehosteten Umgebung: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7111,7 +7175,7 @@ API-Schlüssel für den Ghostfolio Premium Datenanbieter apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7119,7 +7183,7 @@ Möchtest du wirklich einen neuen API-Schlüssel erstellen? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7159,7 +7223,7 @@ Speichern apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7179,7 +7243,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7211,7 +7275,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7275,7 +7339,7 @@ Standardmarktpreis apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7283,7 +7347,7 @@ Modus apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7291,7 +7355,7 @@ Selektor apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7299,7 +7363,7 @@ HTTP Request-Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7343,7 +7407,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7363,11 +7427,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7463,11 +7527,11 @@ Sicherheits-Token apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7475,7 +7539,7 @@ Möchtest du für diesen Benutzer wirklich ein neues Sicherheits-Token generieren? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7483,7 +7547,7 @@ Konto, Position oder Seite finden... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7491,7 +7555,7 @@ Sicherheits-Token generieren apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7572,7 +7636,7 @@ inklusive API-Zugriff für apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Möchtest du diesen Eintrag wirklich löschen? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Name - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Schnellzugriff @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Anlageprofile - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Live Demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Begrenztes Angebot! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Erhalte extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Möchtest du wirklich ein neues Sicherheits-Token generieren? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Anlageprofil verwalten apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Ø Preis pro Einheit apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registrierungsdatum + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Folge Ghostfolio auf LinkedIn diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 06ae21772..2703aebb8 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -11,7 +11,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -43,7 +43,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -63,7 +63,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -90,6 +90,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? ¿Quieres revocar el acceso concedido? @@ -143,7 +151,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -199,11 +211,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -239,11 +251,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -275,11 +287,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -291,7 +303,7 @@ ¿Estás seguro de eliminar esta cuenta? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -377,6 +389,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -387,7 +403,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -435,7 +451,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -623,7 +639,7 @@ ¿Estás seguro de eliminar este usuario? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -638,6 +654,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Contratación diaria @@ -645,6 +669,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -677,14 +705,34 @@ Get Started Empezar + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Sign in @@ -695,7 +743,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -715,11 +763,11 @@ Vaya! Token de seguridad incorrecto. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -751,7 +799,7 @@ Token de seguridad apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -783,15 +831,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -803,31 +851,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Iniciar sesión con Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Iniciar sesión con Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -835,7 +875,7 @@ Seguir conectado apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -851,7 +891,7 @@ Rendimiento bruto absoluto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -859,7 +899,7 @@ Rendimiento neto absoluto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -867,7 +907,7 @@ Rendimiento neto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -875,7 +915,7 @@ Total de activos apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -883,7 +923,7 @@ Capacidad de compra apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -891,7 +931,7 @@ Patrimonio neto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -899,7 +939,7 @@ Rendimiento anualizado apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -907,7 +947,7 @@ Por favor, ingresa la cantidad de tu fondo de emergencia: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -919,7 +959,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -939,7 +979,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -967,7 +1007,7 @@ Reporta un anomalía de los datos apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1007,7 +1047,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1019,7 +1059,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1031,7 +1071,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1043,7 +1083,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1055,7 +1095,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1063,7 +1103,7 @@ De acuerdo apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1071,7 +1111,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1131,7 +1171,7 @@ Por favor, ingresa tu código de cupón: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1139,7 +1179,7 @@ No se puede canjear este código de cupón apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1147,7 +1187,7 @@ El codigo de cupón ha sido canjeado apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1155,7 +1195,7 @@ Refrescar apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1179,7 +1219,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1187,7 +1227,7 @@ Prueba Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1195,7 +1235,7 @@ Canjea el cupón apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1219,7 +1259,7 @@ Ubicación apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1265,6 +1305,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1313,6 +1357,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1321,6 +1369,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1367,7 +1419,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -1521,6 +1573,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1617,6 +1673,10 @@ Markets Mercados + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1817,10 +1877,6 @@ libs/common/src/lib/routes/routes.ts 167 - - - Holdings - Participaciones libs/ui/src/lib/assistant/assistant.html 110 @@ -1859,7 +1915,7 @@ Venta apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -1899,7 +1955,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1915,7 +1971,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -1923,7 +1979,7 @@ Nota apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1965,6 +2021,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -1983,7 +2043,7 @@ Importando datos... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -1991,7 +2051,7 @@ La importación se ha completado apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2094,20 +2154,12 @@ 281 - - Continue with Internet Identity - Continuar con Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Continuar con Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2171,7 +2223,7 @@ Borrador libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2179,7 +2231,7 @@ Importar operaciones apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2187,7 +2239,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2199,7 +2251,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2211,7 +2263,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2219,7 +2271,7 @@ Clonar libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2227,7 +2279,7 @@ Exportar borrador como ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2235,7 +2287,7 @@ ¿Estás seguro de eliminar esta operación? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2251,7 +2303,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2286,14 +2338,6 @@ 56 - - Get started - Comenzar - - apps/client/src/app/components/header/header.component.html - 432 - - This feature is currently unavailable. Esta funcionalidad no está disponible actualmente. @@ -2311,7 +2355,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2327,7 +2371,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2421,6 +2465,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Minimum Price @@ -2455,7 +2503,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2571,7 +2619,7 @@ Benchmark apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -2599,7 +2647,7 @@ Excluido del análisis apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2671,14 +2719,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2705,14 +2753,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Symbol @@ -2745,21 +2793,21 @@ Tag Etiqueta - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Efectivo apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -2802,6 +2850,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Bono @@ -2863,7 +2919,7 @@ Fondo de emergencia apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2883,7 +2939,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2903,11 +2959,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3011,7 +3067,7 @@ Mapeo de símbolos apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -3019,7 +3075,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3035,7 +3091,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3103,7 +3159,7 @@ Validando datos... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3162,8 +3218,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3187,7 +3243,7 @@ Importar Dividendos apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3195,7 +3251,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3299,11 +3355,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3315,11 +3371,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3331,11 +3387,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3347,11 +3403,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3399,7 +3455,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3415,15 +3471,11 @@ Transacciones ilimitadas apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3431,15 +3483,11 @@ Cuentas ilimitadas apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3447,15 +3495,11 @@ Rendimiento del Portfolio apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3463,7 +3507,7 @@ Auto alojado, actualiza manualmente. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3471,11 +3515,11 @@ Gratis apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3483,55 +3527,35 @@ Para nuevos inversores que estan empezando con el trading. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 Fully managed Ghostfolio cloud offering. Oferta en la nube de Ghostfolio totalmente administrada. - apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Para inversores ambiciosos que necesitan una visión completa de sus activos financieros - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Pago único, sin renovación automática. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Empieza - - apps/client/src/app/pages/landing/landing-page.html - 42 + apps/client/src/app/pages/pricing/pricing-page.html + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Para inversores ambiciosos que necesitan una visión completa de sus activos financieros apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Pago único, sin renovación automática. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3539,7 +3563,7 @@ Es gratis. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3551,7 +3575,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3567,11 +3591,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3587,15 +3611,11 @@ Importacion y exportacion de datos apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3611,7 +3631,7 @@ Soporte de la comunidad apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3619,7 +3639,7 @@ Soporte a Traves de Email y Chat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3663,7 +3683,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3687,7 +3707,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3703,7 +3723,7 @@ Suplantar usuario apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3711,7 +3731,7 @@ Eliminar usuario apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3719,7 +3739,7 @@ ¿Realmente deseas eliminar estas actividades? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3759,11 +3779,11 @@ ¿La URL? apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3958,12 +3978,24 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Pasivos apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4119,7 +4151,7 @@ Configuración del scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4350,6 +4382,14 @@ 43 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETFs sin países @@ -4371,7 +4411,7 @@ Activos apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4551,7 +4591,7 @@ Estrellas en GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4563,7 +4603,7 @@ Descargas en Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4683,7 +4723,7 @@ Usuarios activos mensuales apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4691,7 +4731,7 @@ Visto en apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4699,7 +4739,7 @@ Protege tus assets. Mejora tu estrategia de inversión personal. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4707,7 +4747,7 @@ Ghostfolio permite a las personas ocupadas hacer un seguimiento de acciones, ETFs o criptomonedas sin ser rastreadas. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4715,7 +4755,7 @@ Vista 360° apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4723,7 +4763,7 @@ Preparado para Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4731,7 +4771,7 @@ Usa Ghostfolio de forma anónima y sé dueño de tus datos financieros. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4739,7 +4779,7 @@ Disfruta de mejoras continuas gracias a una comunidad sólida. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4755,7 +4795,7 @@ ¿Por qué Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4763,7 +4803,7 @@ Ghostfolio es para ti si estás... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4771,7 +4811,7 @@ operando con acciones, ETFs o criptomonedas en múltiples plataformas apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4779,7 +4819,7 @@ persiguiendo una compra & mantener estrategia apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4787,7 +4827,7 @@ interesado en obtener información sobre la composición de tu portafolio apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4795,7 +4835,7 @@ valorando la privacidad y la propiedad de tus datos apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4803,7 +4843,7 @@ en el minimalismo apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4811,7 +4851,7 @@ preocuparse por diversificar tus recursos financieros apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4819,7 +4859,7 @@ interesado en la independencia financiera apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4827,7 +4867,7 @@ diciendo no a las hojas de cálculo en apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4835,7 +4875,7 @@ todavía leyendo esta lista apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4843,7 +4883,7 @@ Más información sobre Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4851,7 +4891,7 @@ Lo que nuestros usuarios están diciendo apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4859,7 +4899,7 @@ Miembros de todo el mundo están usando Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4867,7 +4907,7 @@ ¿Cómo Ghostfolio work? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4875,7 +4915,7 @@ Regístrate de forma anónima* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4883,7 +4923,7 @@ * no se requiere dirección de correo electrónico ni tarjeta de crédito apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4891,7 +4931,7 @@ Agrega cualquiera de tus transacciones históricas apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4899,7 +4939,7 @@ Obtén información valiosa sobre la composición de tu portafolio apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4907,7 +4947,7 @@ ¿Estás listo? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4915,7 +4955,7 @@ Obtén una visión completa de tus finanzas personales en múltiples plataformas. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4923,7 +4963,7 @@ Comienza en solo 3 pasos apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5229,7 +5269,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5301,7 +5341,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5395,6 +5435,10 @@ Membership Membresía + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5409,7 +5453,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5521,7 +5565,7 @@ Oops, el saldo de efectivo no se ha transferido. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5553,7 +5597,7 @@ ¡Ups! No se pudieron analizar los datos históricos. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5605,7 +5649,7 @@ ¿Realmente desea eliminar el saldo de esta cuenta? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5629,7 +5673,7 @@ Prueba apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5637,7 +5681,7 @@ Rango de fechas libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5669,7 +5713,7 @@ ¡Ups! No se pudo otorgar acceso. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5713,7 +5757,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5733,7 +5777,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5789,7 +5841,7 @@ Semana hasta la fecha libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5801,7 +5853,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5809,7 +5861,7 @@ Mes hasta la fecha libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5821,7 +5873,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5829,7 +5881,7 @@ El año hasta la fecha libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5852,12 +5904,12 @@ 8 - + Reset Filters Reiniciar filtros libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5877,7 +5929,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5889,15 +5941,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Aplicar filtros libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5905,7 +5957,7 @@ Recopilación de datos apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6066,7 +6118,7 @@ Eliminar actividades libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6122,7 +6174,15 @@ Únete ahora o consulta la cuenta de ejemplo apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6298,7 +6358,7 @@ Código abierto apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6397,6 +6457,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canadá @@ -6538,7 +6606,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6554,7 +6622,7 @@ Inactiva apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6566,7 +6634,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6618,7 +6686,7 @@ Cerca apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6657,6 +6725,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes @@ -6665,14 +6741,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Copiar enlace al portapapeles @@ -6702,7 +6770,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6745,14 +6813,6 @@ 69 - - No auto-renewal. - Sin renovación automática. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Este año @@ -6814,7 +6874,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7066,13 +7126,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key No se pudo generar una clave API apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7080,7 +7144,7 @@ Configure esta clave API en su entorno autohospedado: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7088,7 +7152,7 @@ Clave API del proveedor de datos premium de Ghostfolio apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7096,7 +7160,7 @@ ¿Realmente desea generar una nueva clave API? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7136,7 +7200,7 @@ Ahorrar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7156,7 +7220,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7188,7 +7252,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7252,7 +7316,7 @@ Precio de mercado por defecto apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7260,7 +7324,7 @@ Modo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7268,7 +7332,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7276,7 +7340,7 @@ Encabezados de solicitud HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7320,7 +7384,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7340,11 +7404,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7440,11 +7504,11 @@ Token de seguridad apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7452,7 +7516,7 @@ ¿Realmente deseas generar un nuevo token de seguridad para este usuario? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7460,7 +7524,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7468,7 +7532,7 @@ Generar token de seguridad apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7549,7 +7613,7 @@ con acceso a la API para apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7629,7 +7693,7 @@ ¿Realmente deseas eliminar este elemento? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7729,15 +7793,7 @@ 158 - - Name - Nombre - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Enlaces rápidos @@ -7745,24 +7801,16 @@ 58 - - Asset Profiles - Perfiles de activos - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Demostración en vivo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7858,17 +7906,25 @@ Limited Offer! ¡Oferta limitada! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Obtén extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8061,7 +8117,7 @@ ¿Realmente deseas generar un nuevo token de seguridad? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8117,7 +8173,7 @@ Gestionar perfil de activo apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8141,7 +8197,7 @@ Precio medio por unidad apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8532,6 +8588,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Siga a Ghostfolio en LinkedIn diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index aee10e4f8..81da3848d 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -34,7 +34,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -54,7 +54,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -81,6 +81,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Voulez-vous vraiment révoquer cet accès ? @@ -150,7 +158,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -202,7 +214,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -254,11 +266,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -294,11 +306,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -330,11 +342,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -346,7 +358,7 @@ Voulez-vous vraiment supprimer ce compte ? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -432,6 +444,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -442,7 +458,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -490,7 +506,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -624,6 +640,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -634,7 +654,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -654,7 +674,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -666,7 +686,7 @@ Équivalence de Symboles apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -674,7 +694,7 @@ Note apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -830,7 +850,7 @@ Voulez-vous vraiment supprimer cet·te utilisateur·rice ? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -845,6 +865,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Engagement par Jour @@ -852,6 +880,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -898,7 +930,7 @@ Référence apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -925,14 +957,6 @@ 5 - - Get started - Démarrer - - apps/client/src/app/components/header/header.component.html - 432 - - Sign in Se connecter @@ -942,7 +966,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -962,11 +986,11 @@ Oups! Jeton de Sécurité Incorrect. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1046,7 +1070,7 @@ Jeton de Sécurité apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -1078,15 +1102,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1098,31 +1122,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Se connecter avec Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Se connecter avec Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -1130,7 +1146,7 @@ Rester connecté apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -1146,7 +1162,7 @@ Performance Absolue Brute apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -1154,7 +1170,7 @@ Performance Absolue Nette apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -1162,7 +1178,7 @@ Performance nette apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -1170,7 +1186,7 @@ Actifs Totaux apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -1178,7 +1194,7 @@ Pouvoir d’Achat apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -1186,7 +1202,7 @@ Exclus de l’Analyse apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -1194,7 +1210,7 @@ Fortune apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -1202,7 +1218,7 @@ Performance annualisée apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -1210,7 +1226,7 @@ Veuillez entrer le montant de votre fonds d’urgence : apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -1242,7 +1258,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1254,7 +1270,7 @@ Signaler une Erreur de Données apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1266,7 +1282,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1278,7 +1294,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1290,7 +1306,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1302,7 +1318,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1314,7 +1330,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1338,7 +1354,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -1350,7 +1366,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -1358,7 +1374,7 @@ D’accord apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1366,7 +1382,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1438,7 +1454,7 @@ Veuillez entrer votre code promotionnel. apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1446,7 +1462,7 @@ Le code promotionnel n’a pas pu être appliqué apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1454,7 +1470,7 @@ Le code promotionnel a été appliqué apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1462,7 +1478,7 @@ Rafraîchir apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1486,7 +1502,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1494,7 +1510,7 @@ Essayer Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1502,7 +1518,7 @@ Utiliser un Code Promotionnel apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1534,7 +1550,7 @@ Paramètres régionaux apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1612,6 +1628,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1660,6 +1680,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1668,6 +1692,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1824,6 +1852,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1904,6 +1936,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -1920,6 +1956,10 @@ Markets Marchés + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1992,6 +2032,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -2038,7 +2082,7 @@ Vente apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -2074,7 +2118,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -2082,7 +2126,7 @@ Import des données... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -2090,7 +2134,7 @@ L’import est terminé apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2106,7 +2150,7 @@ Validation des données... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -2310,7 +2354,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -2326,7 +2370,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -2417,14 +2461,6 @@ 7 - - Holdings - Positions - - libs/ui/src/lib/assistant/assistant.html - 110 - - Pricing Prix @@ -2492,14 +2528,34 @@ Get Started Démarrer + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Registration @@ -2522,7 +2578,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -2533,20 +2589,12 @@ 101 - - Continue with Internet Identity - Continue avec Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Continuer avec Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2646,7 +2694,7 @@ Brouillon libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2654,7 +2702,7 @@ Importer Activités apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2662,7 +2710,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2674,7 +2722,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2686,7 +2734,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2694,7 +2742,7 @@ Dupliquer libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2702,7 +2750,7 @@ Exporter Brouillon sous ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2710,7 +2758,7 @@ Voulez-vous vraiment supprimer cette activité ? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2726,7 +2774,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2762,7 +2810,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2818,14 +2866,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2852,14 +2900,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -2894,7 +2942,7 @@ Fonds d’Urgence apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2914,7 +2962,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2948,21 +2996,21 @@ Tag Étiquette - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Cash apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -3005,6 +3053,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Obligation @@ -3142,11 +3198,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3161,8 +3217,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3186,7 +3242,7 @@ Importer Dividendes apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3194,7 +3250,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3298,11 +3354,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3314,11 +3370,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3330,11 +3386,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3346,11 +3402,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3398,7 +3454,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3414,15 +3470,11 @@ Transactions Illimitées apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3430,15 +3482,11 @@ Comptes illimités apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3446,15 +3494,11 @@ Performance du Portefeuille apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3462,7 +3506,7 @@ Hébergé localement, mises à jour manuelles. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3470,11 +3514,11 @@ Gratuit apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3482,7 +3526,7 @@ Pour les nouveaux investisseurs qui débutent en Bourse. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -3490,47 +3534,27 @@ Offre Ghostfolio cloud complètement administrée. apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Pour les investisseurs ambitieux qui ont besoin d’une vue complète de leurs actifs financiers. - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Paiement unique, sans auto-renouvellement. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Démarrer - - apps/client/src/app/pages/landing/landing-page.html - 42 + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Pour les investisseurs ambitieux qui ont besoin d’une vue complète de leurs actifs financiers. apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Paiement unique, sans auto-renouvellement. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3538,7 +3562,7 @@ C’est gratuit. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3550,7 +3574,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3566,11 +3590,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3586,15 +3610,11 @@ Import et Export de Données apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3610,7 +3630,7 @@ Support de la Communauté apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3618,7 +3638,7 @@ Support par E-mail et Tchat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3662,7 +3682,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3686,7 +3706,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3702,7 +3722,7 @@ Voir en tant que ... apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3710,7 +3730,7 @@ Supprimer l’Utilisateur apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3718,7 +3738,7 @@ Voulez-vous vraiment supprimer toutes vos activités ? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3758,11 +3778,11 @@ Lien apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3957,12 +3977,24 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Dettes apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4118,7 +4150,7 @@ Configuration du Scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4349,6 +4381,14 @@ 43 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETF sans Pays @@ -4370,7 +4410,7 @@ Actifs apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4550,7 +4590,7 @@ Etoiles sur GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4562,7 +4602,7 @@ Téléchargement depuis Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4682,7 +4722,7 @@ Utilisateurs actifs mensuels apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4690,7 +4730,7 @@ Médias apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4698,7 +4738,7 @@ Protégez vos actifs. Affinez votre stratégie d’investissement personnelle.. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4706,7 +4746,7 @@ Ghostfolio permet aux personnes occupées de suivre ses actions, ETF ou cryptomonnaies sans être pistées. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4714,7 +4754,7 @@ Vision 360° apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4722,7 +4762,7 @@ Compatible Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4730,7 +4770,7 @@ Utilisez Ghostfolio de manière anonyme et soyez propriétaire de vos données financières. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4738,7 +4778,7 @@ Bénéficiez d’améliorations continues grâce à une communauté impliquée. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4754,7 +4794,7 @@ Pourquoi Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4762,7 +4802,7 @@ Ghostfolio est pour vous si vous ... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4770,7 +4810,7 @@ tradez des actions, ETFs or crypto-monnaies sur plusieurs plateforme. apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4778,7 +4818,7 @@ adoptez une stratégie Buy & and Hold apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4786,7 +4826,7 @@ êtes intéressés d’avoir un aperçu de la composition de votre portefeuille apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4794,7 +4834,7 @@ valorisez la confidentialité et la propriété de vos données apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4802,7 +4842,7 @@ êtes minimaliste apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4810,7 +4850,7 @@ vous souciez de diversifier vos ressources financières apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4818,7 +4858,7 @@ êtes intéressés d’atteindre l’indépendance financière apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4826,7 +4866,7 @@ dites non aux feuilles de calcul en apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4834,7 +4874,7 @@ continuez à lire cette liste apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4842,7 +4882,7 @@ En appendre plus sur Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4850,7 +4890,7 @@ Qu’en pensent nos utilisateurs apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4858,7 +4898,7 @@ Les utilisateurs du monde entier utilisent Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4866,7 +4906,7 @@ Comment fonctionne Ghostfolio ? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4874,7 +4914,7 @@ Inscrivez-vous de manière anonyme* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4882,7 +4922,7 @@ * aucune adresse mail ni carte de crédit requise apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4890,7 +4930,7 @@ Ajoutez l’une de vos transactions historiques apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4898,7 +4938,7 @@ Obtenez de précieuses informations sur la composition de votre portefeuille apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4906,7 +4946,7 @@ Êtes- vous prêts ? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4914,7 +4954,7 @@ Obtenez une vue d’ensemble de vos finances personnelles sur plusieurs plateformes. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4922,7 +4962,7 @@ Démarrer en seulement 3 étapes apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5228,7 +5268,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5300,7 +5340,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5394,6 +5434,10 @@ Membership Statut + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5408,7 +5452,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5520,7 +5564,7 @@ Oops, échec du transfert de la cash balance. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5552,7 +5596,7 @@ Oops! Echec du parsing des données historiques. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5604,7 +5648,7 @@ Voulez-vous vraiment supprimer ce solde de compte ? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5628,7 +5672,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5636,7 +5680,7 @@ Intervalle de Date libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5668,7 +5712,7 @@ Oops! Impossible d’accorder l’accès. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5712,7 +5756,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5732,7 +5776,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5788,7 +5840,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5800,7 +5852,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5808,7 +5860,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5820,7 +5872,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5828,7 +5880,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5851,12 +5903,12 @@ 8 - + Reset Filters Réinitialiser les Filtres libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5876,7 +5928,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5888,15 +5940,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Appliquer les Filtres libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5904,7 +5956,7 @@ Collecter les données apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6065,7 +6117,7 @@ Supprimer les Activitées libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6121,7 +6173,15 @@ Rejoindre ou voir un compte démo apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6297,7 +6357,7 @@ Open Source apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canada @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Inactif apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Fermer apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Oui @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Copier le lien dans le presse-papiers @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - Pas de renouvellement automatique. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Cette année @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Impossible de générer une clé API apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Définissez cette clé API dans votre environnement auto-hébergé : apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Clé API du fournisseur de données Ghostfolio Premium apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Voulez-vous vraiment générer une nouvelle clé API ? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Sauvegarder apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Prix du marché par défaut apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Selecteur apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ En-têtes de requête HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Jeton de sécurité apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Voulez-vous vraiment générer un nouveau jeton de sécurité pour cet utilisateur ? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Générer un jeton de sécurité apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ avec accès API pour apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Voulez-vous vraiment supprimer cet élément? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Nom - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Liens rapides @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Profils d’Actifs - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Démo Live apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Offre Limitée ! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Obtenez supplémentaires + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Voulez-vous vraiment générer un nouveau jeton de sécurité? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Gérer le profil d’actif apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Average Unit Price apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Follow Ghostfolio on LinkedIn diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 8afbbba6b..6618d2463 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -11,7 +11,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -43,7 +43,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -63,7 +63,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -90,6 +90,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Vuoi davvero revocare l’accesso concesso? @@ -143,7 +151,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -199,11 +211,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -239,11 +251,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -275,11 +287,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -291,7 +303,7 @@ Vuoi davvero eliminare questo account? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -377,6 +389,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -387,7 +403,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -435,7 +451,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -623,7 +639,7 @@ Vuoi davvero eliminare questo utente? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -638,6 +654,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Partecipazione giornaliera @@ -645,6 +669,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -677,14 +705,34 @@ Get Started Inizia + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Sign in @@ -695,7 +743,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -715,11 +763,11 @@ Ops! Token di sicurezza errato. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -751,7 +799,7 @@ Token di sicurezza apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -783,15 +831,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -803,31 +851,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Accedi con Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Accedi con Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -835,7 +875,7 @@ Rimani connesso apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -851,7 +891,7 @@ Prestazioni lorde assolute apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -859,7 +899,7 @@ Prestazioni nette assolute apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -867,7 +907,7 @@ Prestazioni nette apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -875,7 +915,7 @@ Asset totali apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -883,7 +923,7 @@ Potere d’acquisto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -891,7 +931,7 @@ Patrimonio netto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -899,7 +939,7 @@ Prestazioni annualizzate apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -907,7 +947,7 @@ Inserisci l’importo del tuo fondo di emergenza: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -919,7 +959,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -939,7 +979,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -967,7 +1007,7 @@ Segnala un’anomalia dei dati apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1007,7 +1047,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1019,7 +1059,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1031,7 +1071,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1043,7 +1083,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1055,7 +1095,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1063,7 +1103,7 @@ Bene apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1071,7 +1111,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1131,7 +1171,7 @@ Inserisci il tuo codice del buono: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1139,7 +1179,7 @@ Impossibile riscattare il codice del buono apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1147,7 +1187,7 @@ Il codice del buono è stato riscattato apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1155,7 +1195,7 @@ Ricarica apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1179,7 +1219,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1187,7 +1227,7 @@ Prova Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1195,7 +1235,7 @@ Riscatta il buono apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1219,7 +1259,7 @@ Locale apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1265,6 +1305,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1313,6 +1357,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1321,6 +1369,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1367,7 +1419,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -1521,6 +1573,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1617,6 +1673,10 @@ Markets Mercati + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1817,10 +1877,6 @@ libs/common/src/lib/routes/routes.ts 167 - - - Holdings - Partecipazioni libs/ui/src/lib/assistant/assistant.html 110 @@ -1859,7 +1915,7 @@ Vendi apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -1899,7 +1955,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1915,7 +1971,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -1923,7 +1979,7 @@ Nota apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1965,6 +2021,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -1983,7 +2043,7 @@ Importazione dei dati... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -1991,7 +2051,7 @@ L’importazione è stata completata apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2094,20 +2154,12 @@ 281 - - Continue with Internet Identity - Continua con Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Continua con Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2171,7 +2223,7 @@ Bozza libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2179,7 +2231,7 @@ Importa le attività apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2187,7 +2239,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2199,7 +2251,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2211,7 +2263,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2219,7 +2271,7 @@ Clona libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2227,7 +2279,7 @@ Esporta la bozza come ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2235,7 +2287,7 @@ Vuoi davvero eliminare questa attività? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2251,7 +2303,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2286,14 +2338,6 @@ 56 - - Get started - Inizia - - apps/client/src/app/components/header/header.component.html - 432 - - This feature is currently unavailable. Questa funzionalità non è attualmente disponibile. @@ -2311,7 +2355,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2327,7 +2371,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2421,6 +2465,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Minimum Price @@ -2455,7 +2503,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2571,7 +2619,7 @@ Benchmark apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -2599,7 +2647,7 @@ Escluso dall’analisi apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2671,14 +2719,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2705,14 +2753,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Symbol @@ -2745,21 +2793,21 @@ Tag Etichetta - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Contanti apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -2802,6 +2850,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Obbligazioni @@ -2863,7 +2919,7 @@ Fondo di emergenza apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2883,7 +2939,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2903,11 +2959,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3011,7 +3067,7 @@ Mappatura dei simboli apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -3019,7 +3075,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3035,7 +3091,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3103,7 +3159,7 @@ Convalida dei dati... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3162,8 +3218,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3187,7 +3243,7 @@ Importa i dividendi apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3195,7 +3251,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3299,11 +3355,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3315,11 +3371,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3331,11 +3387,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3347,11 +3403,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3399,7 +3455,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3415,15 +3471,11 @@ Transazioni illimitate apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3431,15 +3483,11 @@ Account illimitati apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3447,15 +3495,11 @@ Prestazioni del portafoglio apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3463,7 +3507,7 @@ Self-hosted, aggiornamento manuale. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3471,11 +3515,11 @@ Free apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3483,55 +3527,35 @@ Per i nuovi investitori che hanno appena iniziato a fare trading. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 Fully managed Ghostfolio cloud offering. Offerta cloud Ghostfolio completamente gestita. - apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Per gli investitori ambiziosi che hanno bisogno di un quadro completo dei propri asset finanziari. - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Pagamento una tantum, senza rinnovo automatico. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Inizia - - apps/client/src/app/pages/landing/landing-page.html - 42 + apps/client/src/app/pages/pricing/pricing-page.html + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Per gli investitori ambiziosi che hanno bisogno di un quadro completo dei propri asset finanziari. apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Pagamento una tantum, senza rinnovo automatico. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3539,7 +3563,7 @@ È gratuito. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3551,7 +3575,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3567,11 +3591,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3587,15 +3611,11 @@ Importazione ed esportazione dei dati apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3611,7 +3631,7 @@ Supporto della comunità apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3619,7 +3639,7 @@ Supporto via email e chat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3663,7 +3683,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3687,7 +3707,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3703,7 +3723,7 @@ Imita l’utente apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3711,7 +3731,7 @@ Elimina l’utente apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3719,7 +3739,7 @@ Vuoi davvero eliminare tutte le tue attività? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3759,11 +3779,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3958,12 +3978,24 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Passività apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4119,7 +4151,7 @@ Configurazione dello scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4350,6 +4382,14 @@ 43 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETF senza paesi @@ -4371,7 +4411,7 @@ Asset apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4551,7 +4591,7 @@ Stelle su GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4563,7 +4603,7 @@ Estrazioni su Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4683,7 +4723,7 @@ Utenti attivi mensili apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4691,7 +4731,7 @@ Come si vede su apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4699,7 +4739,7 @@ Proteggi i tuoi asset. Perfeziona la tua strategia di investimento personale. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4707,7 +4747,7 @@ Ghostfolio permette alle persone impegnate di tenere traccia di azioni, ETF o criptovalute senza essere tracciate. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4715,7 +4755,7 @@ Vista a 360° apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4723,7 +4763,7 @@ Pronto per il Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4731,7 +4771,7 @@ Usa Ghostfolio in modo anonimo e possiedi i tuoi dati finanziari. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4739,7 +4779,7 @@ Beneficia dei continui miglioramenti grazie a una forte comunità. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4755,7 +4795,7 @@ Perché Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4763,7 +4803,7 @@ Ghostfolio è per te se... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4771,7 +4811,7 @@ fai trading di azioni, ETF o criptovalute su più piattaforme apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4779,7 +4819,7 @@ persegui una strategia buy & hold apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4787,7 +4827,7 @@ sei interessato a conoscere la composizione del tuo portafoglio apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4795,7 +4835,7 @@ valorizzi la privacy e la proprietà dei dati apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4803,7 +4843,7 @@ sei per il minimalismo apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4811,7 +4851,7 @@ ti interessa diversificare le tue risorse finanziarie apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4819,7 +4859,7 @@ sei interessato all’indipendenza finanziaria apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4827,7 +4867,7 @@ non vuoi utilizzare il foglio elettronico nel apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4835,7 +4875,7 @@ stai ancora leggendo questo elenco apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4843,7 +4883,7 @@ Ulteriori informazioni su Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4851,7 +4891,7 @@ Cosa dicono i nostri utenti apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4859,7 +4899,7 @@ Membri da tutto il mondo utilizzano Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4867,7 +4907,7 @@ Come funziona Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4875,7 +4915,7 @@ Iscriviti in modo anonimo* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4883,7 +4923,7 @@ * non è richiesto alcun indirizzo email né la carta di credito apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4891,7 +4931,7 @@ Aggiungi le tue transazioni storiche apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4899,7 +4939,7 @@ Ottieni informazioni preziose sulla composizione del tuo portafoglio apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4907,7 +4947,7 @@ Sei pronto? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4915,7 +4955,7 @@ Ottieni un quadro completo delle tue finanze personali su più piattaforme. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4923,7 +4963,7 @@ Inizia in soli 3 passi apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5229,7 +5269,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5301,7 +5341,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5395,6 +5435,10 @@ Membership Iscrizione + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5409,7 +5453,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5521,7 +5565,7 @@ Ops, il trasferimento del saldo di cassa è fallito. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5553,7 +5597,7 @@ Ops! Impossibile elaborare i dati storici. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5605,7 +5649,7 @@ Vuoi veramente elimnare il saldo di questo conto? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5629,7 +5673,7 @@ Prova apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5637,7 +5681,7 @@ Intervallo di date libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5669,7 +5713,7 @@ Ops! Impossibile abilitare l’accesso. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5713,7 +5757,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5733,7 +5777,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5789,7 +5841,7 @@ Da inizio settimana libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5801,7 +5853,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5809,7 +5861,7 @@ Da inizio mese libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5821,7 +5873,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5829,7 +5881,7 @@ Da inizio anno libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5852,12 +5904,12 @@ 8 - + Reset Filters Reset Filtri libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5877,7 +5929,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5889,15 +5941,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Applica i Filtri libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5905,7 +5957,7 @@ Raccolta Dati apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6066,7 +6118,7 @@ Elimina le attività libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6122,7 +6174,15 @@ Registrati adesso o prova l’account demo apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6298,7 +6358,7 @@ Open Source apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6397,6 +6457,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canada @@ -6538,7 +6606,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6554,7 +6622,7 @@ Inattivo apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6566,7 +6634,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6618,7 +6686,7 @@ Chiudi apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6657,6 +6725,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Si @@ -6665,14 +6741,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Copia link negli appunti @@ -6702,7 +6770,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6745,14 +6813,6 @@ 69 - - No auto-renewal. - No rinnovo automatico. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Anno corrente @@ -6814,7 +6874,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7066,13 +7126,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Non è stato possibile generare un API key apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7080,7 +7144,7 @@ Imposta questa API key nel tuo ambiente self-hosted: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7088,7 +7152,7 @@ API Key for Ghostfolio Premium Data Provider apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7096,7 +7160,7 @@ Vuoi davvero generare una nuova API key? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7136,7 +7200,7 @@ Salva apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7156,7 +7220,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7188,7 +7252,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7252,7 +7316,7 @@ Prezzo di mercato predefinito apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7260,7 +7324,7 @@ Modalità apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7268,7 +7332,7 @@ Selettore apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7276,7 +7340,7 @@ Intestazioni della richiesta HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7320,7 +7384,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7340,11 +7404,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7440,11 +7504,11 @@ Token di sicurezza apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7452,7 +7516,7 @@ Vuoi davvero generare un nuovo token di sicurezza per questo utente? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7460,7 +7524,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7468,7 +7532,7 @@ Genera Token di Sicurezza apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7549,7 +7613,7 @@ con accesso API per apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7629,7 +7693,7 @@ Vuoi davvero eliminare questo elemento? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7729,15 +7793,7 @@ 158 - - Name - Nome - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Collegamenti rapidi @@ -7745,24 +7801,16 @@ 58 - - Asset Profiles - Profili delle risorse - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Dimostrazione dal vivo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7858,17 +7906,25 @@ Limited Offer! Offerta limitata! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Get extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8061,7 +8117,7 @@ Vuoi davvero generare un nuovo token di sicurezza? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8117,7 +8173,7 @@ Gestisci profilo risorsa apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8141,7 +8197,7 @@ Average Unit Price apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8532,6 +8588,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Follow Ghostfolio on LinkedIn diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 24917c846..43f22714b 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -10,7 +10,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -42,7 +42,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -62,7 +62,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -89,6 +89,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Wil je deze verleende toegang echt intrekken? @@ -142,7 +150,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -198,11 +210,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -238,11 +250,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -274,11 +286,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -290,7 +302,7 @@ Wil je deze rekening echt verwijderen? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -376,6 +388,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -386,7 +402,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -434,7 +450,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -622,7 +638,7 @@ Wilt je deze gebruiker echt verwijderen? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -637,6 +653,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Betrokkenheid per dag @@ -644,6 +668,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -676,14 +704,34 @@ Get Started Aan de slag + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Sign in @@ -694,7 +742,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -714,11 +762,11 @@ Oeps! Onjuiste beveiligingstoken. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -750,7 +798,7 @@ Beveiligingstoken apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -782,15 +830,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -802,31 +850,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Aanmelden met Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Aanmelden met Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -834,7 +874,7 @@ Aangemeld blijven apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -850,7 +890,7 @@ Absoluut bruto rendement apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -858,7 +898,7 @@ Absoluut netto rendement apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -866,7 +906,7 @@ Netto rendement apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -874,7 +914,7 @@ Totaal Activa apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -882,7 +922,7 @@ Koopkracht apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -890,7 +930,7 @@ Netto waarde apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -898,7 +938,7 @@ Rendement per jaar apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -906,7 +946,7 @@ Voer het bedrag van je noodfonds in: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -918,7 +958,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -938,7 +978,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -966,7 +1006,7 @@ Gegevensstoring melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1006,7 +1046,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1018,7 +1058,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1030,7 +1070,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1042,7 +1082,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1054,7 +1094,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1062,7 +1102,7 @@ Oké apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1070,7 +1110,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1130,7 +1170,7 @@ Voer je couponcode in: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1138,7 +1178,7 @@ Kon je kortingscode niet inwisselen apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1146,7 +1186,7 @@ Je couponcode is ingewisseld apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1154,7 +1194,7 @@ Herladen apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1178,7 +1218,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1186,7 +1226,7 @@ Probeer Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1194,7 +1234,7 @@ Coupon inwisselen apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1218,7 +1258,7 @@ Locatie apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1264,6 +1304,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1312,6 +1356,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1320,6 +1368,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1366,7 +1418,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -1520,6 +1572,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1616,6 +1672,10 @@ Markets Markten + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1816,10 +1876,6 @@ libs/common/src/lib/routes/routes.ts 167 - - - Holdings - Posities libs/ui/src/lib/assistant/assistant.html 110 @@ -1858,7 +1914,7 @@ Verkopen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -1898,7 +1954,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1914,7 +1970,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -1922,7 +1978,7 @@ Opmerking apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1964,6 +2020,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -1982,7 +2042,7 @@ Gegevens importeren... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -1990,7 +2050,7 @@ Importeren is voltooid apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2093,20 +2153,12 @@ 281 - - Continue with Internet Identity - Ga verder met Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Verder met Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2170,7 +2222,7 @@ Concept libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2178,7 +2230,7 @@ Activiteiten importeren apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2186,7 +2238,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2198,7 +2250,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2210,7 +2262,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2218,7 +2270,7 @@ Kloon libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2226,7 +2278,7 @@ Concept exporteren als ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2234,7 +2286,7 @@ Wil je deze activiteit echt verwijderen? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2250,7 +2302,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2285,14 +2337,6 @@ 56 - - Get started - Aan de slag - - apps/client/src/app/components/header/header.component.html - 432 - - This feature is currently unavailable. Deze functie is momenteel niet beschikbaar. @@ -2310,7 +2354,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2326,7 +2370,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2420,6 +2464,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Minimum Price @@ -2454,7 +2502,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2570,7 +2618,7 @@ Benchmark apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -2598,7 +2646,7 @@ Uitgesloten van analyse apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2670,14 +2718,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2704,14 +2752,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Symbol @@ -2744,21 +2792,21 @@ Tag Label - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Contant geld apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -2801,6 +2849,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Obligatie @@ -2862,7 +2918,7 @@ Noodfonds apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2882,7 +2938,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2902,11 +2958,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3010,7 +3066,7 @@ Symbool toewijzen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -3018,7 +3074,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3034,7 +3090,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3102,7 +3158,7 @@ Gegevens valideren... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3161,8 +3217,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3186,7 +3242,7 @@ Importeer dividenden apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3194,7 +3250,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3298,11 +3354,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3314,11 +3370,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3330,11 +3386,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3346,11 +3402,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3398,7 +3454,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3414,15 +3470,11 @@ Onbeperkte transacties apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3430,15 +3482,11 @@ Onbeperkte rekeningen apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3446,15 +3494,11 @@ Portefeuilleprestaties apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3462,7 +3506,7 @@ Zelf hosten, handmatig bijwerken. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3470,11 +3514,11 @@ Gratis apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3482,55 +3526,35 @@ Voor nieuwe beleggers die net beginnen met handelen. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 Fully managed Ghostfolio cloud offering. Volledig beheerd Ghostfolio cloud-aanbod. - apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Voor ambitieuze beleggers die een volledig beeld willen hebben van hun financiële assets. - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Eenmalige betaling, geen automatische verlenging. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Aan de slag - - apps/client/src/app/pages/landing/landing-page.html - 42 + apps/client/src/app/pages/pricing/pricing-page.html + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Voor ambitieuze beleggers die een volledig beeld willen hebben van hun financiële assets. apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Eenmalige betaling, geen automatische verlenging. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3538,7 +3562,7 @@ Het is gratis. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3550,7 +3574,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3566,11 +3590,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3586,15 +3610,11 @@ Data Import and Export apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3610,7 +3630,7 @@ Steun van de Gemeenschap apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3618,7 +3638,7 @@ Ondersteuning via e-mail en chat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3662,7 +3682,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3686,7 +3706,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3702,7 +3722,7 @@ Gebruiker immiteren apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3710,7 +3730,7 @@ Gebruiker verwijderen apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3718,7 +3738,7 @@ Weet je zeker dat je alle activiteiten wilt verwijderen? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3758,11 +3778,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3957,12 +3977,24 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Verplichtingen apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4118,7 +4150,7 @@ Scraper instellingen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4349,6 +4381,14 @@ 43 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETF’s zonder Landen @@ -4370,7 +4410,7 @@ Assets apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4550,7 +4590,7 @@ Sterren op GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4562,7 +4602,7 @@ Pulls op Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4682,7 +4722,7 @@ Maandelijkse actieve gebruikers apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4690,7 +4730,7 @@ Zoals te zien in apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4698,7 +4738,7 @@ Bescherm je financiële bezittingen. Verfijn je persoonlijke investeringsstrategie. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4706,7 +4746,7 @@ Ghostfolio stelt drukbezette mensen in staat om aandelen, ETF’s of cryptocurrencies bij te houden zonder gevolgd te worden. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4714,7 +4754,7 @@ 360°-overzicht apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4722,7 +4762,7 @@ Klaar voor Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4730,7 +4770,7 @@ Gebruik Ghostfolio anoniem en bezit je financiële gegevens. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4738,7 +4778,7 @@ Profiteer van voortdurende verbeteringen door een sterke gemeenschap. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4754,7 +4794,7 @@ Waarom Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4762,7 +4802,7 @@ Ghostfolio is iets voor je als je... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4770,7 +4810,7 @@ handelt in aandelen, ETF’s of cryptocurrencies op meerdere platforms apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4778,7 +4818,7 @@ streeft naar een buy & hold strategie apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4786,7 +4826,7 @@ geïnteresseerd bent in het krijgen van inzicht in je portefeuillesamenstelling apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4794,7 +4834,7 @@ privacy en eigendom van gegevens waardeert apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4802,7 +4842,7 @@ houdt van een minimalistisch ontwerp apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4810,7 +4850,7 @@ zorgdraagt voor het diversifiëren van je financiële middelen apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4818,7 +4858,7 @@ geïnteresseerd bent in financiële onafhankelijkheid apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4826,7 +4866,7 @@ "nee" zegt tegen spreadsheets in apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4834,7 +4874,7 @@ nog steeds deze lijst aan het lezen bent apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4842,7 +4882,7 @@ Leer meer over Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4850,7 +4890,7 @@ Wat onze gebruikers zeggen apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4858,7 +4898,7 @@ Leden van over de hele wereld gebruikenGhostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4866,7 +4906,7 @@ Hoe Ghostfolio werkt? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4874,7 +4914,7 @@ Anoniem aanmelden* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4882,7 +4922,7 @@ * geen e-mailadres of creditcard nodig apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4890,7 +4930,7 @@ Voeg al je historische transacties toe apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4898,7 +4938,7 @@ Krijg waardevolle inzichten in de samenstelling van je portefeuille apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4906,7 +4946,7 @@ Ben jij er klaar voor? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4914,7 +4954,7 @@ Krijg een volledig beeld van je persoonlijke financiën op meerdere platforms. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4922,7 +4962,7 @@ Aan de slag in slechts 3 stappen apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5228,7 +5268,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5300,7 +5340,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5394,6 +5434,10 @@ Membership Lidmaatschap + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5408,7 +5452,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5520,7 +5564,7 @@ Oeps, geldoverdracht is mislukt. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5552,7 +5596,7 @@ Oeps! Ophalen van historische data is mislukt. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5604,7 +5648,7 @@ Wilt u dit rekeningsaldo echt verwijderen? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5628,7 +5672,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5636,7 +5680,7 @@ Datumbereik libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5668,7 +5712,7 @@ Oeps! Kan geen toegang verlenen. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5712,7 +5756,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5732,7 +5776,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5788,7 +5840,7 @@ Week tot nu toe libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5800,7 +5852,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5808,7 +5860,7 @@ Maand tot nu toe libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5820,7 +5872,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5828,7 +5880,7 @@ Jaar tot nu toe libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5851,12 +5903,12 @@ 8 - + Reset Filters Filters Herstellen libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5876,7 +5928,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5888,15 +5940,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Filters Toepassen libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5904,7 +5956,7 @@ Data Verzamelen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6065,7 +6117,7 @@ Verwijder Activiteiten libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6121,7 +6173,15 @@ Word nu lid of bekijk het voorbeeldaccount apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6297,7 +6357,7 @@ Open Source apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canada @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Inactief apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Sluiten apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Ja @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Kopieer link naar klembord @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - Geen automatische verlenging. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Dit jaar @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Er kon geen API-sleutel worden gegenereerd apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Stel deze API-sleutel in uw zelf-gehoste omgeving in: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Ghostfolio Premium Gegevensleverancier API-sleutel apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Wilt u echt een nieuwe API-sleutel genereren? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Opslaan apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Standaard Marktprijs apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Modus apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Kiezer apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ HTTP Verzoek Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Beveiligingstoken apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Wilt u echt een nieuw beveiligingstoken voor deze gebruiker aanmaken? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Beveiligingstoken Aanmaken apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ met API toegang tot apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Wilt u dit item echt verwijderen? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Naam - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Snelle koppelingen @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Activaprofielen - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Live-demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Beperkt aanbod! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Krijg extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Wilt u echt een nieuwe securitytoken genereren? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Beheer activaprofiel apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Gemiddelde eenheidsprijs apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Volg Ghostfolio op LinkedIn diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index 56beabb5f..1ae972c8e 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -183,7 +183,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -243,7 +243,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -263,7 +263,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -290,6 +290,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Czy na pewno chcesz cofnąć przyznany dostęp? @@ -387,7 +395,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -439,7 +451,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -475,11 +487,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -515,11 +527,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -551,11 +563,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -567,7 +579,7 @@ Czy na pewno chcesz usunąć to konto? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -587,7 +599,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -711,7 +723,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -746,6 +758,14 @@ 96 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETF-y bez Krajów @@ -851,7 +871,7 @@ Ups! Nie udało się sparsować danych historycznych. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -905,6 +925,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -915,7 +939,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -935,7 +959,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -947,7 +971,7 @@ Mapowanie Symboli apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -963,7 +987,7 @@ Konfiguracja Scrapera apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -971,7 +995,7 @@ Notatka apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1179,11 +1203,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1287,7 +1311,7 @@ Czy na pewno chcesz usunąć tego użytkownika? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1302,6 +1326,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Zaangażowanie na Dzień @@ -1309,6 +1341,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -1323,7 +1359,7 @@ Wciel się w Użytkownika apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1331,7 +1367,7 @@ Usuń Użytkownika apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1379,7 +1415,7 @@ Poziom Odniesienia (Benchmark) apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1414,14 +1450,6 @@ 5 - - Get started - Rozpocznij - - apps/client/src/app/components/header/header.component.html - 432 - - Sign in Zaloguj się @@ -1431,7 +1459,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -1451,11 +1479,11 @@ Ups! Nieprawidłowy token bezpieczeństwa. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1635,7 +1663,7 @@ Token Bezpieczeństwa apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -1667,15 +1695,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1687,31 +1715,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Zaloguj się przy użyciu Tożsamości Internetowej (Internet Identity) - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Zaloguj się przez Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -1719,7 +1739,7 @@ Pozostań zalogowany apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -1735,7 +1755,7 @@ Bezwzględne Osiągi Brutto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -1747,7 +1767,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -1755,7 +1775,7 @@ Bezwzględne Osiągi Netto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -1763,7 +1783,7 @@ Osiągi Netto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -1771,7 +1791,7 @@ Suma Aktywów apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -1779,7 +1799,7 @@ Aktywa apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -1787,7 +1807,7 @@ Siła Nabywcza apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -1795,7 +1815,7 @@ Wykluczone z Analizy apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -1803,7 +1823,7 @@ Pasywa (Zobowiązania Finansowe) apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -1815,7 +1835,7 @@ Wartość Netto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -1823,7 +1843,7 @@ Osiągi w Ujęciu Rocznym apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -1831,7 +1851,7 @@ Wprowadź wysokość funduszu rezerwowego: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -1863,7 +1883,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1875,7 +1895,7 @@ Zgłoś Błąd Danych apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1903,11 +1923,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -1923,11 +1943,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -1939,11 +1959,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -1955,11 +1975,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -1971,7 +1991,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -1983,11 +2003,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -2043,7 +2063,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -2055,7 +2075,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -2067,7 +2087,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -2079,7 +2099,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -2091,7 +2111,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -2103,7 +2123,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -2135,7 +2155,7 @@ Wpisz kod kuponu: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -2143,7 +2163,7 @@ Nie udało się zrealizować kodu kuponu apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -2151,7 +2171,7 @@ Kupon został zrealizowany apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -2159,7 +2179,7 @@ Odśwież apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -2175,7 +2195,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -2183,7 +2203,7 @@ Wypróbuj Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -2191,7 +2211,7 @@ Wykorzystaj kupon apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -2251,7 +2271,7 @@ Ustawienia Regionalne apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2353,6 +2373,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Export Data @@ -2383,7 +2407,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2395,7 +2419,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2403,7 +2427,7 @@ Okej apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -2411,7 +2435,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -2541,6 +2565,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -2549,13 +2577,17 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Oops, cash balance transfer has failed. Ups, transfer salda nie powiódł się. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -2789,6 +2821,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -2961,14 +2997,34 @@ Get Started Rozpocznij + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Holdings @@ -2993,6 +3049,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -3009,6 +3069,10 @@ Markets Rynki + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -3098,32 +3162,12 @@ 11 - - Get Started - Rozpocznij - - apps/client/src/app/pages/landing/landing-page.html - 42 - - - apps/client/src/app/pages/landing/landing-page.html - 346 - - - apps/client/src/app/pages/pricing/pricing-page.html - 378 - - - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 - - Monthly Active Users Aktywni Użytkownicy w Miesiącu apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -3131,7 +3175,7 @@ Gwiazdki na GitHubie apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -3143,7 +3187,7 @@ Pobrania na Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -3155,7 +3199,7 @@ Dostrzegli Nas apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -3163,7 +3207,7 @@ Chroń swoje zasoby. Udoskonal swoją osobistą strategię inwestycyjną. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -3171,7 +3215,7 @@ Ghostfolio umożliwia zapracowanym osobom śledzenie akcji, funduszy ETF lub kryptowalut, jednocześnie zachowując prywatność. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -3179,7 +3223,7 @@ Widok 360° apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -3187,7 +3231,7 @@ Uzyskaj pełny obraz swoich finansów osobistych na wielu różnych platformach. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -3195,7 +3239,7 @@ Gotowy na Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -3203,7 +3247,7 @@ Korzystaj z Ghostfolio anonimowo i zachowaj pełną kontrolę nad swoimi danymi finansowymi. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -3211,7 +3255,7 @@ Czerp korzyści z nieustannych ulepszeń dzięki silnej społeczności. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -3227,7 +3271,7 @@ Dlaczego Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -3235,7 +3279,7 @@ Ghostfolio jest dla Ciebie, jeśli... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -3243,7 +3287,7 @@ handlujesz akcjami, funduszami ETF lub kryptowalutami na wielu platformach apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -3251,7 +3295,7 @@ realizujesz strategię buy & hold apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -3259,7 +3303,7 @@ chcesz uzyskać wgląd w strukturę swojego portfolio apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -3267,7 +3311,7 @@ cenisz sobie prywatność i własność swoich danych apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -3275,7 +3319,7 @@ lubisz minimalizm apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -3283,7 +3327,7 @@ zależy Ci na dywersyfikacji swoich zasobów finansowych apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -3291,7 +3335,7 @@ jesteś zainteresowany niezależnością finansową apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -3299,7 +3343,7 @@ mówisz „nie” arkuszom kalkulacyjnym w roku apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -3307,7 +3351,7 @@ nadal czytasz tę listę apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -3315,7 +3359,7 @@ Dowiedz się więcej o Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -3323,7 +3367,7 @@ Co mówią nasi użytkownicy apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -3331,7 +3375,7 @@ Użytkownicy z całego świata korzystają z Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -3339,7 +3383,7 @@ Jak działa Ghostfolio ? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -3347,7 +3391,7 @@ Rozpocznij w zaledwie 3 krokach apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -3363,7 +3407,7 @@ Zarejestruj się anonimowo* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -3371,7 +3415,7 @@ * nie jest wymagany ani adres e-mail, ani karta kredytowa apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -3379,7 +3423,7 @@ Dodaj dowolne z Twoich historycznych transakcji apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -3387,7 +3431,7 @@ Zyskaj cenny wgląd w strukturę swojego portfolio apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -3395,7 +3439,7 @@ Czy jesteś gotów? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -3427,7 +3471,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -3541,6 +3585,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -3559,7 +3607,7 @@ Czy na pewno chcesz usunąć te aktywności? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3639,7 +3687,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -3647,7 +3695,7 @@ Importuj Aktywności apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3655,7 +3703,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -3663,7 +3711,7 @@ Impotruj Dywidendy apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3671,7 +3719,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3679,7 +3727,7 @@ Importowanie danych... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -3687,7 +3735,7 @@ Importowanie zakończone apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -3703,7 +3751,7 @@ Weryfikacja danych... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3730,8 +3778,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3975,7 +4023,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3991,7 +4039,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -4106,14 +4154,6 @@ 7 - - Holdings - Inwestycje - - libs/ui/src/lib/assistant/assistant.html - 110 - - Pricing Cennik @@ -4183,15 +4223,11 @@ Nieograniczona Liczba Transakcji apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -4199,15 +4235,11 @@ Nieograniczona Liczba Rachunków apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -4215,15 +4247,11 @@ Wyniki portfela apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -4231,15 +4259,11 @@ Importowanie i Eksportowanie Danych apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -4247,7 +4271,7 @@ Wsparcie Społeczności apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -4255,7 +4279,7 @@ Samodzielny hosting, aktualizacja ręczna. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -4263,11 +4287,11 @@ Bezpłatnie apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -4275,7 +4299,7 @@ Dla początkujących inwestorów, którzy dopiero zaczynają swoją przygodę z tradingiem. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -4283,11 +4307,11 @@ W pełni zarządzana oferta Ghostfolio w chmurze. apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 @@ -4295,7 +4319,7 @@ Dla ambitnych inwestorów, którzy potrzebują pełnego obrazu swoich aktywów finansowych. apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -4303,7 +4327,7 @@ Wsparcie przez E-mail i Czat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -4319,7 +4343,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -4327,7 +4351,7 @@ Płatność jednorazowa, bez automatycznego odnawiania. apps/client/src/app/pages/pricing/pricing-page.html - 303 + 288 @@ -4335,7 +4359,7 @@ Jest bezpłatny. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -4382,20 +4406,12 @@ 281 - - Continue with Internet Identity - Kontynuuj z tożsamością internetową - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Zaloguj z Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -4730,6 +4746,10 @@ Membership Członkostwo + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -4744,7 +4764,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -4800,7 +4820,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -4812,7 +4832,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -4820,7 +4840,7 @@ Przygotuj Wstępną Wersję libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -4828,7 +4848,7 @@ Sklonuj libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -4836,7 +4856,7 @@ Eksportuj Wersję Roboczą jako ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -4844,7 +4864,7 @@ Czy na pewno chcesz usunąć tę działalność? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -4854,6 +4874,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + , @@ -4884,7 +4908,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -4944,7 +4968,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -5000,14 +5024,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -5042,14 +5066,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -5116,7 +5140,7 @@ Fundusz Rezerwowy apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -5192,7 +5216,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -5250,14 +5274,14 @@ Tag Tag - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Year @@ -5267,6 +5291,18 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years Lata @@ -5296,7 +5332,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5324,7 +5360,7 @@ Sprzedaj apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -5336,7 +5372,7 @@ Gotówka apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -5379,6 +5415,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Obligacja @@ -5552,11 +5596,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -5604,7 +5648,7 @@ Czy na pewno chcesz usunąć saldo tego konta? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5628,7 +5672,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5636,7 +5680,7 @@ Zakres Dat libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5668,7 +5712,7 @@ Ups! Nie udało się przyznać dostępu. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5712,7 +5756,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5732,7 +5776,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5788,7 +5840,7 @@ Dotychczasowy tydzień libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5800,7 +5852,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5808,7 +5860,7 @@ Od początku miesiąca libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5820,7 +5872,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5828,7 +5880,7 @@ Od początku roku libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5851,12 +5903,12 @@ 8 - + Reset Filters Resetuj Filtry libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5876,7 +5928,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5888,15 +5940,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Zastosuj Filtry libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5904,7 +5956,7 @@ Gromadzenie Danych apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6065,7 +6117,7 @@ Usuń aktywności libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6121,7 +6173,15 @@ Dołącz teraz lub sprawdź przykładowe konto apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6297,7 +6357,7 @@ Otwarty Kod Źródłowy apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Kanada @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Nieaktywny apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Zamknij apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Tak @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Kopiuj link do schowka @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - Bez automatycznego odnawiania. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year W tym roku @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Nie udało się wygenerować klucza API apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Ustaw ten klucz API w samodzielnie hostowanym środowisku: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Klucz API dostawcy danych Premium Ghostfolio apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Czy na pewno chcesz wygenerować nowy klucz API? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Zapisz apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Domyślna cena rynkowa apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Tryb apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Selektor apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ Nagłówki żądań HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Token bezpieczeństwa apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Czy napewno chcesz wygenerować nowy token bezpieczeństwa dla tego użytkownika? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Generowanie Tokena Zabezpieczającego apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ z dostępem API dla apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Czy na pewno chcesz usunąć ten element? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Nazwa - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Szybkie linki @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Profile zasobów - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Demonstracja na żywo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Oferta ograniczona czasowo! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Uzyskaj dodatkowo + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Czy na pewno chcesz wygenerować nowy token bezpieczeństwa? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Zarządzaj profilem aktywów apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Średnia cena jednostkowa apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Follow Ghostfolio on LinkedIn diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index a27440a63..9c928731a 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -34,7 +34,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -54,7 +54,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -81,6 +81,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Pretende realmente revogar este acesso concedido? @@ -150,7 +158,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -202,7 +214,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -254,11 +266,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -294,11 +306,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -330,11 +342,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -346,7 +358,7 @@ Pretende realmente eliminar esta conta? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -432,6 +444,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -442,7 +458,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -490,7 +506,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -702,7 +718,7 @@ Deseja realmente excluir este utilizador? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -717,6 +733,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Envolvimento por Dia @@ -724,6 +748,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -770,7 +798,7 @@ Referência apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -797,14 +825,6 @@ 5 - - Get started - Começar - - apps/client/src/app/components/header/header.component.html - 432 - - Sign in Iniciar sessão @@ -814,7 +834,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -834,11 +854,11 @@ Oops! Token de Segurança Incorreto. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -926,7 +946,7 @@ Token de Segurança apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -958,15 +978,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -978,31 +998,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - Iniciar sessão com Internet Identity - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Iniciar sessão com Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -1010,7 +1022,7 @@ Manter sessão iniciada apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -1026,7 +1038,7 @@ Desempenho Bruto Absoluto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -1034,7 +1046,7 @@ Desempenho Líquido Absoluto apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -1042,7 +1054,7 @@ Desempenho Líquido apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -1050,7 +1062,7 @@ Ativos Totais apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -1058,7 +1070,7 @@ Poder de Compra apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -1066,7 +1078,7 @@ Excluído da Análise apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -1074,7 +1086,7 @@ Valor Líquido apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -1082,7 +1094,7 @@ Desempenho Anual apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -1090,7 +1102,7 @@ Por favor, insira o valor do seu fundo de emergência: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -1122,7 +1134,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1156,6 +1168,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -1166,7 +1182,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1186,7 +1202,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1214,7 +1230,7 @@ Dados do Relatório com Problema apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1254,7 +1270,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1266,7 +1282,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1278,7 +1294,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1290,7 +1306,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1302,7 +1318,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1326,7 +1342,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -1338,7 +1354,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -1346,7 +1362,7 @@ OK apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -1354,7 +1370,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -1426,7 +1442,7 @@ Por favor, insira o seu código de cupão: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -1434,7 +1450,7 @@ Não foi possível resgatar o código de cupão apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -1442,7 +1458,7 @@ Código de cupão foi resgatado apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -1450,7 +1466,7 @@ Atualizar apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -1474,7 +1490,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -1482,7 +1498,7 @@ Experimentar Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -1490,7 +1506,7 @@ Resgatar Cupão apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -1530,7 +1546,7 @@ Localidade apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1608,6 +1624,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Granted Access @@ -1656,6 +1676,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -1664,6 +1688,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -1800,6 +1828,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -1896,6 +1928,10 @@ Markets Mercados + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -1968,6 +2004,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -2014,7 +2054,7 @@ Venda apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -2050,7 +2090,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -2058,7 +2098,7 @@ Nota apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -2074,7 +2114,7 @@ A importar dados... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -2082,7 +2122,7 @@ A importação foi concluída apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -2352,10 +2392,6 @@ libs/common/src/lib/routes/routes.ts 167 - - - Holdings - Posições libs/ui/src/lib/assistant/assistant.html 110 @@ -2428,14 +2464,34 @@ Get Started Começar + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Registration @@ -2458,7 +2514,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -2469,20 +2525,12 @@ 101 - - Continue with Internet Identity - Continuar com Internet Identity - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Continuar com Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -2546,7 +2594,7 @@ Rascunho libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -2554,7 +2602,7 @@ Importar Atividades apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -2562,7 +2610,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -2574,7 +2622,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -2586,7 +2634,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -2594,7 +2642,7 @@ Clonar libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -2602,7 +2650,7 @@ Exportar Rascunho como ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -2610,7 +2658,7 @@ Deseja realmente eliminar esta atividade? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -2626,7 +2674,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -2662,7 +2710,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -2690,14 +2738,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -2724,21 +2772,21 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Emergency Fund Fundo de Emergência apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -2758,7 +2806,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -2792,21 +2840,21 @@ Tag Marcador - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Cash Dinheiro apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -2849,6 +2897,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Obrigação @@ -2986,11 +3042,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -3018,7 +3074,7 @@ Mapeamento de Símbolo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -3058,7 +3114,7 @@ A validar dados... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3082,7 +3138,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3098,7 +3154,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3161,8 +3217,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3186,7 +3242,7 @@ Importar Dividendos apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3194,7 +3250,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3298,11 +3354,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3314,11 +3370,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3330,11 +3386,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3346,11 +3402,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3398,7 +3454,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -3414,15 +3470,11 @@ Transações Ilimitadas apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3430,15 +3482,11 @@ Contas Ilimitadas apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3446,15 +3494,11 @@ Desempenho do Portefólio apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3462,7 +3506,7 @@ Hospedado localmente, atualização manual. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3470,11 +3514,11 @@ Grátis apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3482,55 +3526,35 @@ Para novos investidores que estão a começar a investir agora. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 Fully managed Ghostfolio cloud offering. Ghostfolio hospedado na nuvem, totalmente gerido. - apps/client/src/app/pages/pricing/pricing-page.html - 144 - - - apps/client/src/app/pages/pricing/pricing-page.html - 270 - - - - For ambitious investors who need the full picture of their financial assets. - Para investidores ambiciosos que precisam de ter uma visão completa de seus ativos financeiros. - - apps/client/src/app/pages/pricing/pricing-page.html - 187 - - - - One-time payment, no auto-renewal. - Pagamento único, sem renovação automática. - - apps/client/src/app/pages/pricing/pricing-page.html - 303 - - - - Get Started - Começar - - apps/client/src/app/pages/landing/landing-page.html - 42 + apps/client/src/app/pages/pricing/pricing-page.html + 150 - apps/client/src/app/pages/landing/landing-page.html - 346 + apps/client/src/app/pages/pricing/pricing-page.html + 255 + + + For ambitious investors who need the full picture of their financial assets. + Para investidores ambiciosos que precisam de ter uma visão completa de seus ativos financeiros. apps/client/src/app/pages/pricing/pricing-page.html - 378 + 193 + + + One-time payment, no auto-renewal. + Pagamento único, sem renovação automática. - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 + apps/client/src/app/pages/pricing/pricing-page.html + 288 @@ -3538,7 +3562,7 @@ É gratuito. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3550,7 +3574,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -3566,11 +3590,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3586,15 +3610,11 @@ Importação e Exportação de Dados apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3610,7 +3630,7 @@ Suporte da Comunidade apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3618,7 +3638,7 @@ Suporte por Email e Chat apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3662,7 +3682,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3686,7 +3706,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3702,7 +3722,7 @@ Personificar Utilizador apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -3710,7 +3730,7 @@ Apagar Utilizador apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -3718,7 +3738,7 @@ Deseja mesmo eliminar estas atividades? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3758,11 +3778,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -3957,12 +3977,24 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Liabilities Responsabilidades apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -4118,7 +4150,7 @@ Configuração do raspador apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -4349,6 +4381,14 @@ 43 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETFs sem países @@ -4370,7 +4410,7 @@ Ativos apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -4550,7 +4590,7 @@ Estrelas no GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4562,7 +4602,7 @@ Não puxa Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4682,7 +4722,7 @@ Usuários ativos mensais apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -4690,7 +4730,7 @@ Como visto em apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -4698,7 +4738,7 @@ Proteja o seu assets. Refine your personal investment strategy. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -4706,7 +4746,7 @@ O Ghostfolio permite que pessoas ocupadas acompanhem ações, ETFs ou criptomoedas sem serem rastreadas. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -4714,7 +4754,7 @@ 360° visualizar apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -4722,7 +4762,7 @@ Web3 Preparar apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -4730,7 +4770,7 @@ Use o Ghostfolio anonimamente e possua seus dados financeiros. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4738,7 +4778,7 @@ Beneficie-se de melhorias contínuas através de uma comunidade forte. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -4754,7 +4794,7 @@ Por que Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -4762,7 +4802,7 @@ Ghostfolio é para você se você for... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -4770,7 +4810,7 @@ negociar ações, ETFs ou criptomoedas em múltiplas plataformas apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -4778,7 +4818,7 @@ buscando uma compra & estratégia de retenção apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -4786,7 +4826,7 @@ interessado em obter insights sobre a composição do seu portfólio apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -4794,7 +4834,7 @@ valorizando a privacidade e a propriedade dos dados apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -4802,7 +4842,7 @@ no minimalismo apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -4810,7 +4850,7 @@ preocupando-se em diversificar seus recursos financeiros apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -4818,7 +4858,7 @@ interessado em independência financeira apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -4826,7 +4866,7 @@ dizendo não às planilhas em apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -4834,7 +4874,7 @@ ainda lendo esta lista apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -4842,7 +4882,7 @@ Saiba mais sobre o Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -4850,7 +4890,7 @@ Qual é o nosso users are saying apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -4858,7 +4898,7 @@ Membros de todo o mundo estão usando Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4866,7 +4906,7 @@ Como é que Ghostfolio work? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4874,7 +4914,7 @@ Inscreva-se anonimamente* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4882,7 +4922,7 @@ * no e-mail address nor credit card required apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4890,7 +4930,7 @@ Adicione qualquer uma de suas transações históricas apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4898,7 +4938,7 @@ Obtenha insights valiosos sobre a composição do seu portfólio apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4906,7 +4946,7 @@ São you preparar? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4914,7 +4954,7 @@ Tenha uma visão completa das suas finanças pessoais em diversas plataformas. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -4922,7 +4962,7 @@ Comece em apenas 3 passos apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -5228,7 +5268,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5300,7 +5340,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5394,6 +5434,10 @@ Membership Associação + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5408,7 +5452,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5520,7 +5564,7 @@ Ops, a transferência do saldo em dinheiro falhou. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5552,7 +5596,7 @@ Ops! Não foi possível analisar os dados históricos. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5604,7 +5648,7 @@ Você realmente deseja excluir o saldo desta conta? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5628,7 +5672,7 @@ Teste apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5636,7 +5680,7 @@ Período libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5668,7 +5712,7 @@ Ops! Não foi possível conceder acesso. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5712,7 +5756,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5732,7 +5776,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5788,7 +5840,7 @@ Semana até agora libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5800,7 +5852,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5808,7 +5860,7 @@ Do mês até a data libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5820,7 +5872,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5828,7 +5880,7 @@ No acumulado do ano libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5851,12 +5903,12 @@ 8 - + Reset Filters Redefinir filtros libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5876,7 +5928,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5888,15 +5940,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Aplicar filtros libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5904,7 +5956,7 @@ Coleta de dados apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6065,7 +6117,7 @@ Excluir atividades libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6121,7 +6173,15 @@ Cadastre-se agora ou confira a conta de exemplo apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6297,7 +6357,7 @@ Código aberto apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Canadá @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Inativo apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Fechar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Sim @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Copiar link para a área de transferência @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - Sem renovação automática. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Este ano @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key Não foi possível gerar uma chave de API apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Defina esta chave de API no seu ambiente auto-hospedado: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Chave de API do Provedor de Dados do Ghostfolio Premium apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Você realmente deseja gerar uma nova chave de API? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Guardar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Preço de mercado padrão apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Security token apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Do you really want to generate a new security token for this user? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Generate Security Token apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ with API access for apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Do you really want to delete this item? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Nome - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Links rápidos @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Perfis de ativos - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Live Demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Limited Offer! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Get extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Do you really want to generate a new security token? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Gerenciar perfil de ativos apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Preço médio unitário apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Siga o Ghostfolio no LinkedIn diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index b548c0e20..810f91cfa 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -215,7 +215,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -235,7 +235,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -262,6 +262,14 @@ 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Bu erişim iznini geri almayı gerçekten istiyor musunuz? @@ -347,7 +355,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -399,7 +411,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -435,11 +447,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -475,11 +487,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -511,11 +523,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -527,7 +539,7 @@ Bu hesabı silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -613,6 +625,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Historical Market Data @@ -623,7 +639,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -671,7 +687,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -706,6 +722,14 @@ 96 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries Ülkesi Olmayan ETF’ler @@ -833,6 +857,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -843,7 +871,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -863,7 +891,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -875,7 +903,7 @@ Sembol Eşleştirme apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -891,7 +919,7 @@ Veri Toplayıcı Yapılandırması apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -899,7 +927,7 @@ Not apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1091,11 +1119,11 @@ Url apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1151,7 +1179,7 @@ Bu kullanıcıyı silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1166,6 +1194,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day Günlük etkileşim @@ -1173,6 +1209,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -1187,7 +1227,7 @@ Kullanıcıyı Taklit Et apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1195,7 +1235,7 @@ Kullanıcıyı Sil apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1243,7 +1283,7 @@ Karşılaştırma Ölçütü apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1270,14 +1310,6 @@ 5 - - Get started - Haydi Başlayalım - - apps/client/src/app/components/header/header.component.html - 432 - - Sign in Giriş @@ -1287,7 +1319,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -1307,11 +1339,11 @@ Hay Allah! Güvenlik anahtarı yanlış. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1491,7 +1523,7 @@ Güvenlik Jetonu apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -1523,15 +1555,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1543,31 +1575,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - İnternet Kimliği (Internet Identity) ile Oturum Aç - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Google ile Oturum Aç apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -1575,7 +1599,7 @@ Oturumu açık tut apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -1591,7 +1615,7 @@ Toplam Brüt Performans apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -1599,7 +1623,7 @@ Toplam Net Performans apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -1607,7 +1631,7 @@ Net Performans apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -1615,7 +1639,7 @@ Toplam Varlıklar apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -1623,7 +1647,7 @@ Varlıklar apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -1631,7 +1655,7 @@ Alım Limiti apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -1639,7 +1663,7 @@ Analize Dahil Edilmemiştir. apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -1647,7 +1671,7 @@ Yükümlülükler apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -1659,7 +1683,7 @@ Toplam Varlık apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -1667,7 +1691,7 @@ Yıllıklandırılmış Performans apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -1675,7 +1699,7 @@ Lütfen acil durum yedeği meblağını giriniz: apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -1707,7 +1731,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1723,7 +1747,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -1731,7 +1755,7 @@ Rapor Veri Sorunu apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1759,11 +1783,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -1779,11 +1803,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -1795,11 +1819,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -1811,11 +1835,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -1827,7 +1851,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -1839,11 +1863,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -1899,7 +1923,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -1911,7 +1935,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -1923,7 +1947,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -1935,7 +1959,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -1947,7 +1971,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -1959,7 +1983,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -1983,7 +2007,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -1995,7 +2019,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2003,7 +2027,7 @@ Tamam apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -2011,7 +2035,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -2141,6 +2165,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -2149,6 +2177,10 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Update account @@ -2357,6 +2389,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -2541,14 +2577,34 @@ Get Started Başla + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Holdings @@ -2573,6 +2629,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -2589,6 +2649,10 @@ Markets Piyasalar + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -2654,32 +2718,12 @@ 11 - - Get Started - Başla - - apps/client/src/app/pages/landing/landing-page.html - 42 - - - apps/client/src/app/pages/landing/landing-page.html - 346 - - - apps/client/src/app/pages/pricing/pricing-page.html - 378 - - - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 - - Monthly Active Users Aylık Aktif Kullanıcılar apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -2687,7 +2731,7 @@ GitHub’daki Beğeniler apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -2699,7 +2743,7 @@ Docker Hub’ta Çekmeler apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -2711,7 +2755,7 @@ Şurada görüldüğü gibi apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -2719,7 +2763,7 @@ varlıklarınızı koruyun. Kişisel yatırım stratejinizi geliştirin. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -2727,7 +2771,7 @@ Ghostfolio, takip edilmeden hisse senetleri, ETF’ler veya kripto paraları izlemek isteyen yoğun insanlara güç verir. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -2735,7 +2779,7 @@ 360° Görünüm apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -2743,7 +2787,7 @@ Kişisel finansınızın tam resmini birden fazla platformda edinin. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -2751,7 +2795,7 @@ Web3 Hazır apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -2759,7 +2803,7 @@ Ghostfolio’yu anonim olarak kullanın ve finansal verilerinize sahip çıkın. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -2767,7 +2811,7 @@ Güçlü bir topluluk aracılığıyla sürekli gelişmelerden faydalanın. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -2783,7 +2827,7 @@ Neden Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -2791,7 +2835,7 @@ Ghostfolio tam size göre, apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -2799,7 +2843,7 @@ Birden fazla platformda hisse senedi, ETF veya kripto para ticareti yapıyorsanız, apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -2807,7 +2851,7 @@ al ve tut stratejisi izliyorsanız, apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -2815,7 +2859,7 @@ Portföy bileşimine dair içgörüleri edinmek istiyorsanız, apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -2823,7 +2867,7 @@ Gizliliğe ve verilerinize sahip çıkmayı önemsiyorsanız apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -2831,7 +2875,7 @@ minimalizme ilgi duyuyorsanız apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -2839,7 +2883,7 @@ finansal kaynaklarınızı çeşitlendirmeye önem veriyorsanız apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -2847,7 +2891,7 @@ finansal özgürlük peşindeyseniz apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -2855,7 +2899,7 @@ elektronik tablo uygulamalarına hayır diyorsanız apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -2863,7 +2907,7 @@ bu listeyi hala okuyorsanız apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -2871,7 +2915,7 @@ Ghostfolio hakkında daha fazla bilgi edinin apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -2879,7 +2923,7 @@ Kullanıcılarımızın görüşleri apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -2887,7 +2931,7 @@ Dünyanın dört bir yanındaki kullanıcılar Ghostfolio Premium kullanıyorlar. apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -2895,7 +2939,7 @@ NasılGhostfolio çalışır? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -2903,7 +2947,7 @@ Sadece 3 adımda başlayın apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -2919,7 +2963,7 @@ Anonim olarak kaydolun* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -2927,7 +2971,7 @@ * e-posta adresi veya kredi kartı gerekmez apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -2935,7 +2979,7 @@ Herhangi bir geçmiş işleminizi ekleyin apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -2943,7 +2987,7 @@ Portföy bileşiminizle ilgili değerli bilgiler edinin apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -2951,7 +2995,7 @@ Hazır mısınız? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -3045,6 +3089,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -3063,7 +3111,7 @@ Tüm işlemlerinizi silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3119,7 +3167,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -3127,7 +3175,7 @@ İşlemleri İçe Aktar apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3135,7 +3183,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -3143,7 +3191,7 @@ Temettüleri İçe Aktar apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3151,7 +3199,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3159,7 +3207,7 @@ Veri içe aktarılıyor... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -3167,7 +3215,7 @@ İçe aktarma tamamlandı apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -3183,7 +3231,7 @@ Veri doğrulanıyor... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3210,8 +3258,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3463,7 +3511,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -3479,7 +3527,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -3594,14 +3642,6 @@ 7 - - Holdings - Varlıklar - - libs/ui/src/lib/assistant/assistant.html - 110 - - Pricing Fiyatlandırma @@ -3671,15 +3711,11 @@ Sınırsız İşlem apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -3687,15 +3723,11 @@ Kısıtsız Hesaplar apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -3703,15 +3735,11 @@ Portföy Performansı apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -3719,15 +3747,11 @@ Veri İçe Aktarma ve Dışa Aktarma apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -3735,7 +3759,7 @@ Topluluk Desteği apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -3743,7 +3767,7 @@ Tarafınızca barındırılıyor, elle güncelleyiniz. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -3751,11 +3775,11 @@ Ücretsiz apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -3763,7 +3787,7 @@ Alım satıma henüz başlamış yeni yatırımcılar için. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -3771,11 +3795,11 @@ Eksiksiz yönetilen Ghostfolio bulut teklifi. apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 @@ -3783,7 +3807,7 @@ Finansal varlıklarının tamamını görmeye ihtiyaç duyan hırslı yatırımcılar için. apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -3791,7 +3815,7 @@ E-posta ve Sohbet Desteği apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -3807,7 +3831,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -3815,7 +3839,7 @@ Tek seferlik ödeme, otomatik yenileme yok. apps/client/src/app/pages/pricing/pricing-page.html - 303 + 288 @@ -3823,7 +3847,7 @@ Ücretsiz. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -3879,7 +3903,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -3890,20 +3914,12 @@ 101 - - Continue with Internet Identity - İnternet Kimliği ile Devam Et - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Google ile Devam Et apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -4264,7 +4280,7 @@ Lütfen kupon kodunuzu girin: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -4272,7 +4288,7 @@ Kupon kodu kullanılamadı apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -4280,7 +4296,7 @@ Kupon kodu kullanıldı apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -4288,7 +4304,7 @@ Yeniden Yükle apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -4324,7 +4340,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -4332,7 +4348,7 @@ Premium’u Deneyin apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -4340,7 +4356,7 @@ Kupon Kullan apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -4380,7 +4396,7 @@ Yerel Ayarlar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -4470,6 +4486,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Export Data @@ -4520,7 +4540,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -4532,7 +4552,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -4540,7 +4560,7 @@ Taslak libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -4548,7 +4568,7 @@ Klonla libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -4556,7 +4576,7 @@ Taslakları ICS Olarak Dışa Aktar libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -4564,7 +4584,7 @@ TBu işlemi silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -4580,7 +4600,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -4640,7 +4660,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -4696,14 +4716,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -4738,14 +4758,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -4812,7 +4832,7 @@ Acil Durum Fonu apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -4888,7 +4908,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -4946,14 +4966,14 @@ Tag Etiket - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Year @@ -4963,6 +4983,18 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years Yıl @@ -5004,7 +5036,7 @@ Sat apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -5016,7 +5048,7 @@ Nakit apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -5059,6 +5091,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Bono @@ -5196,11 +5236,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -5236,7 +5276,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -5308,7 +5348,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5394,6 +5434,10 @@ Membership Üyelik + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5408,7 +5452,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5520,7 +5564,7 @@ Hay Allah, Nakit bakiyesi tranferi başarısız oldu. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -5552,7 +5596,7 @@ Hay Allah! Geçmiş veriler ayrıştırılamadı. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -5604,7 +5648,7 @@ Bu nakit bakiyesini silmeyi gerçekten istiyor musunuz? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5628,7 +5672,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5636,7 +5680,7 @@ Tarih Aralığı libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5668,7 +5712,7 @@ Hay Allah! Erişim izni verilemedi. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -5712,7 +5756,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5732,7 +5776,15 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 + + + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 @@ -5788,7 +5840,7 @@ Hafta içi libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5800,7 +5852,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5808,7 +5860,7 @@ Ay içi libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5820,7 +5872,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5828,7 +5880,7 @@ Yıl içi libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5851,12 +5903,12 @@ 8 - + Reset Filters Filtreleri Sıfırla libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5876,7 +5928,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5888,15 +5940,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters Filtreleri Uygula libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5904,7 +5956,7 @@ Veri Toplama apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6065,7 +6117,7 @@ Etkinlikleri Sil libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6121,7 +6173,15 @@ Hemen katıl ya da örnek hesabı incele apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6297,7 +6357,7 @@ Açık Kaynak apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6396,6 +6456,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Kanada @@ -6537,7 +6605,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -6553,7 +6621,7 @@ Pasif apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6565,7 +6633,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6617,7 +6685,7 @@ Kapat apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6656,6 +6724,14 @@ 11 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Evet @@ -6664,14 +6740,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Bağlantıyı panoya kopyala @@ -6701,7 +6769,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6744,14 +6812,6 @@ 69 - - No auto-renewal. - Otomatik yenileme yok. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year Bu yıl @@ -6813,7 +6873,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -7065,13 +7125,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key API anahtarı oluşturulamadı apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7079,7 +7143,7 @@ Bu API anahtarını kendi barındırılan ortamınıza ayarlayın: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7087,7 +7151,7 @@ Ghostfolio Premium Veri Sağlayıcı API Anahtarı apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7095,7 +7159,7 @@ Yeni bir API anahtarı oluşturmak istediğinize emin misiniz? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7135,7 +7199,7 @@ Kaydet apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7155,7 +7219,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7187,7 +7251,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7251,7 +7315,7 @@ Varsayılan Piyasa Fiyatı apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Mod apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Seçici apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ HTTP İstek Başlıkları apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Güvenlik belirteci apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Bu kullanıcı için yeni bir güvenlik belirteci oluşturmak istediğinize emin misiniz? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Güvenlik belirteci oluştur apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ API erişimi için apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Bu öğeyi silmek istediğinize emin misiniz? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - İsim - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Hızlı Bağlantılar @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Varlık Profilleri - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Canlı Demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Sınırlı Teklif! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Get extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Do you really want to generate a new security token? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Manage Asset Profile apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Average Unit Price apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Ghostfolio’yu LinkedIn’de takip edin diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 7d7b278f4..24bc12dd1 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -38,7 +38,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -70,7 +70,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -295,7 +295,7 @@ please apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -342,14 +342,6 @@ 33 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard Скопіювати посилання в буфер обміну @@ -382,6 +374,14 @@ 101 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? Ви дійсно хочете відкликати цей наданий доступ? @@ -422,14 +422,6 @@ 86 - - Holdings - Активи - - libs/ui/src/lib/assistant/assistant.html - 110 - - Cash Balances Баланс готівки @@ -495,7 +487,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -547,7 +543,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -583,11 +579,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -623,11 +619,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -659,11 +655,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -675,7 +671,7 @@ Ви дійсно хочете видалити цей обліковий запис? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -695,7 +691,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -715,7 +711,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -862,6 +858,14 @@ 96 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries ETF без країн @@ -1053,6 +1057,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -1063,7 +1071,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1083,7 +1091,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1095,7 +1103,7 @@ Зіставлення символів apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 @@ -1111,7 +1119,7 @@ Конфігурація скребка apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -1119,7 +1127,7 @@ Тест apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -1127,11 +1135,11 @@ URL apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1147,7 +1155,7 @@ Примітка apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1303,7 +1311,7 @@ Збір даних apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -1503,15 +1511,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1523,11 +1531,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html @@ -1554,6 +1562,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Do you really want to delete this tag? Ви дійсно хочете видалити цей тег? @@ -1583,7 +1599,7 @@ Ви дійсно хочете видалити цього користувача? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1601,6 +1617,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + API Requests Today @@ -1609,6 +1629,10 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Last Request @@ -1623,7 +1647,7 @@ Видавати себе за користувача apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1631,7 +1655,7 @@ Видалити користувача apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1679,7 +1703,7 @@ Порівняльний показник apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1719,7 +1743,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -1735,7 +1759,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -1750,24 +1774,16 @@ 5 - - Get started - Почати - - apps/client/src/app/components/header/header.component.html - 432 - - Oops! Incorrect Security Token. Упс! Неправильний Секретний Токен. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1787,7 +1803,7 @@ If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -1827,7 +1843,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1851,7 +1867,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -1875,7 +1891,7 @@ Повідомити про збій даних apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -2083,7 +2099,7 @@ Секретний Токен apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -2106,20 +2122,12 @@ 72 - - Sign in with Internet Identity - Увійти з Інтернет-Ідентичністю - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google Увійти з Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -2127,7 +2135,7 @@ Залишатися в системі apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -2167,7 +2175,7 @@ Абсолютний валовий дохід apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -2175,7 +2183,7 @@ Абсолютний чистий прибуток apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -2183,7 +2191,7 @@ Чистий прибуток apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -2191,7 +2199,7 @@ Загальні активи apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -2199,7 +2207,7 @@ Активи apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -2207,7 +2215,7 @@ Купівельна спроможність apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -2215,7 +2223,7 @@ Виключено з аналізу apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -2223,7 +2231,7 @@ Зобов’язання apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -2235,7 +2243,7 @@ Чиста вартість apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -2243,7 +2251,7 @@ Річна доходність apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -2251,7 +2259,7 @@ Зберегти apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -2271,7 +2279,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -2299,7 +2307,7 @@ Будь ласка, встановіть суму вашого резервного фонду. apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -2363,7 +2371,7 @@ Oops! Could not update access. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 @@ -2391,11 +2399,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -2411,11 +2419,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -2427,11 +2435,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -2443,11 +2451,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -2459,7 +2467,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -2471,11 +2479,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -2523,7 +2531,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -2535,7 +2543,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -2547,7 +2555,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -2559,7 +2567,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -2571,7 +2579,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -2579,7 +2587,7 @@ Упс! Не вдалося надати доступ. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 @@ -2625,6 +2633,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Me @@ -2635,7 +2647,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -2667,7 +2679,7 @@ Не вдалося згенерувати ключ API apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -2675,7 +2687,7 @@ ОК apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -2683,7 +2695,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -2691,7 +2703,7 @@ Встановіть цей ключ API у вашому self-hosted середовищі: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -2699,7 +2711,7 @@ Ключ API Ghostfolio Premium Data Provider apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -2707,7 +2719,7 @@ Ви дійсно хочете згенерувати новий ключ API? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -2715,7 +2727,7 @@ Не вдалося обміняти код купона apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -2723,7 +2735,7 @@ Код купона був обміняний apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -2731,7 +2743,7 @@ Перезавантажити apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -2739,7 +2751,7 @@ Будь ласка, введіть ваш код купона. apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -2755,7 +2767,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -2763,7 +2775,7 @@ Спробуйте Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -2771,15 +2783,7 @@ Обміняти купон apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 - - - - No auto-renewal. - Без автоматичного поновлення. - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 + 66 @@ -2810,6 +2814,14 @@ 280 + + Include in + Include in + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 + + Oops! There was an error setting up biometric authentication. Упс! Виникла помилка під час налаштування біометричної автентифікації. @@ -2863,7 +2875,7 @@ Локалізація apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2999,7 +3011,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -3019,7 +3031,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -3157,6 +3169,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -3165,13 +3181,17 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Oops, cash balance transfer has failed. Упс, перенесення балансу готівки не вдалося. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -3421,6 +3441,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -3618,14 +3642,34 @@ Get Started Почати + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Holdings @@ -3650,6 +3694,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -3666,6 +3714,10 @@ Markets Ринки + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -3763,32 +3815,12 @@ 11 - - Get Started - Почати - - apps/client/src/app/pages/landing/landing-page.html - 42 - - - apps/client/src/app/pages/landing/landing-page.html - 346 - - - apps/client/src/app/pages/pricing/pricing-page.html - 378 - - - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 - - Monthly Active Users Щомісячні активні користувачі apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -3796,7 +3828,7 @@ Зірки на GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -3808,7 +3840,7 @@ Завантаження на Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -3820,7 +3852,7 @@ Як видно в apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -3828,7 +3860,7 @@ Захищайте свої активи. Вдосконалюйте власну інвестиційну стратегію. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -3836,7 +3868,7 @@ Ghostfolio допомагає зайнятим людям відстежувати акції, ETF або криптовалюти без ризику бути відстеженими. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -3844,7 +3876,7 @@ 360° огляд apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -3852,7 +3884,7 @@ Отримайте повну картину ваших особистих фінансів на різних платформах. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -3860,7 +3892,7 @@ Готовий до Web3 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -3868,7 +3900,7 @@ Використовуйте Ghostfolio анонімно та володійте своїми фінансовими даними. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -3876,7 +3908,7 @@ Отримуйте користь від постійних покращень завдяки сильній спільноті. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 @@ -3892,7 +3924,7 @@ Чому Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -3900,7 +3932,7 @@ Ghostfolio для вас, якщо ви... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -3908,7 +3940,7 @@ торгуєте акціями, ETF або криптовалютами на різних платформах apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -3916,7 +3948,7 @@ дотримуєтеся стратегії купівлі та утримання apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -3924,7 +3956,7 @@ вас цікавлять інсайти вашого складу портфеля apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -3932,7 +3964,7 @@ цінуєте конфіденційність і володіння даними apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -3940,7 +3972,7 @@ займаєтесь мінімалізмом apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -3948,7 +3980,7 @@ піклуєтесь про диверсифікацію ваших фінансових ресурсів apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -3956,7 +3988,7 @@ цікавитесь фінансовою незалежністю apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -3964,7 +3996,7 @@ кажете ні таблицям у apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -3972,7 +4004,7 @@ все ще читаєте цей список apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -3980,7 +4012,7 @@ Дізнайтеся більше про Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -3988,7 +4020,7 @@ Що говорять користувачі apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -3996,7 +4028,7 @@ Члени зі всього світу використовують Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -4004,7 +4036,7 @@ Як працює Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -4012,7 +4044,7 @@ Почніть всього за 3 кроки apps/client/src/app/pages/landing/landing-page.html - 285 + 284 @@ -4028,7 +4060,7 @@ Зареєструйтеся анонімно* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -4036,7 +4068,7 @@ * не потрібні електронна адреса та кредитна картка apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -4044,7 +4076,7 @@ Додайте будь-які з ваших історичних транзакцій apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -4052,7 +4084,7 @@ Отримуйте цінні інсайти вашого складу портфеля apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -4060,7 +4092,7 @@ Ви готові? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4068,7 +4100,7 @@ Приєднуйтесь зараз або перегляньте демонстраційний рахунок apps/client/src/app/pages/landing/landing-page.html - 334 + 333 @@ -4100,7 +4132,7 @@ with your university e-mail address apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -4214,6 +4246,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -4308,7 +4344,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -4324,7 +4360,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -4332,7 +4368,7 @@ Імпортувати активності apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -4340,7 +4376,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -4348,7 +4384,7 @@ Імпорт дивідендів apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -4356,7 +4392,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -4364,7 +4400,7 @@ Імпортуються дані... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -4372,7 +4408,7 @@ Імпорт завершено apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 @@ -4388,7 +4424,7 @@ Перевірка даних... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -4415,8 +4451,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -4692,7 +4728,7 @@ Looking for a student discount? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -4708,7 +4744,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -4736,7 +4772,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -4756,7 +4792,7 @@ here apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 @@ -4775,6 +4811,14 @@ 91 + + Close Holding + Close Holding + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 + + Absolute Asset Performance Абсолютна прибутковість активів @@ -4932,7 +4976,7 @@ Неактивний apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -5004,15 +5048,11 @@ Необмежені транзакції apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -5020,15 +5060,11 @@ Необмежена кількість рахунків apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -5036,15 +5072,11 @@ Прибутковість портфеля apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -5052,15 +5084,11 @@ Імпорт та експорт даних apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -5068,7 +5096,7 @@ Підтримка спільноти apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -5076,7 +5104,7 @@ Самохостинг, оновлення вручну. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -5084,11 +5112,11 @@ Безкоштовно apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -5096,7 +5124,7 @@ Для нових інвесторів, які тільки починають з торгівлі. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -5104,11 +5132,11 @@ Повністю керована хмарна пропозиція Ghostfolio. apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 @@ -5116,7 +5144,7 @@ Для амбітних інвесторів, яким потрібна повна картина їхніх фінансових активів. apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -5124,7 +5152,7 @@ Підтримка електронної пошти та чату apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -5132,7 +5160,7 @@ Разова оплата, без автоматичного поновлення. apps/client/src/app/pages/pricing/pricing-page.html - 303 + 288 @@ -5140,7 +5168,7 @@ Це безкоштовно. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -5211,20 +5239,12 @@ 281 - - Continue with Internet Identity - Продовжити з Інтернет-Ідентичністю - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google Продовжити з Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -5475,7 +5495,7 @@ Відкритий код apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5747,7 +5767,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -5917,6 +5937,10 @@ Membership Членство + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -5931,7 +5955,7 @@ Request it apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -5983,7 +6007,7 @@ Ви дійсно хочете видалити цей рахунок? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -5995,7 +6019,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -6007,7 +6031,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -6015,7 +6039,7 @@ Видалити активності libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6023,7 +6047,7 @@ Чернетка libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -6031,7 +6055,7 @@ Клонувати libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -6039,7 +6063,7 @@ Експортувати чернетку як ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -6047,7 +6071,7 @@ Ви дійсно хочете видалити ці дії? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -6055,7 +6079,7 @@ Ви дійсно хочете видалити цю активність? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -6063,7 +6087,7 @@ Тиждень до дати libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -6075,7 +6099,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -6083,7 +6107,7 @@ Місяць до дати libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -6095,7 +6119,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -6103,7 +6127,7 @@ Рік до дати libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -6123,7 +6147,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -6135,7 +6159,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -6145,29 +6169,33 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + Date Range Діапазон дат libs/ui/src/lib/assistant/assistant.html - 171 + 170 - + Reset Filters Скинути фільтри libs/ui/src/lib/assistant/assistant.html - 266 + 204 - + Apply Filters Застосувати фільтри libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -6215,7 +6243,7 @@ contact us apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -6283,7 +6311,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -6315,7 +6343,7 @@ Упс! Не вдалося отримати історичні дані. libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -6355,14 +6383,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -6397,14 +6425,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -6451,7 +6479,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6511,7 +6539,7 @@ Закрити apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6579,7 +6607,7 @@ Резервний фонд apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -6647,7 +6675,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -6713,14 +6741,14 @@ Tag Тег - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Year @@ -6730,6 +6758,18 @@ 32 + + View Details + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years Роки @@ -6738,6 +6778,14 @@ 33 + + Role + Role + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes Так @@ -6767,7 +6815,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -6795,7 +6843,7 @@ Продати apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -6807,7 +6855,7 @@ Готівка apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -6858,6 +6906,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond Облігація @@ -7002,6 +7058,14 @@ 83 + + View Holding + View Holding + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada Канада @@ -7195,11 +7259,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -7251,7 +7315,7 @@ Default Market Price apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7259,7 +7323,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7267,7 +7331,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7275,7 +7339,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7319,7 +7383,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7339,11 +7403,11 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 @@ -7439,11 +7503,11 @@ Security token apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7451,7 +7515,7 @@ Do you really want to generate a new security token for this user? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 @@ -7459,7 +7523,7 @@ Find account, holding or page... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7467,7 +7531,7 @@ Generate Security Token apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7548,7 +7612,7 @@ with API access for apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7628,7 +7692,7 @@ Do you really want to delete this item? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7728,15 +7792,7 @@ 158 - - Name - Name - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links Quick Links @@ -7744,24 +7800,16 @@ 58 - - Asset Profiles - Asset Profiles - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo Live Demo apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7857,17 +7905,25 @@ Limited Offer! Limited Offer! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra Get extra + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -8060,7 +8116,7 @@ Do you really want to generate a new security token? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8116,7 +8172,7 @@ Manage Asset Profile apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8140,7 +8196,7 @@ Average Unit Price apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8531,6 +8587,14 @@ 128 + + Registration Date + Registration Date + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn Follow Ghostfolio on LinkedIn diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index c414e42e8..7cc46b3c5 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -201,8 +201,8 @@ 310 - apps/client/src/app/pages/features/features-page.html - 63 + apps/client/src/app/pages/register/register-page.html + 27 apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html @@ -308,8 +308,8 @@ Job ID - apps/client/src/app/components/admin-jobs/admin-jobs.html - 34 + apps/client/src/app/pages/pricing/pricing-page.html + 350 @@ -328,7 +328,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -420,8 +420,8 @@ 120 - - Created + + Do you really want to revoke this granted access? apps/client/src/app/components/admin-jobs/admin-jobs.html 134 @@ -570,7 +570,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -612,8 +616,8 @@ 246 - libs/ui/src/lib/i18n.ts - 6 + libs/ui/src/lib/activities-table/activities-table.component.html + 278 @@ -654,23 +658,15 @@ 113 - libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html - 26 + libs/ui/src/lib/activities-table/activities-table.component.html + 259 First Activity - apps/client/src/app/components/admin-market-data/admin-market-data.html - 147 - - - apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 212 - - - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 219 + libs/ui/src/lib/activities-table/activities-table.component.html + 295 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -726,19 +722,12 @@ Gather Profile Data - apps/client/src/app/components/admin-market-data/admin-market-data.html - 234 - - - apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 66 + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 - - - Delete Profiles - apps/client/src/app/components/admin-market-data/admin-market-data.html - 242 + libs/ui/src/lib/activities-table/activities-table.component.html + 440 @@ -769,11 +758,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 467 libs/ui/src/lib/benchmark/benchmark.component.html @@ -783,8 +772,8 @@ Do you really want to delete this asset profile? - apps/client/src/app/components/admin-market-data/admin-market-data.service.ts - 37 + libs/ui/src/lib/accounts-table/accounts-table.component.ts + 148 @@ -804,8 +793,8 @@ Current week - apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 196 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 563 @@ -910,8 +899,8 @@ Max - apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + libs/ui/src/lib/activities-table/activities-table.component.html + 167 libs/ui/src/lib/assistant/assistant.component.ts @@ -953,8 +942,8 @@ 599 - - An error occurred while updating to (). + + ETFs without Countries apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 607 @@ -1080,28 +1069,8 @@ 66 - apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html - 105 - - - apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html - 65 - - - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 345 - - - apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html - 48 - - - libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html - 46 - - - libs/ui/src/lib/i18n.ts - 9 + libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts + 262 @@ -1160,6 +1129,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -1169,7 +1142,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1188,7 +1161,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1210,14 +1183,14 @@ Symbol Mapping apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 Scraper Configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -1289,7 +1262,7 @@ Note apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1576,12 +1549,12 @@ Accounts - apps/client/src/app/components/admin-platform/admin-platform.component.html - 52 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 482 - apps/client/src/app/components/admin-users/admin-users.html - 98 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 534 apps/client/src/app/components/header/header.component.html @@ -1828,7 +1801,7 @@ Do you really want to delete this user? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1874,12 +1847,23 @@ 281 + + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + API Requests Today @@ -1899,7 +1883,7 @@ Impersonate User apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1913,7 +1897,7 @@ Delete User apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1979,8 +1963,8 @@ Available - apps/client/src/app/components/data-provider-status/data-provider-status.component.html - 3 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -2477,11 +2461,11 @@ 5 - - Log out + + Get started apps/client/src/app/components/header/header.component.html - 329 + 432 @@ -2492,7 +2476,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -2518,11 +2502,11 @@ Oops! Incorrect Security Token. apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -2872,7 +2856,7 @@ Security Token apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -2906,21 +2890,21 @@ Sign in with Google apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 27 Stay signed in - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + apps/client/src/app/pages/landing/landing-page.html + 48 Stocks - apps/client/src/app/components/markets/markets.component.ts - 52 + apps/client/src/app/pages/landing/landing-page.html + 350 apps/client/src/app/pages/features/features-page.html @@ -2934,15 +2918,15 @@ 53 - apps/client/src/app/pages/features/features-page.html - 51 + apps/client/src/app/pages/pricing/pricing-page.html + 343 Oops! A data provider is experiencing the hiccups. - apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html - 8 + apps/client/src/app/pages/register/register-page.html + 31 @@ -2952,29 +2936,25 @@ 95 - - Time in Market + + Sign in with Internet Identity - apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 3 + apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html + 37 - - {VAR_PLURAL, plural, =1 {activity} other {activities}} + + Sign in with Google - apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 14 + apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html + 47 Buy - apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 31 - - - libs/ui/src/lib/i18n.ts - 37 + apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html + 56 @@ -2992,28 +2972,39 @@ Absolute Gross Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 + + + + Fees + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 208 + + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 84 Absolute Net Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 Net Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 Total Assets apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -3046,28 +3037,28 @@ Assets apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 Buying Power apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 Excluded from Analysis apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 Liabilities apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -3078,21 +3069,21 @@ Net Worth apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 Annualized Performance apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 Please set the amount of your emergency fund. apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -3126,15 +3117,19 @@ Deactivate - apps/client/src/app/components/rule/rule.component.html - 78 + libs/ui/src/lib/activities-table/activities-table.component.html + 188 + + + libs/ui/src/lib/holdings-table/holdings-table.component.html + 74 Activate - apps/client/src/app/components/rule/rule.component.html - 83 + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 452 @@ -3159,11 +3154,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -3178,11 +3173,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -3193,11 +3188,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -3208,11 +3203,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -3223,7 +3218,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -3234,11 +3229,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -3305,8 +3300,8 @@ Join now - apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html - 193 + apps/client/src/app/pages/pricing/pricing-page.html + 299 @@ -3319,8 +3314,8 @@ Oops! Could not update access. - apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + libs/ui/src/lib/assistant/assistant.component.ts + 383 @@ -3333,8 +3328,8 @@ Edit access - apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html - 11 + libs/ui/src/lib/assistant/assistant.component.ts + 395 @@ -3347,9 +3342,9 @@ Public - apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html - 31 - + libs/ui/src/lib/assistant/assistant.component.ts + 405 + User ID @@ -3358,15 +3353,19 @@ 51 - apps/client/src/app/components/user-account-settings/user-account-settings.html - 252 + libs/ui/src/lib/assistant/assistant.component.ts + 430 Do you really want to generate a new security token? - apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 216 + + + libs/ui/src/lib/assistant/assistant.component.ts + 436 @@ -3394,64 +3393,28 @@ Could not generate an API key apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 - - - - Okay - - apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 - - - apps/client/src/app/core/http-response.interceptor.ts - 89 - - - apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 - - - - Set this API key in your self-hosted environment: - - apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 - - - - Ghostfolio Premium Data Provider API Key - - apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 - - - - Do you really want to generate a new API key? - - apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 215 Could not redeem coupon code apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 Coupon code has been redeemed apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 Reload apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -3473,21 +3436,21 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 Try Premium apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 Redeem Coupon apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -3565,8 +3528,12 @@ 56 - - If a translation is missing, kindly support us in extending it here. + + Locale + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 437 + apps/client/src/app/components/user-account-settings/user-account-settings.html 59 @@ -3673,6 +3640,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 272 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Close Account @@ -3700,7 +3671,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -3718,11 +3689,15 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 - - Oops! It looks like you’re making too many requests. Please slow down a bit. + + Okay + + apps/client/src/app/components/user-account-membership/user-account-membership.component.ts + 154 + apps/client/src/app/core/http-response.interceptor.ts 106 @@ -3731,8 +3706,8 @@ Our - apps/client/src/app/pages/about/oss-friends/oss-friends-page.html - 6 + apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts + 195 @@ -3886,15 +3861,27 @@ Terms of Service - apps/client/src/app/pages/about/terms-of-service/terms-of-service-page.html - 5 + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 375 + + + apps/client/src/app/pages/accounts/accounts-page.html + 4 + + + libs/common/src/lib/routes/routes.ts + 69 + + + libs/ui/src/lib/assistant/assistant.html + 84 Oops, cash balance transfer has failed. apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -4297,9 +4284,6 @@ apps/client/src/app/pages/i18n/i18n-page.html 55 - - - The fixed income contribution of your current investment (${fixedIncomeValueRatio}%) exceeds ${thresholdMax}% apps/client/src/app/pages/i18n/i18n-page.html 57 @@ -4494,19 +4478,20 @@ 150 - - Fee Ratio + + Get Started apps/client/src/app/pages/i18n/i18n-page.html 152 - - - The fees do exceed ${thresholdMax}% of your initial investment (${feeRatio}%) apps/client/src/app/pages/i18n/i18n-page.html 154 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + The fees do not exceed ${thresholdMax}% of your initial investment (${feeRatio}%) @@ -4542,6 +4527,10 @@ apps/client/src/app/pages/i18n/i18n-page.html 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% @@ -4557,8 +4546,8 @@ 175 - - Emerging Markets + + Markets apps/client/src/app/pages/i18n/i18n-page.html 180 @@ -4690,6 +4679,13 @@ 10 + + Edit access + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 11 + + Get Started @@ -4709,33 +4705,18 @@ 334 - - Live Demo - - apps/client/src/app/pages/landing/landing-page.html - 49 - - - apps/client/src/app/pages/landing/landing-page.html - 351 - - - libs/common/src/lib/routes/routes.ts - 231 - - Monthly Active Users apps/client/src/app/pages/landing/landing-page.html - 70 + 69 Stars on GitHub apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -4746,7 +4727,7 @@ Pulls on Docker Hub apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -4757,49 +4738,49 @@ As seen in apps/client/src/app/pages/landing/landing-page.html - 115 + 114 Protect your assets. Refine your personal investment strategy. apps/client/src/app/pages/landing/landing-page.html - 125 + 124 Ghostfolio empowers busy people to keep track of stocks, ETFs or cryptocurrencies without being tracked. apps/client/src/app/pages/landing/landing-page.html - 129 + 128 360° View apps/client/src/app/pages/landing/landing-page.html - 139 + 138 Get the full picture of your personal finances across multiple platforms. apps/client/src/app/pages/landing/landing-page.html - 142 + 141 Web3 Ready apps/client/src/app/pages/landing/landing-page.html - 150 + 149 Use Ghostfolio anonymously and own your financial data. apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -4817,154 +4798,154 @@ Benefit from continuous improvements through a strong community. apps/client/src/app/pages/landing/landing-page.html - 163 + 162 Why Ghostfolio? apps/client/src/app/pages/landing/landing-page.html - 171 + 170 Ghostfolio is for you if you are... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 trading stocks, ETFs or cryptocurrencies on multiple platforms apps/client/src/app/pages/landing/landing-page.html - 179 + 178 pursuing a buy & hold strategy apps/client/src/app/pages/landing/landing-page.html - 185 + 184 interested in getting insights of your portfolio composition apps/client/src/app/pages/landing/landing-page.html - 190 + 189 valuing privacy and data ownership apps/client/src/app/pages/landing/landing-page.html - 195 + 194 into minimalism apps/client/src/app/pages/landing/landing-page.html - 198 + 197 caring about diversifying your financial resources apps/client/src/app/pages/landing/landing-page.html - 202 + 201 interested in financial independence apps/client/src/app/pages/landing/landing-page.html - 206 + 205 saying no to spreadsheets in apps/client/src/app/pages/landing/landing-page.html - 210 + 209 still reading this list apps/client/src/app/pages/landing/landing-page.html - 213 + 212 Learn more about Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 218 + 217 What our users are saying apps/client/src/app/pages/landing/landing-page.html - 227 + 226 Members from around the globe are using Ghostfolio Premium apps/client/src/app/pages/landing/landing-page.html - 266 + 265 How does Ghostfolio work? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 Get started in only 3 steps apps/client/src/app/pages/landing/landing-page.html - 285 + 284 Sign up anonymously* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 * no e-mail address nor credit card required apps/client/src/app/pages/landing/landing-page.html - 293 + 292 Add any of your historical transactions apps/client/src/app/pages/landing/landing-page.html - 305 + 304 Get valuable insights of your portfolio composition apps/client/src/app/pages/landing/landing-page.html - 317 + 316 Are you ready? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -4988,6 +4969,20 @@ 37 + + Ghostfolio Status + + apps/client/src/app/pages/about/overview/about-overview-page.html + 62 + + + + with your university e-mail address + + apps/client/src/app/pages/pricing/pricing-page.html + 365 + + Active Users @@ -5045,42 +5040,92 @@ 132 - - Update activity + + Activities - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 10 + apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html + 86 - - - Stocks, ETFs, bonds, cryptocurrencies, commodities - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 25 + apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html + 115 - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 65 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 221 - - - One-time fee, annual account fees - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 33 + apps/client/src/app/components/admin-tag/admin-tag.component.html + 45 - - - Distribution of corporate earnings - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 41 + apps/client/src/app/components/admin-users/admin-users.html + 119 - - - Revenue for lending out money - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 231 + + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 342 + + + apps/client/src/app/pages/portfolio/activities/activities-page.html + 4 + + + libs/common/src/lib/routes/routes.ts + 128 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 119 + + + + Do you really want to delete these activities? + + libs/ui/src/lib/activities-table/activities-table.component.ts + 270 + + + + Update activity + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 10 + + + + Stocks, ETFs, bonds, cryptocurrencies, commodities + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 25 + + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 65 + + + + One-time fee, annual account fees + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 33 + + + + Distribution of corporate earnings + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 41 + + + + Revenue for lending out money + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 49 @@ -5151,29 +5196,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 - - - - Fee - - apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html - 262 - - - libs/ui/src/lib/activities-table/activities-table.component.html - 237 - - - libs/ui/src/lib/i18n.ts - 39 + 212 Import Activities apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -5181,14 +5211,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 377 Import Dividends apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -5196,28 +5226,28 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 389 Importing data... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 Import has been completed apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 Validating data... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -5241,8 +5271,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -5493,8 +5523,8 @@ Yearly - apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 91 + apps/client/src/app/pages/pricing/pricing-page.html + 359 @@ -5514,8 +5544,8 @@ Analysis - apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 2 + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 310 libs/common/src/lib/routes/routes.ts @@ -5641,15 +5671,15 @@ 7 - - Sustainable retirement income + + Holdings - apps/client/src/app/pages/portfolio/fire/fire-page.html - 40 + libs/ui/src/lib/assistant/assistant.html + 110 - - If you retire today, you would be able to withdraw + + Pricing apps/client/src/app/pages/portfolio/fire/fire-page.html 66 @@ -5743,110 +5773,94 @@ Unlimited Transactions apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 Unlimited Accounts apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 Portfolio Performance apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 Data Import and Export apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 Community Support apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 Self-hosted, update manually. apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 Free apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 For new investors who are just getting started with trading. apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 Fully managed Ghostfolio cloud offering. apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 For ambitious investors who need the full picture of their financial assets. apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -5860,7 +5874,7 @@ Email and Chat Support apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -5881,70 +5895,21 @@ Get extra apps/client/src/app/pages/pricing/pricing-page.html - 314 + 297 If you plan to open an account at apps/client/src/app/pages/pricing/pricing-page.html - 329 - - - - please - - apps/client/src/app/pages/pricing/pricing-page.html - 350 - - - - contact us - - apps/client/src/app/pages/pricing/pricing-page.html - 353 - - - - to use our referral link and get a Ghostfolio Premium membership for one year - - apps/client/src/app/pages/pricing/pricing-page.html - 357 - - - - Looking for a student discount? - - apps/client/src/app/pages/pricing/pricing-page.html - 359 - - - - Request it - - apps/client/src/app/pages/pricing/pricing-page.html - 361 - - - - here - - apps/client/src/app/pages/pricing/pricing-page.html - 364 - - - - with your university e-mail address - - apps/client/src/app/pages/pricing/pricing-page.html - 365 + 303 It’s free. apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -6014,6 +5979,17 @@ 238 + + Registration + + apps/client/src/app/components/admin-users/admin-users.html + 81 + + + libs/common/src/lib/routes/routes.ts + 281 + + Continue with Internet Identity @@ -6025,7 +6001,7 @@ Continue with Google apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -6579,8 +6555,8 @@ 46 - - Users + + Membership libs/common/src/lib/routes/routes.ts 61 @@ -6597,32 +6573,8 @@ about kebab-case - libs/common/src/lib/routes/routes.ts - 176 - - - libs/common/src/lib/routes/routes.ts - 177 - - - libs/common/src/lib/routes/routes.ts - 182 - - - libs/common/src/lib/routes/routes.ts - 190 - - - libs/common/src/lib/routes/routes.ts - 198 - - - libs/common/src/lib/routes/routes.ts - 206 - - - libs/common/src/lib/routes/routes.ts - 214 + apps/client/src/app/pages/pricing/pricing-page.html + 361 @@ -6872,7 +6824,7 @@ Do you really want to delete this account balance? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -6916,7 +6868,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 402 @@ -6927,49 +6879,35 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 415 - - Delete Activities + + Draft libs/ui/src/lib/activities-table/activities-table.component.html - 67 - - - - Draft - - libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 Clone libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 446 Export Draft as ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 - - - - Do you really want to delete these activities? - - libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 456 Do you really want to delete this activity? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -7073,6 +7011,10 @@ libs/ui/src/lib/benchmark/benchmark.component.html 12 + + libs/ui/src/lib/assistant/assistant.html + 140 + 50-Day Trend @@ -7102,6 +7044,13 @@ 117 + + contact us + + apps/client/src/app/pages/pricing/pricing-page.html + 353 + + from ATH @@ -7151,8 +7100,16 @@ 59 - - Deposit + + Interest + + apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html + 69 + + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 298 + libs/ui/src/lib/fire-calculator/fire-calculator.component.ts 362 @@ -7197,6 +7154,25 @@ 221 + + Account + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 85 + + + libs/ui/src/lib/activities-table/activities-table.component.html + 310 + + + libs/ui/src/lib/assistant/assistant.html + 185 + + + libs/ui/src/lib/i18n.ts + 4 + + Asia-Pacific @@ -7204,11 +7180,62 @@ 5 - - Buy and sell + + Asset Class + + apps/client/src/app/components/admin-market-data/admin-market-data.html + 114 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 230 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 321 + + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 242 + + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 290 + + + libs/ui/src/lib/assistant/assistant.html + 246 + + + libs/ui/src/lib/i18n.ts + 6 + + + + Asset Sub Class + + apps/client/src/app/components/admin-market-data/admin-market-data.html + 123 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 239 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 337 + + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 251 + + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 309 + libs/ui/src/lib/i18n.ts - 8 + 7 @@ -7239,6 +7266,21 @@ 14 + + Emergency Fund + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 156 + + + apps/client/src/app/pages/features/features-page.html + 89 + + + libs/ui/src/lib/i18n.ts + 16 + + Grant @@ -7289,7 +7331,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -7320,6 +7362,44 @@ 29 + + Symbol + + apps/client/src/app/components/admin-jobs/admin-jobs.html + 68 + + + apps/client/src/app/components/admin-market-data/admin-market-data.html + 74 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 168 + + + apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html + 37 + + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 315 + + + libs/ui/src/lib/i18n.ts + 30 + + + + Tag + + libs/ui/src/lib/assistant/assistant.html + 235 + + + libs/ui/src/lib/i18n.ts + 31 + + Year @@ -7327,6 +7407,17 @@ 32 + + View Details + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years @@ -7341,8 +7432,16 @@ 34 - - Liability + + Fee + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 262 + + + libs/ui/src/lib/activities-table/activities-table.component.html + 236 + libs/ui/src/lib/i18n.ts 41 @@ -7359,7 +7458,29 @@ Alternative Investment libs/ui/src/lib/i18n.ts - 46 + 41 + + + + Sell + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 43 + + + libs/ui/src/lib/i18n.ts + 42 + + + + Cash + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 190 + + + libs/ui/src/lib/i18n.ts + 55 @@ -7390,6 +7511,13 @@ 51 + + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond @@ -7492,245 +7620,2451 @@ Armenia libs/ui/src/lib/i18n.ts - 77 + 106 - - Argentina + + Extreme Greed libs/ui/src/lib/i18n.ts - 78 + 107 - - Australia + + Neutral libs/ui/src/lib/i18n.ts - 79 + 110 - - Austria + + Valid until - libs/ui/src/lib/i18n.ts - 80 + apps/client/src/app/components/admin-settings/admin-settings.component.html + 74 - - - Belgium - libs/ui/src/lib/i18n.ts - 81 + libs/ui/src/lib/membership-card/membership-card.component.html + 42 - - British Virgin Islands + + Time to add your first activity. - libs/ui/src/lib/i18n.ts - 82 + libs/ui/src/lib/no-transactions-info/no-transactions-info.component.html + 12 - - Bulgaria + + No data available - libs/ui/src/lib/i18n.ts - 83 - - - - Canada + apps/client/src/app/pages/portfolio/allocations/allocations-page.html + 250 + - libs/ui/src/lib/i18n.ts - 84 + apps/client/src/app/pages/public/public-page.html + 196 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 209 + + + libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts + 414 + + + libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts + 427 + + + libs/ui/src/lib/top-holdings/top-holdings.component.html + 181 - - Czech Republic + + If a translation is missing, kindly support us in extending it here. - libs/ui/src/lib/i18n.ts - 85 + apps/client/src/app/components/user-account-settings/user-account-settings.html + 59 - - Finland + + Date Range - libs/ui/src/lib/i18n.ts - 86 + libs/ui/src/lib/assistant/assistant.html + 171 - - France + + The current market price is - libs/ui/src/lib/i18n.ts - 87 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 672 - - Germany + + Test - libs/ui/src/lib/i18n.ts - 88 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 500 - - India + + Oops! Could not grant access. - libs/ui/src/lib/i18n.ts - 89 + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts + 142 - - Indonesia + + Argentina libs/ui/src/lib/i18n.ts - 90 + 78 - - Italy + + Australia libs/ui/src/lib/i18n.ts - 91 + 79 - - Japan + + Austria - libs/ui/src/lib/i18n.ts - 92 + apps/client/src/app/components/access-table/access-table.component.html + 18 + + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 38 - - Netherlands + + Private - libs/ui/src/lib/i18n.ts - 93 + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 30 - - New Zealand + + Job Queue - libs/ui/src/lib/i18n.ts - 94 + libs/common/src/lib/routes/routes.ts + 46 - - Poland + + Market data is delayed for - libs/ui/src/lib/i18n.ts + apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts 95 - - Romania + + Absolute Currency Performance - libs/ui/src/lib/i18n.ts - 96 + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 145 - - Singapore + + Absolute Net Performance - libs/ui/src/lib/i18n.ts - 97 + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 193 - - South Africa + + Absolute Asset Performance - libs/ui/src/lib/i18n.ts - 98 + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 102 - - Thailand + + Investment - libs/ui/src/lib/i18n.ts - 100 + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 171 + + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 58 + + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 80 - - Ukraine + + Belgium - libs/ui/src/lib/i18n.ts - 101 + apps/client/src/app/pages/pricing/pricing-page.html + 364 - - United Kingdom + + British Virgin Islands libs/ui/src/lib/i18n.ts - 102 + 82 - - United States + + Bulgaria libs/ui/src/lib/i18n.ts - 103 + 83 - - Extreme Fear + + Canada libs/ui/src/lib/i18n.ts - 106 + 84 - - Extreme Greed + + Year to date - libs/ui/src/lib/i18n.ts - 107 + libs/ui/src/lib/assistant/assistant.component.ts + 395 - - Neutral + + Week to date - libs/ui/src/lib/i18n.ts - 110 + libs/ui/src/lib/assistant/assistant.component.ts + 387 - - API Key + + Month to date - libs/ui/src/lib/membership-card/membership-card.component.html - 18 + libs/ui/src/lib/assistant/assistant.component.ts + 391 - - Generate Ghostfolio Premium Data Provider API key for self-hosted environments... + + MTD - libs/ui/src/lib/membership-card/membership-card.component.html - 26 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 200 + + + libs/ui/src/lib/assistant/assistant.component.ts + 391 - - Time to add your first activity. + + WTD - libs/ui/src/lib/no-transactions-info/no-transactions-info.component.html - 12 + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 196 + + + libs/ui/src/lib/assistant/assistant.component.ts + 387 - - Oops! Could not find any assets. + + Oops! A data provider is experiencing the hiccups. - libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html - 40 + apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html + 8 - - Create + + View - libs/ui/src/lib/tags-selector/tags-selector.component.html - 50 + apps/client/src/app/components/access-table/access-table.component.html + 23 + + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 42 - - Show more + + Reset Filters - libs/ui/src/lib/top-holdings/top-holdings.component.html - 174 + libs/ui/src/lib/assistant/assistant.html + 266 + + + + year + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 208 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 290 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 296 + + + libs/ui/src/lib/assistant/assistant.component.ts + 405 + + + + years + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 212 + + + libs/ui/src/lib/assistant/assistant.component.ts + 430 + + + + Apply Filters + + libs/ui/src/lib/assistant/assistant.html + 276 + + + + self-hosting + kebab-case + + libs/common/src/lib/routes/routes.ts + 243 + + + libs/common/src/lib/routes/routes.ts + 246 + + + + Self-Hosting + + apps/client/src/app/pages/faq/faq-page.component.ts + 60 + + + libs/common/src/lib/routes/routes.ts + 248 + + + + Data Gathering + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 593 + + + apps/client/src/app/components/admin-overview/admin-overview.html + 60 + + + + General + + apps/client/src/app/pages/faq/faq-page.component.ts + 49 + + + + Cloud + + apps/client/src/app/pages/faq/faq-page.component.ts + 54 + + + libs/common/src/lib/routes/routes.ts + 240 + + + + Oops! It looks like you’re making too many requests. Please slow down a bit. + + apps/client/src/app/core/http-response.interceptor.ts + 106 + + + + My Account + + apps/client/src/app/pages/i18n/i18n-page.html + 13 + + + + Closed + + apps/client/src/app/components/home-holdings/home-holdings.component.ts + 65 + + + + Active + + apps/client/src/app/components/home-holdings/home-holdings.component.ts + 64 + + + + Indonesia + + libs/ui/src/lib/i18n.ts + 85 + + + + Finland + + libs/ui/src/lib/i18n.ts + 86 + + + + France + + libs/ui/src/lib/i18n.ts + 87 + + + + {VAR_PLURAL, plural, =1 {activity} other {activities}} + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html + 14 + + + + Delete Activities + + libs/ui/src/lib/activities-table/activities-table.component.html + 67 + + + + Internationalization + + libs/common/src/lib/routes/routes.ts + 119 + + + + Close Account + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 307 + + + + Do you really want to close your Ghostfolio account? + + apps/client/src/app/components/user-account-settings/user-account-settings.component.ts + 206 + + + + Danger Zone + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 272 + + + + Approximation based on the top holdings of each ETF + + apps/client/src/app/pages/portfolio/allocations/allocations-page.html + 340 + + + + By ETF Holding + + apps/client/src/app/pages/portfolio/allocations/allocations-page.html + 333 + + + + Join now or check out the example account + + apps/client/src/app/pages/landing/landing-page.html + 334 + + + + Oops! There was an error setting up biometric authentication. + + apps/client/src/app/components/user-account-settings/user-account-settings.component.ts + 334 + + + + Show more + + libs/ui/src/lib/top-holdings/top-holdings.component.html + 174 + + + + Do you really want to delete these profiles? + + apps/client/src/app/components/admin-market-data/admin-market-data.service.ts + 68 + + + + Delete Profiles + + apps/client/src/app/components/admin-market-data/admin-market-data.html + 242 + + + + Oops! Could not delete profiles. + + apps/client/src/app/components/admin-market-data/admin-market-data.service.ts + 56 + + + + Benchmarks + + apps/client/src/app/components/admin-market-data/admin-market-data.component.ts + 127 + + + + Chart + + apps/client/src/app/components/home-holdings/home-holdings.html + 19 + + + + Table + + apps/client/src/app/components/home-holdings/home-holdings.html + 16 + + + + Would you like to refine your personal investment strategy? + + apps/client/src/app/pages/public/public-page.html + 234 + + + + Wealth + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 98 + + + + Community + + apps/client/src/app/components/footer/footer.component.html + 80 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 85 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 90 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 94 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 98 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 102 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 106 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 110 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 114 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 118 + + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 123 + + + apps/client/src/app/pages/features/features-page.html + 276 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 85 + + + + Thailand + + libs/ui/src/lib/i18n.ts + 88 + + + + India + + libs/ui/src/lib/i18n.ts + 89 + + + + Indonesia + + libs/ui/src/lib/i18n.ts + 90 + + + + Italy + + libs/ui/src/lib/i18n.ts + 91 + + + + Japan + + libs/ui/src/lib/i18n.ts + 92 + + + + App + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 83 + + + + Tool + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 96 + + + + Investor + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 89 + + + + Wealth Management + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 99 + + + + Canada + + libs/ui/src/lib/i18n.ts + 93 + + + + New Zealand + + libs/ui/src/lib/i18n.ts + 94 + + + + Poland + + libs/ui/src/lib/i18n.ts + 95 + + + + Romania + + libs/ui/src/lib/i18n.ts + 96 + + + + Singapore + + libs/ui/src/lib/i18n.ts + 88 + + + + United States + + libs/ui/src/lib/i18n.ts + 103 + + + + Budgeting + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 84 + + + + Belgium + + libs/ui/src/lib/i18n.ts + 81 + + + + Open Source + + apps/client/src/app/pages/landing/landing-page.html + 160 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts + 90 + + + + Czech Republic + + libs/ui/src/lib/i18n.ts + 85 + + + + Australia + + libs/ui/src/lib/i18n.ts + 79 + + + + South Africa + + libs/ui/src/lib/i18n.ts + 98 + + + + Thailand + + libs/ui/src/lib/i18n.ts + 100 + + + + Ukraine + + libs/ui/src/lib/i18n.ts + 101 + + + + United Kingdom + + libs/ui/src/lib/i18n.ts + 102 + + + + Error + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 663 + + + + Cancel + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 161 + + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 598 + + + apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html + 57 + + + apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html + 44 + + + apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html + 27 + + + apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html + 17 + + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 66 + + + apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html + 105 + + + apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html + 65 + + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 345 + + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 48 + + + libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html + 46 + + + libs/ui/src/lib/i18n.ts + 103 + + + + Yes + + libs/ui/src/lib/i18n.ts + 106 + + + + Extreme Greed + + apps/client/src/app/pages/portfolio/fire/fire-page.html + 95 + + + + Inactive + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 87 + + + + Close + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 600 + + + apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html + 59 + + + apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html + 46 + + + apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html + 29 + + + apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html + 19 + + + apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html + 130 + + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 68 + + + apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html + 107 + + + + Activate + + apps/client/src/app/components/rule/rule.component.html + 83 + + + + Oops! Could not update access. + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts + 179 + + + + Deactivate + + apps/client/src/app/components/rule/rule.component.html + 78 + + + + Threshold Max + + apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html + 93 + + + + send an e-mail to + + apps/client/src/app/pages/about/overview/about-overview-page.html + 87 + + + + Customize + + apps/client/src/app/components/rule/rule.component.html + 69 + + + + Portfolio Snapshot + + apps/client/src/app/components/admin-jobs/admin-jobs.html + 56 + + + + Threshold Min + + apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html + 55 + + + + If you plan to open an account at + + apps/client/src/app/pages/pricing/pricing-page.html + 329 + + + + Performance with currency effect Performance + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 83 + + + + Accounts + + libs/ui/src/lib/assistant/assistant.html + 84 + + + + Copy link to clipboard + + apps/client/src/app/components/access-table/access-table.component.html + 84 + + + + Change with currency effect Change + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 63 + + + + No auto-renewal. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 70 + + + + From the beginning + + apps/client/src/app/pages/public/public-page.html + 60 + + + + This year + + apps/client/src/app/pages/public/public-page.html + 42 + + + + offers a free plan + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 256 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 273 + + + + does not offer a free plan + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 263 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 280 + + + + Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions. + + apps/client/src/app/pages/about/overview/about-overview-page.html + 10 + + + + to use our referral link and get a Ghostfolio Premium membership for one year + + apps/client/src/app/pages/pricing/pricing-page.html + 357 + + + + can be self-hosted + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 178 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 195 + + + + cannot be self-hosted + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 185 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 202 + + + + can be used anonymously + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 217 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 234 + + + + cannot be used anonymously + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 224 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 241 + + + + is not Open Source Software + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 146 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 163 + + + + is Open Source Software + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 139 + + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 156 + + + + This page has been archived. + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 14 + + + + Oops! Invalid currency. + + apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html + 48 + + + + Oops! Could not find any assets. + + libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html + 40 + + + + Ukraine + + libs/ui/src/lib/i18n.ts + 110 + + + + API Key + + libs/ui/src/lib/membership-card/membership-card.component.html + 18 + + + + Generate Ghostfolio Premium Data Provider API key for self-hosted environments... + + libs/ui/src/lib/membership-card/membership-card.component.html + 26 + + + + Time to add your first activity. + + libs/ui/src/lib/no-transactions-info/no-transactions-info.component.html + 12 + + + + Oops! Could not find any assets. + + apps/client/src/app/components/admin-users/admin-users.html + 162 + + + + Could not generate an API key + + apps/client/src/app/components/user-account-membership/user-account-membership.component.ts + 141 + + + + Do you really want to generate a new API key? + + apps/client/src/app/components/user-account-membership/user-account-membership.component.ts + 164 + + + + Ghostfolio Premium Data Provider API Key + + apps/client/src/app/components/user-account-membership/user-account-membership.component.ts + 159 + + + + Set this API key in your self-hosted environment: + + apps/client/src/app/components/user-account-membership/user-account-membership.component.ts + 156 + + + + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 58 + + + + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 56 + + + + Save + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 609 + + + apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html + 68 + + + apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html + 55 + + + apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html + 38 + + + apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html + 28 + + + apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts + 73 + + + apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html + 136 + + + apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html + 81 + + + apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html + 116 + + + apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html + 356 + + + libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html + 48 + + + + Received Access + + apps/client/src/app/components/user-account-access/user-account-access.html + 53 + + + + Check the system status at + + apps/client/src/app/pages/about/overview/about-overview-page.html + 57 + + + + Me + + apps/client/src/app/components/header/header.component.html + 213 + + + apps/client/src/app/components/user-account-access/user-account-access.component.ts + 251 + + + + Please enter your Ghostfolio API key. + + apps/client/src/app/components/admin-settings/admin-settings.component.ts + 147 + + + + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 199 + + + + Link has been copied to the clipboard + + apps/client/src/app/components/access-table/access-table.component.ts + 101 + + + + Mode + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 450 + + + + Default Market Price + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 409 + + + + Selector + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 466 + + + + Instant + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 234 + + + + Lazy + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 230 + + + + HTTP Request Headers + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 422 + + + + real-time + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 234 + + + + end of day + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 230 + + + + Open Duck.ai + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 200 + + + + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + + + Show more + + libs/ui/src/lib/holdings-table/holdings-table.component.html + 143 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 367 + + + + Performance + + apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html + 6 + + + apps/client/src/app/components/home-overview/home-overview.component.ts + 55 + + + libs/ui/src/lib/holdings-table/holdings-table.component.html + 166 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 367 + + + libs/ui/src/lib/treemap-chart/treemap-chart.component.ts + 380 + + + + The project has been initiated by + + apps/client/src/app/pages/about/overview/about-overview-page.html + 40 + + + + Copy AI prompt to clipboard for analysis + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 67 + + + + Singapore + + libs/ui/src/lib/i18n.ts + 97 + + + + Armenia + + libs/ui/src/lib/i18n.ts + 77 + + + + British Virgin Islands + + libs/ui/src/lib/i18n.ts + 82 + + + + Copy portfolio data to clipboard for AI prompt + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 42 + + + + I understand that if I lose my security token, I cannot recover my account + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 28 + + + + Please keep your security token safe. If you lose it, you will not be able to recover your account. + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 18 + + + + Here is your security token. It is only visible once, please store and keep it in a safe place. + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 67 + + + + Continue + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 57 + + + + Terms and Conditions + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 15 + + + + Do you really want to generate a new security token for this user? + + apps/client/src/app/components/admin-users/admin-users.component.ts + 201 + + + + Find account, holding or page... + + libs/ui/src/lib/assistant/assistant.component.ts + 162 + + + + Security token + + apps/client/src/app/components/admin-users/admin-users.component.ts + 196 + + + apps/client/src/app/components/user-account-access/user-account-access.component.ts + 169 + + + + Generate Security Token + + apps/client/src/app/components/admin-users/admin-users.html + 233 + + + + United Kingdom + + libs/ui/src/lib/i18n.ts + 102 + + + + Terms of Service + + apps/client/src/app/components/footer/footer.component.html + 62 + + + libs/common/src/lib/routes/routes.ts + 217 + + + + terms-of-service + kebab-case + + libs/common/src/lib/routes/routes.ts + 212 + + + libs/common/src/lib/routes/routes.ts + 215 + + + + Terms of Service + + apps/client/src/app/pages/about/terms-of-service/terms-of-service-page.html + 5 + + + + and I agree to the Terms of Service. + + apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html + 34 + + + + () is already in use. + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 599 + + + + An error occurred while updating to (). + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 607 + + + + Apply + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 153 + + + + with API access for + + apps/client/src/app/pages/pricing/pricing-page.html + 253 + + + + Data Gathering is off + + apps/client/src/app/components/admin-market-data/admin-market-data.html + 38 + + + + Performance Calculation + + apps/client/src/app/components/user-account-settings/user-account-settings.html + 31 + + + + someone + + apps/client/src/app/pages/public/public-page.component.ts + 59 + + + + Add asset to watchlist + + apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html + 7 + + + + Watchlist + + apps/client/src/app/components/home-watchlist/home-watchlist.html + 4 + + + apps/client/src/app/pages/features/features-page.html + 197 + + + libs/common/src/lib/routes/routes.ts + 110 + + + + Do you really want to delete this item? + + libs/ui/src/lib/benchmark/benchmark.component.ts + 139 + + + + Log out + + apps/client/src/app/components/header/header.component.html + 329 + + + + Calculations are based on delayed market data and may not be displayed in real-time. + + apps/client/src/app/components/home-market/home-market.html + 45 + + + apps/client/src/app/components/markets/markets.html + 54 + + + + changelog + kebab-case + + libs/common/src/lib/routes/routes.ts + 180 + + + libs/common/src/lib/routes/routes.ts + 183 + + + + Sync Demo User Account + + apps/client/src/app/components/admin-overview/admin-overview.html + 195 + + + + Demo user account has been synced. + + apps/client/src/app/components/admin-overview/admin-overview.component.ts + 275 + + + + Name + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 + + + + Set up + + apps/client/src/app/pages/i18n/i18n-page.html + 145 + + + + No emergency fund has been set up + + apps/client/src/app/pages/i18n/i18n-page.html + 147 + + + + An emergency fund has been set up + + apps/client/src/app/pages/i18n/i18n-page.html + 150 + + + + Fee Ratio + + apps/client/src/app/pages/i18n/i18n-page.html + 152 + + + + The fees do exceed ${thresholdMax}% of your initial investment (${feeRatio}%) + + apps/client/src/app/pages/i18n/i18n-page.html + 154 + + + + The fees do not exceed ${thresholdMax}% of your initial investment (${feeRatio}%) + + apps/client/src/app/pages/i18n/i18n-page.html + 158 + + + + Quick Links + + libs/ui/src/lib/assistant/assistant.html + 58 + + + + Asset Profiles + + libs/ui/src/lib/assistant/assistant.html + 140 + + + + Live Demo + + apps/client/src/app/pages/landing/landing-page.html + 49 + + + apps/client/src/app/pages/landing/landing-page.html + 351 + + + libs/common/src/lib/routes/routes.ts + 231 + + + + Open Source Alternative to + + libs/common/src/lib/routes/routes.ts + 326 + + + + Single Account + + apps/client/src/app/pages/i18n/i18n-page.html + 28 + + + + Your net worth is managed by a single account + + apps/client/src/app/pages/i18n/i18n-page.html + 30 + + + + Your net worth is managed by ${accountsLength} accounts + + apps/client/src/app/pages/i18n/i18n-page.html + 36 + + + + personal-finance-tools + kebab-case + + libs/common/src/lib/routes/routes.ts + 312 + + + libs/common/src/lib/routes/routes.ts + 315 + + + libs/common/src/lib/routes/routes.ts + 323 + + + + markets + kebab-case + + libs/common/src/lib/routes/routes.ts + 304 + + + libs/common/src/lib/routes/routes.ts + 307 + + + + Get Access + + apps/client/src/app/components/admin-settings/admin-settings.component.html + 27 + + + + Fuel your self-hosted Ghostfolio with a powerful data provider to access 80,000+ tickers from over 50 exchanges worldwide. + + apps/client/src/app/components/admin-settings/admin-settings.component.html + 16 + + + + Learn more + + apps/client/src/app/components/admin-settings/admin-settings.component.html + 38 + + + + Limited Offer! + + apps/client/src/app/pages/pricing/pricing-page.html + 312 + + + + Get extra + + apps/client/src/app/pages/pricing/pricing-page.html + 314 + + + + Unavailable + + apps/client/src/app/components/data-provider-status/data-provider-status.component.html + 5 + + + + Available + + apps/client/src/app/components/data-provider-status/data-provider-status.component.html + 3 + + + + Current month + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 200 + + + + new + + apps/client/src/app/components/admin-settings/admin-settings.component.html + 67 + + + apps/client/src/app/pages/admin/admin-page.component.ts + 56 + + + + Investment + + apps/client/src/app/pages/i18n/i18n-page.html + 15 + + + + Over ${thresholdMax}% of your current investment is at ${maxAccountName} (${maxInvestmentRatio}%) + + apps/client/src/app/pages/i18n/i18n-page.html + 17 + + + + The major part of your current investment is at ${maxAccountName} (${maxInvestmentRatio}%) and does not exceed ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 24 + + + + Equity + + apps/client/src/app/pages/i18n/i18n-page.html + 41 + + + + The equity contribution of your current investment (${equityValueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 43 + + + + The equity contribution of your current investment (${equityValueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 47 + + + + The equity contribution of your current investment (${equityValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 51 + + + + Fixed Income + + apps/client/src/app/pages/i18n/i18n-page.html + 55 + + + + The fixed income contribution of your current investment (${fixedIncomeValueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 57 + + + + The fixed income contribution of your current investment (${fixedIncomeValueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 61 + + + + The fixed income contribution of your current investment (${fixedIncomeValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 66 + + + + Investment: Base Currency + + apps/client/src/app/pages/i18n/i18n-page.html + 85 + + + + The major part of your current investment is not in your base currency (${baseCurrencyValueRatio}% in ${baseCurrency}) + + apps/client/src/app/pages/i18n/i18n-page.html + 88 + + + + The major part of your current investment is in your base currency (${baseCurrencyValueRatio}% in ${baseCurrency}) + + apps/client/src/app/pages/i18n/i18n-page.html + 92 + + + + Investment + + apps/client/src/app/pages/i18n/i18n-page.html + 95 + + + + Over ${thresholdMax}% of your current investment is in ${currency} (${maxValueRatio}%) + + apps/client/src/app/pages/i18n/i18n-page.html + 97 + + + + The major part of your current investment is in ${currency} (${maxValueRatio}%) and does not exceed ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 101 + + + + start + kebab-case + + libs/common/src/lib/routes/routes.ts + 336 + + + libs/common/src/lib/routes/routes.ts + 337 + + + + Generate + + apps/client/src/app/components/user-account-access/user-account-access.html + 43 + + + + If you encounter a bug, would like to suggest an improvement or a new feature, please join the Ghostfolio Slack community, post to @ghostfolio_ + + apps/client/src/app/pages/about/overview/about-overview-page.html + 69 + + + + Do you really want to generate a new security token? + + apps/client/src/app/components/user-account-access/user-account-access.component.ts + 174 + + + + Cryptocurrencies + + apps/client/src/app/components/markets/markets.component.ts + 53 + + + apps/client/src/app/pages/features/features-page.html + 51 + + + + Stocks + + apps/client/src/app/components/markets/markets.component.ts + 52 + + + apps/client/src/app/pages/features/features-page.html + 15 + + + + + + apps/client/src/app/components/admin-users/admin-users.html + 40 + + + + Manage Asset Profile + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 + + + + Alternative Investment + + libs/ui/src/lib/i18n.ts + 46 + + + + Collectible + + libs/ui/src/lib/i18n.ts + 56 + + + + Average Unit Price + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts + 111 + + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 101 + + + + No results found... + + libs/ui/src/lib/assistant/assistant.html + 51 + + + + Account Cluster Risks + + apps/client/src/app/pages/i18n/i18n-page.html + 14 + + + + Asset Class Cluster Risks + + apps/client/src/app/pages/i18n/i18n-page.html + 39 + + + + Currency Cluster Risks + + apps/client/src/app/pages/i18n/i18n-page.html + 83 + + + + Economic Market Cluster Risks + + apps/client/src/app/pages/i18n/i18n-page.html + 106 + + + + Emergency Fund + + apps/client/src/app/pages/i18n/i18n-page.html + 144 + + + + Fees + + apps/client/src/app/pages/i18n/i18n-page.html + 161 + + + + Liquidity + + apps/client/src/app/pages/i18n/i18n-page.html + 70 + + + + Buying Power + + apps/client/src/app/pages/i18n/i18n-page.html + 71 + + + + Your buying power is below ${thresholdMin} ${baseCurrency} + + apps/client/src/app/pages/i18n/i18n-page.html + 73 + + + + Your buying power is 0 ${baseCurrency} + + apps/client/src/app/pages/i18n/i18n-page.html + 77 + + + + Your buying power exceeds ${thresholdMin} ${baseCurrency} + + apps/client/src/app/pages/i18n/i18n-page.html + 80 + + + + Regional Market Cluster Risks + + apps/client/src/app/pages/i18n/i18n-page.html + 163 + + + + Developed Markets + + apps/client/src/app/pages/i18n/i18n-page.html + 109 + + + + The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 112 + + + + The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 117 + + + + The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 122 + + + + Emerging Markets + + apps/client/src/app/pages/i18n/i18n-page.html + 127 + + + + The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 130 + + + + The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 135 + + + + The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 140 + + + + No accounts have been set up + + apps/client/src/app/pages/i18n/i18n-page.html + 21 + + + + Your net worth is managed by 0 accounts + + apps/client/src/app/pages/i18n/i18n-page.html + 33 + + + + Asia-Pacific + + apps/client/src/app/pages/i18n/i18n-page.html + 165 + + + + The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 167 + + + + The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 171 + + + + The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 175 + + + + Emerging Markets + + apps/client/src/app/pages/i18n/i18n-page.html + 180 + + + + The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 183 + + + + The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 187 + + + + The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 191 + + + + Europe + + apps/client/src/app/pages/i18n/i18n-page.html + 195 + + + + The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 197 + + + + The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 201 + + + + The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 205 + + + + Japan + + apps/client/src/app/pages/i18n/i18n-page.html + 209 + + + + The Japan market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 211 + + + + The Japan market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 215 + + + + The Japan market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 219 + + + + North America + + apps/client/src/app/pages/i18n/i18n-page.html + 223 + + + + The North America market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 225 + + + + The North America market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}% + + apps/client/src/app/pages/i18n/i18n-page.html + 229 + + + + The North America market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}% + + apps/client/src/app/pages/i18n/i18n-page.html + 233 + + + + Support Ghostfolio + + apps/client/src/app/pages/about/overview/about-overview-page.html + 166 + + + + Find Ghostfolio on GitHub + + apps/client/src/app/pages/about/overview/about-overview-page.html + 99 + + + apps/client/src/app/pages/about/overview/about-overview-page.html + 138 + + + + Ghostfolio is an independent & bootstrapped business + + apps/client/src/app/pages/about/overview/about-overview-page.html + 157 + + + + Send an e-mail + + apps/client/src/app/pages/about/overview/about-overview-page.html + 89 + + + apps/client/src/app/pages/about/overview/about-overview-page.html + 128 + + + + Join the Ghostfolio Slack community + + apps/client/src/app/pages/about/overview/about-overview-page.html + 109 + + + + Follow Ghostfolio on LinkedIn + + apps/client/src/app/pages/about/overview/about-overview-page.html + 147 + + + + Follow Ghostfolio on X (formerly Twitter) + + apps/client/src/app/pages/about/overview/about-overview-page.html + 118 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index a206d7e42..5c2508f8d 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -184,7 +184,7 @@ apps/client/src/app/pages/register/register-page.html - 27 + 28 apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html @@ -241,10 +241,10 @@ please - please + apps/client/src/app/pages/pricing/pricing-page.html - 350 + 336 @@ -264,7 +264,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 158 + 163 @@ -285,12 +285,20 @@ with - with + apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 87 + + plus + plus + + apps/client/src/app/pages/pricing/pricing-page.html + 202 + + Do you really want to revoke this granted access? 您真的要撤销此访问权限吗? @@ -396,7 +404,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 135 + 137 + + + libs/ui/src/lib/benchmark/benchmark.component.html + 12 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -448,7 +460,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 279 + 283 @@ -484,11 +496,11 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 260 + 264 libs/ui/src/lib/activities-table/activities-table.component.html - 296 + 300 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -524,11 +536,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 307 + 313 libs/ui/src/lib/activities-table/activities-table.component.html - 441 + 453 @@ -560,11 +572,11 @@ libs/ui/src/lib/accounts-table/accounts-table.component.html - 318 + 324 libs/ui/src/lib/activities-table/activities-table.component.html - 468 + 480 libs/ui/src/lib/benchmark/benchmark.component.html @@ -576,7 +588,7 @@ 您确定要删除此账户吗? libs/ui/src/lib/accounts-table/accounts-table.component.ts - 148 + 151 @@ -596,7 +608,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 563 + 567 @@ -661,7 +673,7 @@ and is driven by the efforts of its contributors - and is driven by the efforts of its contributors + 并且得益于其 贡献者 apps/client/src/app/pages/about/overview/about-overview-page.html 49 @@ -720,7 +732,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 167 + 172 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html @@ -755,6 +767,14 @@ 96 + + Everything in + Everything in + + apps/client/src/app/pages/pricing/pricing-page.html + 199 + + ETFs without Countries 没有国家的 ETF @@ -860,7 +880,7 @@ 哎呀!无法解析历史数据。 libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts - 262 + 263 @@ -914,6 +934,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 278 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 53 + Sectors @@ -924,7 +948,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 511 + 515 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -944,7 +968,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 522 + 526 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -956,12 +980,12 @@ 代码映射 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 375 + 379 and we share aggregated key metrics of the platform’s performance - and we share aggregated key metrics of the platform’s performance + 并且我们分享平台性能的聚合 关键指标 apps/client/src/app/pages/about/overview/about-overview-page.html 32 @@ -972,7 +996,7 @@ 刮削配置 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 400 + 404 @@ -980,7 +1004,7 @@ 笔记 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 547 + 551 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -1188,11 +1212,11 @@ 网址 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 482 + 486 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 534 + 538 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1221,7 +1245,7 @@ Current year - Current year + 当前年份 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 204 @@ -1296,7 +1320,7 @@ 您真的要删除该用户吗? apps/client/src/app/components/admin-users/admin-users.component.ts - 175 + 210 @@ -1311,6 +1335,14 @@ 231 + + No auto-renewal on membership. + No auto-renewal on membership. + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 73 + + Engagement per Day 每天的参与度 @@ -1318,6 +1350,10 @@ apps/client/src/app/components/admin-users/admin-users.html 141 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 89 + Last Request @@ -1332,7 +1368,7 @@ 模拟用户 apps/client/src/app/components/admin-users/admin-users.html - 223 + 232 @@ -1340,7 +1376,7 @@ 删除用户 apps/client/src/app/components/admin-users/admin-users.html - 244 + 253 @@ -1388,7 +1424,7 @@ 基准 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 369 + 371 apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1423,14 +1459,6 @@ 5 - - Get started - 开始使用 - - apps/client/src/app/components/header/header.component.html - 432 - - Sign in 登入 @@ -1440,7 +1468,7 @@ apps/client/src/app/components/header/header.component.ts - 279 + 290 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -1460,11 +1488,11 @@ 哎呀!安全令牌不正确。 apps/client/src/app/components/header/header.component.ts - 294 + 305 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 153 + 154 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -1541,7 +1569,7 @@ The source code is fully available as open source software (OSS) under the AGPL-3.0 license - The source code is fully available as open source software (OSS) under the AGPL-3.0 license + 源代码完全可用,作为开源软件 (OSS),遵循AGPL-3.0许可证 apps/client/src/app/pages/about/overview/about-overview-page.html 16 @@ -1605,7 +1633,7 @@ Current week - Current week + 当前周 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 196 @@ -1644,7 +1672,7 @@ 安全令牌 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 7 + 8 apps/client/src/app/components/user-account-access/user-account-access.html @@ -1676,15 +1704,15 @@ apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 27 + 32 apps/client/src/app/pages/landing/landing-page.html - 48 + 47 apps/client/src/app/pages/landing/landing-page.html - 350 + 348 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -1696,31 +1724,23 @@ apps/client/src/app/pages/pricing/pricing-page.html - 343 + 329 apps/client/src/app/pages/register/register-page.html - 31 + 33 apps/client/src/app/pages/webauthn/webauthn-page.html 30 - - Sign in with Internet Identity - 使用互联网身份登录 - - apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 37 - - Sign in with Google 使用 Google 登录 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 47 + 44 @@ -1728,7 +1748,7 @@ 保持登录 apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html - 56 + 55 @@ -1744,7 +1764,7 @@ 绝对总业绩 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 70 + 73 @@ -1756,7 +1776,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 84 + 88 @@ -1764,7 +1784,7 @@ 绝对净绩效 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 102 + 107 @@ -1772,7 +1792,7 @@ 净绩效 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 117 + 123 @@ -1780,7 +1800,7 @@ 总资产 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 143 + 149 @@ -1788,7 +1808,7 @@ 资产 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 203 + 226 @@ -1796,7 +1816,7 @@ 购买力 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 216 + 241 @@ -1804,7 +1824,7 @@ 从分析中排除 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 228 + 267 @@ -1812,7 +1832,7 @@ 负债 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 243 + 295 apps/client/src/app/pages/features/features-page.html @@ -1824,7 +1844,7 @@ 净值 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 264 + 317 @@ -1832,7 +1852,7 @@ 年化业绩 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 277 + 331 @@ -1840,7 +1860,7 @@ 请输入您的应急基金金额。 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 75 + 108 @@ -1872,7 +1892,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 188 + 193 libs/ui/src/lib/holdings-table/holdings-table.component.html @@ -1884,7 +1904,7 @@ 报告数据故障 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 452 + 451 @@ -1912,11 +1932,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 44 + 47 apps/client/src/app/pages/pricing/pricing-page.html - 205 + 207 @@ -1932,11 +1952,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 48 + 51 apps/client/src/app/pages/pricing/pricing-page.html - 209 + 211 @@ -1948,11 +1968,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 52 + 55 apps/client/src/app/pages/pricing/pricing-page.html - 213 + 215 @@ -1964,11 +1984,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 56 + 59 apps/client/src/app/pages/pricing/pricing-page.html - 217 + 219 @@ -1980,7 +2000,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 237 + 223 @@ -1992,11 +2012,11 @@ apps/client/src/app/pages/pricing/pricing-page.html - 72 + 75 apps/client/src/app/pages/pricing/pricing-page.html - 261 + 246 @@ -2052,7 +2072,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 299 + 284 @@ -2064,7 +2084,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 383 + 365 @@ -2076,7 +2096,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -2088,7 +2108,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -2100,7 +2120,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 @@ -2112,7 +2132,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 436 + 418 @@ -2144,7 +2164,7 @@ 请输入您的优惠券代码。 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 215 + 218 @@ -2152,7 +2172,7 @@ 无法兑换优惠券代码 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 179 + 182 @@ -2160,7 +2180,7 @@ 优惠券代码已被兑换 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 192 + 195 @@ -2168,7 +2188,7 @@ 重新加载 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 193 + 196 @@ -2184,7 +2204,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 283 + 268 @@ -2192,7 +2212,7 @@ 尝试高级版 apps/client/src/app/components/user-account-membership/user-account-membership.html - 49 + 52 @@ -2200,7 +2220,7 @@ 兑换优惠券 apps/client/src/app/components/user-account-membership/user-account-membership.html - 63 + 66 @@ -2260,7 +2280,7 @@ 语言环境 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 437 + 441 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2362,6 +2382,10 @@ apps/client/src/app/components/user-account-settings/user-account-settings.html 252 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 11 + Export Data @@ -2392,7 +2416,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 194 + 195 @@ -2404,7 +2428,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 192 + 193 @@ -2412,7 +2436,7 @@ 好的 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 154 + 157 apps/client/src/app/core/http-response.interceptor.ts @@ -2420,7 +2444,7 @@ apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 195 + 196 @@ -2521,7 +2545,7 @@ for - for + 用于 apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 128 @@ -2550,6 +2574,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 375 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 66 + apps/client/src/app/pages/accounts/accounts-page.html 4 @@ -2558,13 +2586,17 @@ libs/common/src/lib/routes/routes.ts 69 + + libs/ui/src/lib/assistant/assistant.html + 84 + Oops, cash balance transfer has failed. 糟糕,现金余额转账失败。 apps/client/src/app/pages/accounts/accounts-page.component.ts - 324 + 341 @@ -2798,6 +2830,10 @@ apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html 189 + + apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html + 147 + apps/client/src/app/pages/blog/blog-page.html 5 @@ -2953,7 +2989,7 @@ per week - per week + 每周 apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 130 @@ -2970,14 +3006,34 @@ Get Started 立即开始 + + apps/client/src/app/components/header/header.component.html + 433 + apps/client/src/app/pages/features/features-page.html 320 + + apps/client/src/app/pages/landing/landing-page.html + 41 + + + apps/client/src/app/pages/landing/landing-page.html + 344 + + + apps/client/src/app/pages/pricing/pricing-page.html + 363 + apps/client/src/app/pages/public/public-page.html 242 + + apps/client/src/app/pages/resources/personal-finance-tools/product-page.html + 334 + Holdings @@ -3002,6 +3058,10 @@ libs/common/src/lib/routes/routes.ts 167 + + libs/ui/src/lib/assistant/assistant.html + 110 + Summary @@ -3018,6 +3078,10 @@ Markets 市场 + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 373 + apps/client/src/app/components/footer/footer.component.html 11 @@ -3101,38 +3165,18 @@ Edit access - Edit access + 编辑权限 apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html 11 - - Get Started - 开始使用 - - apps/client/src/app/pages/landing/landing-page.html - 42 - - - apps/client/src/app/pages/landing/landing-page.html - 346 - - - apps/client/src/app/pages/pricing/pricing-page.html - 378 - - - apps/client/src/app/pages/resources/personal-finance-tools/product-page.html - 334 - - Monthly Active Users 每月活跃用户数 apps/client/src/app/pages/landing/landing-page.html - 70 + 69 @@ -3140,7 +3184,7 @@ GitHub 上的星星 apps/client/src/app/pages/landing/landing-page.html - 88 + 87 apps/client/src/app/pages/open/open-page.html @@ -3152,7 +3196,7 @@ Docker Hub 拉取次数 apps/client/src/app/pages/landing/landing-page.html - 106 + 105 apps/client/src/app/pages/open/open-page.html @@ -3164,7 +3208,7 @@ 如图所示 apps/client/src/app/pages/landing/landing-page.html - 115 + 114 @@ -3172,7 +3216,7 @@ 保护你的资产。完善你的个人投资策略 apps/client/src/app/pages/landing/landing-page.html - 125 + 124 @@ -3180,7 +3224,7 @@ Ghostfolio 使忙碌的人们能够在不被追踪的情况下跟踪股票、ETF 或加密货币。 apps/client/src/app/pages/landing/landing-page.html - 129 + 128 @@ -3188,7 +3232,7 @@ 360° 视角 apps/client/src/app/pages/landing/landing-page.html - 139 + 138 @@ -3196,7 +3240,7 @@ 跨多个平台全面了解您的个人财务状况。 apps/client/src/app/pages/landing/landing-page.html - 142 + 141 @@ -3204,7 +3248,7 @@ Web3 就绪 apps/client/src/app/pages/landing/landing-page.html - 150 + 149 @@ -3212,7 +3256,7 @@ 匿名使用 Ghostfolio 并拥有您的财务数据。 apps/client/src/app/pages/landing/landing-page.html - 153 + 152 @@ -3220,12 +3264,12 @@ 通过强大的社区不断改进,从中受益。 apps/client/src/app/pages/landing/landing-page.html - 163 + 162 Get access to 80’000+ tickers from over 50 exchanges - Get access to 80’000+ tickers from over 50 exchanges + 获取来自 50 多个交易所的 80,000 多个行情的访问权限 apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 84 @@ -3236,7 +3280,7 @@ 为什么使用Ghostfolio apps/client/src/app/pages/landing/landing-page.html - 171 + 170 @@ -3244,7 +3288,7 @@ 如果您符合以下条件,那么 Ghostfolio 适合您... apps/client/src/app/pages/landing/landing-page.html - 173 + 172 @@ -3252,7 +3296,7 @@ 在多个平台上交易股票、ETF 或加密货币 apps/client/src/app/pages/landing/landing-page.html - 179 + 178 @@ -3260,7 +3304,7 @@ 采取买入并持有策略 apps/client/src/app/pages/landing/landing-page.html - 185 + 184 @@ -3268,7 +3312,7 @@ 有兴趣深入了解您的投资组合构成 apps/client/src/app/pages/landing/landing-page.html - 190 + 189 @@ -3276,7 +3320,7 @@ 重视隐私和数据所有权 apps/client/src/app/pages/landing/landing-page.html - 195 + 194 @@ -3284,7 +3328,7 @@ 进入极简主义 apps/client/src/app/pages/landing/landing-page.html - 198 + 197 @@ -3292,7 +3336,7 @@ 关心您的财务资源多元化 apps/client/src/app/pages/landing/landing-page.html - 202 + 201 @@ -3300,7 +3344,7 @@ 对财务独立感兴趣 apps/client/src/app/pages/landing/landing-page.html - 206 + 205 @@ -3308,7 +3352,7 @@ 年对电子表格说不 apps/client/src/app/pages/landing/landing-page.html - 210 + 209 @@ -3316,7 +3360,7 @@ 仍在阅读此列表 apps/client/src/app/pages/landing/landing-page.html - 213 + 212 @@ -3324,7 +3368,7 @@ 了解有关 Ghostfolio 的更多信息 apps/client/src/app/pages/landing/landing-page.html - 218 + 217 @@ -3332,7 +3376,7 @@ 听听我们的用户怎么说 apps/client/src/app/pages/landing/landing-page.html - 227 + 226 @@ -3340,7 +3384,7 @@ 来自世界各地的会员正在使用Ghostfolio 高级版 apps/client/src/app/pages/landing/landing-page.html - 266 + 265 @@ -3348,7 +3392,7 @@ Ghostfolio 如何工作? apps/client/src/app/pages/landing/landing-page.html - 283 + 282 @@ -3356,12 +3400,12 @@ 只需 3 步即可开始 apps/client/src/app/pages/landing/landing-page.html - 285 + 284 less than - less than + 少于 apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 129 @@ -3372,7 +3416,7 @@ 匿名注册* apps/client/src/app/pages/landing/landing-page.html - 291 + 290 @@ -3380,7 +3424,7 @@ * 无需电子邮件地址或信用卡 apps/client/src/app/pages/landing/landing-page.html - 293 + 292 @@ -3388,7 +3432,7 @@ 添加您的任何历史交易 apps/client/src/app/pages/landing/landing-page.html - 305 + 304 @@ -3396,7 +3440,7 @@ 获取有关您的投资组合构成的宝贵见解 apps/client/src/app/pages/landing/landing-page.html - 317 + 316 @@ -3404,7 +3448,7 @@ 准备好了吗? apps/client/src/app/pages/landing/landing-page.html - 331 + 330 @@ -3425,7 +3469,7 @@ Ghostfolio Status - Ghostfolio Status + Ghostfolio 状态 apps/client/src/app/pages/about/overview/about-overview-page.html 62 @@ -3433,10 +3477,10 @@ with your university e-mail address - with your university e-mail address + 使用您的学校电子邮件地址 apps/client/src/app/pages/pricing/pricing-page.html - 365 + 351 @@ -3465,7 +3509,7 @@ and a safe withdrawal rate (SWR) of - and a safe withdrawal rate (SWR) of + 和安全取款率 (SWR) 为 apps/client/src/app/pages/portfolio/fire/fire-page.html 107 @@ -3489,7 +3533,7 @@ Job ID - Job ID + 作业 ID apps/client/src/app/components/admin-jobs/admin-jobs.html 34 @@ -3550,6 +3594,10 @@ apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 342 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 75 + apps/client/src/app/pages/portfolio/activities/activities-page.html 4 @@ -3568,7 +3616,7 @@ 您确定要删除这些活动吗? libs/ui/src/lib/activities-table/activities-table.component.ts - 270 + 279 @@ -3648,7 +3696,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 213 + 217 @@ -3656,7 +3704,7 @@ 导入活动记录 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 91 + 92 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3664,7 +3712,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 378 + 382 @@ -3672,7 +3720,7 @@ 导入股息 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 136 + 137 libs/ui/src/lib/activities-table/activities-table.component.html @@ -3680,7 +3728,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 390 + 394 @@ -3688,7 +3736,7 @@ 正在导入数据... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 174 + 175 @@ -3696,12 +3744,12 @@ 导入已完成 apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 184 + 185 or start a discussion at - or start a discussion at + 或在以下位置开始讨论 apps/client/src/app/pages/about/overview/about-overview-page.html 94 @@ -3712,7 +3760,7 @@ 验证数据... apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts - 298 + 299 @@ -3739,8 +3787,8 @@ 32 - libs/ui/src/lib/assistant/assistant.html - 207 + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 26 @@ -3889,7 +3937,7 @@ Exclude from Analysis - Exclude from Analysis + 排除在分析之外 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html 90 @@ -3913,7 +3961,7 @@ Latest activities - Latest activities + 最新活动 apps/client/src/app/pages/public/public-page.html 211 @@ -3981,10 +4029,10 @@ Looking for a student discount? - Looking for a student discount? + 寻找学生折扣? apps/client/src/app/pages/pricing/pricing-page.html - 359 + 345 @@ -4000,7 +4048,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 310 + 365 apps/client/src/app/pages/features/features-page.html @@ -4115,14 +4163,6 @@ 7 - - Holdings - 持仓 - - libs/ui/src/lib/assistant/assistant.html - 110 - - Pricing 价格 @@ -4165,7 +4205,7 @@ Our official Ghostfolio Premium cloud offering is the easiest way to get started. Due to the time it saves, this will be the best option for most people. Revenue is used to cover operational costs for the hosting infrastructure and professional data providers, and to fund ongoing development. - 我们的官方 Ghostfolio Premium 云产品是最简单的入门方法。由于它节省了时间,这对于大多数人来说将是最佳选择。收入用于支付托管基础设施的成本和资助持续开发。 + 我们的官方 Ghostfolio Premium 云产品是最简单的入门方法。由于它节省了时间,这对于大多数人来说将是最佳选择。收入用于支付托管基础设施的成本和资助持续开发。 apps/client/src/app/pages/pricing/pricing-page.html 7 @@ -4192,15 +4232,11 @@ 无限交易 apps/client/src/app/pages/pricing/pricing-page.html - 32 - - - apps/client/src/app/pages/pricing/pricing-page.html - 121 + 35 apps/client/src/app/pages/pricing/pricing-page.html - 193 + 127 @@ -4208,15 +4244,11 @@ 无限账户 apps/client/src/app/pages/pricing/pricing-page.html - 36 - - - apps/client/src/app/pages/pricing/pricing-page.html - 125 + 39 apps/client/src/app/pages/pricing/pricing-page.html - 197 + 131 @@ -4224,15 +4256,11 @@ 投资组合表现 apps/client/src/app/pages/pricing/pricing-page.html - 40 - - - apps/client/src/app/pages/pricing/pricing-page.html - 129 + 43 apps/client/src/app/pages/pricing/pricing-page.html - 201 + 135 @@ -4240,15 +4268,11 @@ 数据导入与导出 apps/client/src/app/pages/pricing/pricing-page.html - 60 - - - apps/client/src/app/pages/pricing/pricing-page.html - 133 + 63 apps/client/src/app/pages/pricing/pricing-page.html - 221 + 139 @@ -4256,7 +4280,7 @@ 社区支持 apps/client/src/app/pages/pricing/pricing-page.html - 77 + 80 @@ -4264,7 +4288,7 @@ 自托管,手动更新。 apps/client/src/app/pages/pricing/pricing-page.html - 81 + 84 @@ -4272,11 +4296,11 @@ 自由的 apps/client/src/app/pages/pricing/pricing-page.html - 83 + 86 apps/client/src/app/pages/pricing/pricing-page.html - 146 + 152 @@ -4284,7 +4308,7 @@ 适合刚开始交易的新投资者。 apps/client/src/app/pages/pricing/pricing-page.html - 116 + 119 @@ -4292,11 +4316,11 @@ 完全托管的 Ghostfolio 云产品。 apps/client/src/app/pages/pricing/pricing-page.html - 144 + 150 apps/client/src/app/pages/pricing/pricing-page.html - 270 + 255 @@ -4304,7 +4328,7 @@ 适合需要全面了解其金融资产的雄心勃勃的投资者。 apps/client/src/app/pages/pricing/pricing-page.html - 187 + 193 @@ -4312,7 +4336,7 @@ 电子邮件和聊天支持 apps/client/src/app/pages/pricing/pricing-page.html - 266 + 251 @@ -4328,7 +4352,7 @@ apps/client/src/app/pages/pricing/pricing-page.html - 297 + 282 @@ -4336,7 +4360,7 @@ 一次性付款,无自动续订。 apps/client/src/app/pages/pricing/pricing-page.html - 303 + 288 @@ -4344,7 +4368,7 @@ 免费。 apps/client/src/app/pages/pricing/pricing-page.html - 380 + 365 @@ -4365,7 +4389,7 @@ Sustainable retirement income - Sustainable retirement income + 可持续的退休收入 apps/client/src/app/pages/portfolio/fire/fire-page.html 40 @@ -4391,20 +4415,12 @@ 281 - - Continue with Internet Identity - 继续互联网身份 - - apps/client/src/app/pages/register/register-page.html - 42 - - Continue with Google 继续使用谷歌 apps/client/src/app/pages/register/register-page.html - 53 + 43 @@ -4506,7 +4522,7 @@ per month - per month + 每月 apps/client/src/app/pages/portfolio/fire/fire-page.html 92 @@ -4522,7 +4538,7 @@ Website of Thomas Kaul - Website of Thomas Kaul + Thomas Kaul 的网站 apps/client/src/app/pages/about/overview/about-overview-page.html 44 @@ -4751,6 +4767,10 @@ Membership 会员资格 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 48 + libs/common/src/lib/routes/routes.ts 31 @@ -4762,10 +4782,10 @@ Request it - Request it + 请求它 apps/client/src/app/pages/pricing/pricing-page.html - 361 + 347 @@ -4817,7 +4837,7 @@ 您确实要删除该帐户余额吗? libs/ui/src/lib/account-balances/account-balances.component.ts - 120 + 121 @@ -4829,7 +4849,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 403 + 407 @@ -4841,7 +4861,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 416 + 420 @@ -4849,7 +4869,7 @@ 草稿 libs/ui/src/lib/activities-table/activities-table.component.html - 142 + 144 @@ -4857,7 +4877,7 @@ 克隆 libs/ui/src/lib/activities-table/activities-table.component.html - 447 + 459 @@ -4865,7 +4885,7 @@ 将汇票导出为 ICS libs/ui/src/lib/activities-table/activities-table.component.html - 457 + 469 @@ -4873,7 +4893,7 @@ 您确实要删除此活动吗? libs/ui/src/lib/activities-table/activities-table.component.ts - 280 + 289 @@ -4883,6 +4903,10 @@ apps/client/src/app/components/admin-settings/admin-settings.component.html 106 + + libs/ui/src/lib/assistant/assistant.html + 140 + 50-Day Trend @@ -4902,7 +4926,7 @@ , - , + , apps/client/src/app/pages/portfolio/fire/fire-page.html 93 @@ -4926,10 +4950,10 @@ contact us - contact us + 联系我们 apps/client/src/app/pages/pricing/pricing-page.html - 353 + 339 @@ -4989,7 +5013,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 298 + 352 libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -5045,14 +5069,14 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 311 + 315 - libs/ui/src/lib/assistant/assistant.html - 185 + libs/ui/src/lib/i18n.ts + 4 - libs/ui/src/lib/i18n.ts + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html 4 @@ -5087,14 +5111,14 @@ apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 290 - - libs/ui/src/lib/assistant/assistant.html - 246 - libs/ui/src/lib/i18n.ts 6 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 64 + Asset Sub Class @@ -5161,7 +5185,7 @@ 应急基金 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 156 + 164 apps/client/src/app/pages/features/features-page.html @@ -5237,7 +5261,7 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 412 + 413 @@ -5295,14 +5319,14 @@ Tag 标签 - - libs/ui/src/lib/assistant/assistant.html - 235 - libs/ui/src/lib/i18n.ts 31 + + libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html + 53 + Year @@ -5312,6 +5336,18 @@ 32 + + View Details + 查看详细信息 + + apps/client/src/app/components/admin-users/admin-users.html + 225 + + + libs/ui/src/lib/accounts-table/accounts-table.component.html + 307 + + Years @@ -5341,7 +5377,7 @@ libs/ui/src/lib/activities-table/activities-table.component.html - 237 + 241 libs/ui/src/lib/i18n.ts @@ -5369,7 +5405,7 @@ 卖出 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 43 + 44 libs/ui/src/lib/i18n.ts @@ -5381,7 +5417,7 @@ 现金 apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 190 + 212 libs/ui/src/lib/i18n.ts @@ -5424,6 +5460,14 @@ 51 + + Authentication + Authentication + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 32 + + Bond 债券 @@ -5514,7 +5558,7 @@ If you retire today, you would be able to withdraw - If you retire today, you would be able to withdraw + 如果您今天退休,您将能够提取 apps/client/src/app/pages/portfolio/fire/fire-page.html 66 @@ -5597,11 +5641,11 @@ libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 414 + 415 libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts - 427 + 428 libs/ui/src/lib/top-holdings/top-holdings.component.html @@ -5621,7 +5665,7 @@ 日期范围 libs/ui/src/lib/assistant/assistant.html - 171 + 170 @@ -5637,7 +5681,7 @@ 测试 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 500 + 504 @@ -5645,12 +5689,12 @@ 哎呀!无法授予访问权限。 apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 142 + 141 Argentina - Argentina + 阿根廷 libs/ui/src/lib/i18n.ts 78 @@ -5720,6 +5764,14 @@ 193 + + Close Holding + 关闭持仓 + + apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html + 442 + + Absolute Asset Performance 绝对资产回报 @@ -5737,7 +5789,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html - 58 + 60 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -5754,10 +5806,10 @@ here - here + 这里 apps/client/src/app/pages/pricing/pricing-page.html - 364 + 350 @@ -5789,7 +5841,7 @@ 今年迄今为止 libs/ui/src/lib/assistant/assistant.component.ts - 395 + 377 @@ -5797,7 +5849,7 @@ 本周至今 libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5805,7 +5857,7 @@ 本月至今 libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5817,7 +5869,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 391 + 373 @@ -5829,7 +5881,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 387 + 369 @@ -5852,12 +5904,12 @@ 42 - + Reset Filters 重置过滤器 libs/ui/src/lib/assistant/assistant.html - 266 + 204 @@ -5877,7 +5929,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 405 + 387 @@ -5889,15 +5941,15 @@ libs/ui/src/lib/assistant/assistant.component.ts - 430 + 412 - + Apply Filters 应用过滤器 libs/ui/src/lib/assistant/assistant.html - 276 + 217 @@ -5930,7 +5982,7 @@ 数据收集 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 593 + 597 apps/client/src/app/components/admin-overview/admin-overview.html @@ -5991,7 +6043,7 @@ Indonesia - Indonesia + 印度尼西亚 libs/ui/src/lib/i18n.ts 90 @@ -6066,7 +6118,7 @@ 删除活动 libs/ui/src/lib/activities-table/activities-table.component.html - 67 + 69 @@ -6122,7 +6174,15 @@ 立即加入 或查看示例账户 apps/client/src/app/pages/landing/landing-page.html - 334 + 333 + + + + Include in + 包含在 + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 369 @@ -6298,7 +6358,7 @@ 开源 apps/client/src/app/pages/landing/landing-page.html - 160 + 159 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6397,6 +6457,14 @@ 83 + + View Holding + 查看持仓 + + libs/ui/src/lib/activities-table/activities-table.component.html + 446 + + Canada 加拿大 @@ -6535,15 +6603,15 @@ Oops! Could not update access. - Oops! Could not update access. + 哎呀!无法更新访问权限。 apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts - 179 + 178 based on your total assets of - based on your total assets of + 基于您总资产的 apps/client/src/app/pages/portfolio/fire/fire-page.html 95 @@ -6554,7 +6622,7 @@ 非活跃 apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 87 + 88 @@ -6566,7 +6634,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 598 + 602 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6618,7 +6686,7 @@ 关闭 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 600 + 604 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6657,6 +6725,14 @@ 11 + + Role + 角色 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 36 + + Yes @@ -6665,14 +6741,6 @@ 34 - - Accounts - Accounts - - libs/ui/src/lib/assistant/assistant.html - 84 - - Copy link to clipboard 复制链接到剪贴板 @@ -6699,10 +6767,10 @@ If you plan to open an account at - If you plan to open an account at + 如果您计划开通账户在 apps/client/src/app/pages/pricing/pricing-page.html - 329 + 315 @@ -6731,7 +6799,7 @@ send an e-mail to - send an e-mail to + 发送电子邮件至 apps/client/src/app/pages/about/overview/about-overview-page.html 87 @@ -6745,14 +6813,6 @@ 69 - - No auto-renewal. - 不自动续订。 - - apps/client/src/app/components/user-account-membership/user-account-membership.html - 70 - - This year 今年 @@ -6811,10 +6871,10 @@ to use our referral link and get a Ghostfolio Premium membership for one year - to use our referral link and get a Ghostfolio Premium membership for one year + 使用我们的推荐链接并获得一年的Ghostfolio Premium会员资格 apps/client/src/app/pages/pricing/pricing-page.html - 357 + 343 @@ -6891,7 +6951,7 @@ Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions. - Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions. + Ghostfolio 是一款轻量级的财富管理应用程序,旨在帮助个人跟踪股票、ETF 或加密货币,并做出基于数据的稳健投资决策。 apps/client/src/app/pages/about/overview/about-overview-page.html 10 @@ -7066,13 +7126,17 @@ apps/client/src/app/components/admin-users/admin-users.html 162 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 98 + Could not generate an API key 无法生成 API 密钥 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 141 + 144 @@ -7080,7 +7144,7 @@ 在您的自托管环境中设置此 API 密钥: apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 156 + 159 @@ -7088,7 +7152,7 @@ Ghostfolio Premium 数据提供者 API 密钥 apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 159 + 162 @@ -7096,7 +7160,7 @@ 您确定要生成新的 API 密钥吗? apps/client/src/app/components/user-account-membership/user-account-membership.component.ts - 164 + 167 @@ -7136,7 +7200,7 @@ 保存 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 609 + 613 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7156,7 +7220,7 @@ apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts - 73 + 106 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -7188,7 +7252,7 @@ apps/client/src/app/components/user-account-access/user-account-access.component.ts - 251 + 260 @@ -7201,7 +7265,7 @@ Check the system status at - Check the system status at + 检查系统状态 apps/client/src/app/pages/about/overview/about-overview-page.html 57 @@ -7252,7 +7316,7 @@ 默认市场价格 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 409 + 413 @@ -7260,7 +7324,7 @@ 模式 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 450 + 454 @@ -7268,7 +7332,7 @@ 选择器 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 466 + 470 @@ -7276,7 +7340,7 @@ HTTP 请求标头 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 422 + 426 @@ -7320,7 +7384,7 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 @@ -7340,16 +7404,16 @@ libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 367 + 368 libs/ui/src/lib/treemap-chart/treemap-chart.component.ts - 380 + 381 The project has been initiated by - The project has been initiated by + 该项目发起于 apps/client/src/app/pages/about/overview/about-overview-page.html 40 @@ -7440,11 +7504,11 @@ 安全令牌 apps/client/src/app/components/admin-users/admin-users.component.ts - 196 + 231 apps/client/src/app/components/user-account-access/user-account-access.component.ts - 169 + 170 @@ -7452,15 +7516,15 @@ 您确定要为此用户生成新的安全令牌吗? apps/client/src/app/components/admin-users/admin-users.component.ts - 201 + 236 Find account, holding or page... - Find account, holding or page... + 查找账户、持仓或页面... libs/ui/src/lib/assistant/assistant.component.ts - 162 + 153 @@ -7468,7 +7532,7 @@ 生成安全令牌 apps/client/src/app/components/admin-users/admin-users.html - 233 + 242 @@ -7549,7 +7613,7 @@ 包含 API 访问权限,适用于 apps/client/src/app/pages/pricing/pricing-page.html - 253 + 238 @@ -7629,7 +7693,7 @@ 您确定要删除此项目吗? libs/ui/src/lib/benchmark/benchmark.component.ts - 139 + 140 @@ -7729,15 +7793,7 @@ 158 - - Name - 名称 - - libs/ui/src/lib/benchmark/benchmark.component.html - 12 - - - + Quick Links 快速链接 @@ -7745,24 +7801,16 @@ 58 - - Asset Profiles - 资产概况 - - libs/ui/src/lib/assistant/assistant.html - 140 - - Live Demo 现场演示 apps/client/src/app/pages/landing/landing-page.html - 49 + 48 apps/client/src/app/pages/landing/landing-page.html - 351 + 349 libs/common/src/lib/routes/routes.ts @@ -7858,17 +7906,25 @@ Limited Offer! 限时优惠! + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 40 + apps/client/src/app/pages/pricing/pricing-page.html - 312 + 297 Get extra 获取额外 + + apps/client/src/app/components/user-account-membership/user-account-membership.html + 43 + apps/client/src/app/pages/pricing/pricing-page.html - 314 + 300 @@ -7889,7 +7945,7 @@ Current month - Current month + 当前月份 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 200 @@ -8061,7 +8117,7 @@ 您真的想要生成一个新的安全令牌吗? apps/client/src/app/components/user-account-access/user-account-access.component.ts - 174 + 175 @@ -8074,7 +8130,7 @@ If you encounter a bug, would like to suggest an improvement or a new feature, please join the Ghostfolio Slack community, post to @ghostfolio_ - If you encounter a bug, would like to suggest an improvement or a new feature, please join the Ghostfolio Slack community, post to @ghostfolio_ + 如果您遇到错误,想要建议改进或新功能,请加入 Ghostfolio Slack 社区,发布到 @ghostfolio_ apps/client/src/app/pages/about/overview/about-overview-page.html 69 @@ -8117,7 +8173,7 @@ 管理资产概况 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 442 + 466 @@ -8141,7 +8197,7 @@ 平均单位价格 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts - 111 + 113 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -8198,7 +8254,7 @@ Liquidity - Liquidity + 流动性 apps/client/src/app/pages/i18n/i18n-page.html 70 @@ -8206,7 +8262,7 @@ Buying Power - Buying Power + 购买力 apps/client/src/app/pages/i18n/i18n-page.html 71 @@ -8214,7 +8270,7 @@ Your buying power is below ${thresholdMin} ${baseCurrency} - Your buying power is below ${thresholdMin} ${baseCurrency} + 您的购买力低于 ${thresholdMin} ${baseCurrency} apps/client/src/app/pages/i18n/i18n-page.html 73 @@ -8222,7 +8278,7 @@ Your buying power is 0 ${baseCurrency} - Your buying power is 0 ${baseCurrency} + 您的购买力为 0 ${baseCurrency} apps/client/src/app/pages/i18n/i18n-page.html 77 @@ -8230,7 +8286,7 @@ Your buying power exceeds ${thresholdMin} ${baseCurrency} - Your buying power exceeds ${thresholdMin} ${baseCurrency} + 您的购买力超过了 ${thresholdMin} ${baseCurrency} apps/client/src/app/pages/i18n/i18n-page.html 80 @@ -8532,6 +8588,14 @@ 128 + + Registration Date + 注册日期 + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 20 + + Follow Ghostfolio on LinkedIn 在 LinkedIn 上关注 Ghostfolio diff --git a/apps/client/src/main.ts b/apps/client/src/main.ts index 2f656ddf2..fc8a9ef7a 100644 --- a/apps/client/src/main.ts +++ b/apps/client/src/main.ts @@ -1,17 +1,45 @@ import { locale } from '@ghostfolio/common/config'; -import { InfoItem } from '@ghostfolio/common/interfaces'; +import { InfoResponse } from '@ghostfolio/common/interfaces'; import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; -import { enableProdMode } from '@angular/core'; -import { LOCALE_ID } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { Platform } from '@angular/cdk/platform'; +import { + provideHttpClient, + withInterceptorsFromDi +} from '@angular/common/http'; +import { enableProdMode, importProvidersFrom, LOCALE_ID } from '@angular/core'; +import { + DateAdapter, + MAT_DATE_FORMATS, + MAT_DATE_LOCALE, + MatNativeDateModule +} from '@angular/material/core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { RouterModule, TitleStrategy } from '@angular/router'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { provideIonicAngular } from '@ionic/angular/standalone'; +import { provideMarkdown } from 'ngx-markdown'; +import { provideNgxSkeletonLoader } from 'ngx-skeleton-loader'; +import { NgxStripeModule, STRIPE_PUBLISHABLE_KEY } from 'ngx-stripe'; -import { AppModule } from './app/app.module'; +import { CustomDateAdapter } from './app/adapter/custom-date-adapter'; +import { DateFormats } from './app/adapter/date-formats'; +import { GfAppComponent } from './app/app.component'; +import { routes } from './app/app.routes'; +import { authInterceptorProviders } from './app/core/auth.interceptor'; +import { httpResponseInterceptorProviders } from './app/core/http-response.interceptor'; +import { LanguageService } from './app/core/language.service'; +import { ModulePreloadService } from './app/core/module-preload.service'; +import { GfNotificationModule } from './app/core/notification/notification.module'; +import { PageTitleStrategy } from './app/services/page-title.strategy'; import { environment } from './environments/environment'; (async () => { const response = await fetch('/api/v1/info'); - const info: InfoItem = await response.json(); + const info: InfoResponse = await response.json(); const utmSource = window.localStorage.getItem('utm_source') as | 'ios' | 'trusted-web-activity'; @@ -29,9 +57,54 @@ import { environment } from './environments/environment'; enableProdMode(); } - platformBrowserDynamic() - .bootstrapModule(AppModule, { - providers: [{ provide: LOCALE_ID, useValue: locale }] - }) - .catch((error) => console.error(error)); + await bootstrapApplication(GfAppComponent, { + providers: [ + authInterceptorProviders, + httpResponseInterceptorProviders, + importProvidersFrom( + GfNotificationModule, + MatNativeDateModule, + MatSnackBarModule, + MatTooltipModule, + NgxStripeModule.forRoot(environment.stripePublicKey), + RouterModule.forRoot(routes, { + anchorScrolling: 'enabled', + preloadingStrategy: ModulePreloadService, + scrollPositionRestoration: 'top' + }), + ServiceWorkerModule.register('ngsw-worker.js', { + enabled: environment.production, + registrationStrategy: 'registerImmediately' + }) + ), + LanguageService, + ModulePreloadService, + provideAnimations(), + provideHttpClient(withInterceptorsFromDi()), + provideIonicAngular(), + provideMarkdown(), + provideNgxSkeletonLoader(), + { + deps: [LanguageService, MAT_DATE_LOCALE, Platform], + provide: DateAdapter, + useClass: CustomDateAdapter + }, + { + provide: LOCALE_ID, + useValue: locale + }, + { + provide: MAT_DATE_FORMATS, + useValue: DateFormats + }, + { + provide: STRIPE_PUBLISHABLE_KEY, + useFactory: () => environment.stripePublicKey + }, + { + provide: TitleStrategy, + useClass: PageTitleStrategy + } + ] + }); })(); diff --git a/apps/client/src/polyfills.ts b/apps/client/src/polyfills.ts index bb1da3e23..df81e917b 100644 --- a/apps/client/src/polyfills.ts +++ b/apps/client/src/polyfills.ts @@ -52,3 +52,4 @@ import 'zone.js'; // Included with Angular CLI. */ import '@angular/localize/init'; +import 'reflect-metadata'; diff --git a/apps/client/src/styles.scss b/apps/client/src/styles.scss index 6c9742f23..b7a031bfa 100644 --- a/apps/client/src/styles.scss +++ b/apps/client/src/styles.scss @@ -1,5 +1,6 @@ @import './styles/bootstrap'; @import './styles/table'; +@import './styles/variables'; @import 'svgmap/dist/svgMap'; @@ -8,14 +9,18 @@ --font-family-sans-serif: 'Inter', Roboto, 'Helvetica Neue', sans-serif; --light-background: rgb(255, 255, 255); - --dark-primary-text: 0, 0, 0, 0.87; + --dark-primary-text: + #{red($dark-primary-text)}, #{green($dark-primary-text)}, + #{blue($dark-primary-text)}, #{alpha($dark-primary-text)}; --dark-secondary-text: 0, 0, 0, 0.54; --dark-accent-text: 0, 0, 0, 0.87; --dark-warn-text: 0, 0, 0, 0.87; --dark-disabled-text: 0, 0, 0, 0.38; --dark-dividers: 0, 0, 0, 0.12; --dark-focused: 0, 0, 0, 0.12; - --light-primary-text: 255, 255, 255, 1; + --light-primary-text: + #{red($light-primary-text)}, #{green($light-primary-text)}, + #{blue($light-primary-text)}, #{alpha($light-primary-text)}; --light-secondary-text: 255, 255, 255, 0.7; --light-accent-text: 255, 255, 255, 1; --light-warn-text: 255, 255, 255, 1; diff --git a/apps/client/src/styles/theme.scss b/apps/client/src/styles/theme.scss index fe9fd44a5..8dd6d8e36 100644 --- a/apps/client/src/styles/theme.scss +++ b/apps/client/src/styles/theme.scss @@ -1,9 +1,6 @@ @use '@angular/material' as mat; -$dark-primary-text: rgba(black, 0.87); -$light-primary-text: white; - -$mat-css-dark-theme-selector: '.theme-dark'; +@use './variables.scss' as variables; $gf-primary: ( 50: var(--gf-theme-primary-50), @@ -21,20 +18,20 @@ $gf-primary: ( A400: var(--gf-theme-primary-A400), A700: var(--gf-theme-primary-A700), contrast: ( - 50: $dark-primary-text, - 100: $dark-primary-text, - 200: $dark-primary-text, - 300: $light-primary-text, - 400: $light-primary-text, - 500: $light-primary-text, - 600: $light-primary-text, - 700: $light-primary-text, - 800: $light-primary-text, - 900: $light-primary-text, - A100: $dark-primary-text, - A200: $light-primary-text, - A400: $light-primary-text, - A700: $light-primary-text + 50: variables.$dark-primary-text, + 100: variables.$dark-primary-text, + 200: variables.$dark-primary-text, + 300: variables.$light-primary-text, + 400: variables.$light-primary-text, + 500: variables.$light-primary-text, + 600: variables.$light-primary-text, + 700: variables.$light-primary-text, + 800: variables.$light-primary-text, + 900: variables.$light-primary-text, + A100: variables.$dark-primary-text, + A200: variables.$light-primary-text, + A400: variables.$light-primary-text, + A700: variables.$light-primary-text ) ); @@ -54,20 +51,20 @@ $gf-secondary: ( A400: var(--gf-theme-secondary-A400), A700: var(--gf-theme-secondary-A700), contrast: ( - 50: $dark-primary-text, - 100: $dark-primary-text, - 200: $dark-primary-text, - 300: $light-primary-text, - 400: $light-primary-text, - 500: $light-primary-text, - 600: $light-primary-text, - 700: $light-primary-text, - 800: $light-primary-text, - 900: $light-primary-text, - A100: $dark-primary-text, - A200: $light-primary-text, - A400: $light-primary-text, - A700: $light-primary-text + 50: variables.$dark-primary-text, + 100: variables.$dark-primary-text, + 200: variables.$dark-primary-text, + 300: variables.$light-primary-text, + 400: variables.$light-primary-text, + 500: variables.$light-primary-text, + 600: variables.$light-primary-text, + 700: variables.$light-primary-text, + 800: variables.$light-primary-text, + 900: variables.$light-primary-text, + A100: variables.$dark-primary-text, + A200: variables.$light-primary-text, + A400: variables.$light-primary-text, + A700: variables.$light-primary-text ) ); diff --git a/apps/client/src/styles/variables.scss b/apps/client/src/styles/variables.scss new file mode 100644 index 000000000..061c182fd --- /dev/null +++ b/apps/client/src/styles/variables.scss @@ -0,0 +1,4 @@ +$dark-primary-text: rgba(black, 0.87); +$light-primary-text: rgba(white, 1); + +$mat-css-dark-theme-selector: '.theme-dark'; diff --git a/apps/ui-e2e/cypress.json b/apps/ui-e2e/cypress.json deleted file mode 100644 index 71520788e..000000000 --- a/apps/ui-e2e/cypress.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "fileServerFolder": ".", - "fixturesFolder": "./src/fixtures", - "integrationFolder": "./src/integration", - "modifyObstructiveCode": false, - "supportFile": "./src/support/index.ts", - "pluginsFile": "./src/plugins/index", - "video": true, - "videosFolder": "../../dist/cypress/apps/ui-e2e/videos", - "screenshotsFolder": "../../dist/cypress/apps/ui-e2e/screenshots", - "chromeWebSecurity": false, - "baseUrl": "http://localhost:4400" -} diff --git a/apps/ui-e2e/eslint.config.cjs b/apps/ui-e2e/eslint.config.cjs deleted file mode 100644 index 5e6707635..000000000 --- a/apps/ui-e2e/eslint.config.cjs +++ /dev/null @@ -1,33 +0,0 @@ -const { FlatCompat } = require('@eslint/eslintrc'); -const js = require('@eslint/js'); -const baseConfig = require('../../eslint.config.cjs'); - -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended -}); - -module.exports = [ - { - ignores: ['**/dist'] - }, - ...baseConfig, - ...compat.extends('plugin:cypress/recommended'), - { - files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], - // Override or add rules here - rules: {}, - languageOptions: { - parserOptions: { - project: ['apps/ui-e2e/tsconfig.json'] - } - } - }, - { - files: ['src/plugins/index.js'], - rules: { - '@typescript-eslint/no-var-requires': 'off', - 'no-undef': 'off' - } - } -]; diff --git a/apps/ui-e2e/project.json b/apps/ui-e2e/project.json deleted file mode 100644 index a5b4cf53a..000000000 --- a/apps/ui-e2e/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "ui-e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/ui-e2e/src", - "projectType": "application", - "tags": [], - "implicitDependencies": ["ui"], - "targets": { - "e2e": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/ui-e2e/cypress.json", - "devServerTarget": "ui:storybook" - }, - "configurations": { - "ci": { - "devServerTarget": "ui:storybook:ci" - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "options": { - "lintFilePatterns": ["apps/ui-e2e/**/*.{js,ts}"] - } - } - } -} diff --git a/apps/ui-e2e/src/fixtures/example.json b/apps/ui-e2e/src/fixtures/example.json deleted file mode 100644 index 294cbed6c..000000000 --- a/apps/ui-e2e/src/fixtures/example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io" -} diff --git a/apps/ui-e2e/src/integration/value/value.component.spec.ts b/apps/ui-e2e/src/integration/value/value.component.spec.ts deleted file mode 100644 index 5b90784e7..000000000 --- a/apps/ui-e2e/src/integration/value/value.component.spec.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe('ui', () => { - beforeEach(() => cy.visit('/iframe.html?id=valuecomponent--loading')); - it('should render the component', () => { - cy.get('gf-value').should('exist'); - }); -}); diff --git a/apps/ui-e2e/src/plugins/index.js b/apps/ui-e2e/src/plugins/index.js deleted file mode 100644 index 63aa33cbe..000000000 --- a/apps/ui-e2e/src/plugins/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor'); - -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - - // Preprocess Typescript file using Nx helper - on('file:preprocessor', preprocessTypescript(config)); -}; diff --git a/apps/ui-e2e/src/support/commands.ts b/apps/ui-e2e/src/support/commands.ts deleted file mode 100644 index 310f1fa0e..000000000 --- a/apps/ui-e2e/src/support/commands.ts +++ /dev/null @@ -1,33 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -// eslint-disable-next-line @typescript-eslint/no-namespace -declare namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - } -} -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/ui-e2e/src/support/index.ts b/apps/ui-e2e/src/support/index.ts deleted file mode 100644 index fad130159..000000000 --- a/apps/ui-e2e/src/support/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** -// Import commands.js using ES2015 syntax: -import './commands'; diff --git a/apps/ui-e2e/tsconfig.json b/apps/ui-e2e/tsconfig.json deleted file mode 100644 index c4f818ecd..000000000 --- a/apps/ui-e2e/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "sourceMap": false, - "outDir": "../../dist/out-tsc", - "allowJs": true, - "types": ["cypress", "node"] - }, - "include": ["src/**/*.ts", "src/**/*.js"] -} diff --git a/eslint.config.cjs b/eslint.config.cjs index c7e08821c..5962e261d 100644 --- a/eslint.config.cjs +++ b/eslint.config.cjs @@ -18,16 +18,19 @@ module.exports = [ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ - 'warn', + 'error', { - enforceBuildableLibDependency: true, allow: [], + allowCircularSelfDependency: true, depConstraints: [ { sourceTag: '*', onlyDependOnLibsWithTags: ['*'] } - ] + ], + enforceBuildableLibDependency: true, + // Temporary fix, should be removed eventually + ignoredCircularDependencies: [['client', 'ui']] } ], '@typescript-eslint/no-extra-semi': 'error', @@ -152,7 +155,6 @@ module.exports = [ // The following rules are part of eslint:recommended // and can be remove once solved - 'no-constant-binary-expression': 'warn', 'no-loss-of-precision': 'warn', // The following rules are part of @typescript-eslint/recommended-type-checked @@ -170,7 +172,6 @@ module.exports = [ '@typescript-eslint/no-unsafe-argument': 'warn', '@typescript-eslint/no-unsafe-assignment': 'warn', '@typescript-eslint/no-unsafe-enum-comparison': 'warn', - '@typescript-eslint/no-unsafe-function-type': 'warn', '@typescript-eslint/no-unsafe-member-access': 'warn', '@typescript-eslint/no-unsafe-return': 'warn', '@typescript-eslint/no-unsafe-call': 'warn', @@ -189,8 +190,7 @@ module.exports = [ // The following rules are part of @typescript-eslint/stylistic-type-checked // and can be remove once solved - '@typescript-eslint/prefer-nullish-coalescing': 'warn', // TODO: Requires strictNullChecks: true - '@typescript-eslint/prefer-regexp-exec': 'warn' + '@typescript-eslint/prefer-nullish-coalescing': 'warn' // TODO: Requires strictNullChecks: true } })) ]; diff --git a/apps/api/src/app/auth-device/auth-device.dto.ts b/libs/common/src/lib/dtos/auth-device.dto.ts similarity index 100% rename from apps/api/src/app/auth-device/auth-device.dto.ts rename to libs/common/src/lib/dtos/auth-device.dto.ts diff --git a/apps/api/src/app/access/create-access.dto.ts b/libs/common/src/lib/dtos/create-access.dto.ts similarity index 100% rename from apps/api/src/app/access/create-access.dto.ts rename to libs/common/src/lib/dtos/create-access.dto.ts diff --git a/apps/api/src/app/account-balance/create-account-balance.dto.ts b/libs/common/src/lib/dtos/create-account-balance.dto.ts similarity index 100% rename from apps/api/src/app/account-balance/create-account-balance.dto.ts rename to libs/common/src/lib/dtos/create-account-balance.dto.ts diff --git a/apps/api/src/app/import/create-account-with-balances.dto.ts b/libs/common/src/lib/dtos/create-account-with-balances.dto.ts similarity index 75% rename from apps/api/src/app/import/create-account-with-balances.dto.ts rename to libs/common/src/lib/dtos/create-account-with-balances.dto.ts index 6cf4057f8..2d1d3ed2a 100644 --- a/apps/api/src/app/import/create-account-with-balances.dto.ts +++ b/libs/common/src/lib/dtos/create-account-with-balances.dto.ts @@ -1,8 +1,9 @@ -import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { AccountBalance } from '@ghostfolio/common/interfaces'; import { IsArray, IsOptional } from 'class-validator'; +import { CreateAccountDto } from './create-account.dto'; + export class CreateAccountWithBalancesDto extends CreateAccountDto { @IsArray() @IsOptional() diff --git a/apps/api/src/app/account/create-account.dto.ts b/libs/common/src/lib/dtos/create-account.dto.ts similarity index 89% rename from apps/api/src/app/account/create-account.dto.ts rename to libs/common/src/lib/dtos/create-account.dto.ts index b331d4ec7..fa88580f1 100644 --- a/apps/api/src/app/account/create-account.dto.ts +++ b/libs/common/src/lib/dtos/create-account.dto.ts @@ -1,4 +1,4 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { Transform, TransformFnParams } from 'class-transformer'; import { diff --git a/apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts b/libs/common/src/lib/dtos/create-asset-profile-with-market-data.dto.ts similarity index 85% rename from apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts rename to libs/common/src/lib/dtos/create-asset-profile-with-market-data.dto.ts index fd90ab1af..04611371d 100644 --- a/apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts +++ b/libs/common/src/lib/dtos/create-asset-profile-with-market-data.dto.ts @@ -3,7 +3,7 @@ import { MarketData } from '@ghostfolio/common/interfaces'; import { DataSource } from '@prisma/client'; import { IsArray, IsEnum, IsOptional } from 'class-validator'; -import { CreateAssetProfileDto } from '../admin/create-asset-profile.dto'; +import { CreateAssetProfileDto } from './create-asset-profile.dto'; export class CreateAssetProfileWithMarketDataDto extends CreateAssetProfileDto { @IsEnum([DataSource.MANUAL], { diff --git a/apps/api/src/app/admin/create-asset-profile.dto.ts b/libs/common/src/lib/dtos/create-asset-profile.dto.ts similarity index 94% rename from apps/api/src/app/admin/create-asset-profile.dto.ts rename to libs/common/src/lib/dtos/create-asset-profile.dto.ts index 8041b0f0e..80d45ba42 100644 --- a/apps/api/src/app/admin/create-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/create-asset-profile.dto.ts @@ -1,4 +1,4 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { diff --git a/apps/api/src/app/order/create-order.dto.ts b/libs/common/src/lib/dtos/create-order.dto.ts similarity index 85% rename from apps/api/src/app/order/create-order.dto.ts rename to libs/common/src/lib/dtos/create-order.dto.ts index ba7a1d868..dfd0d8aa5 100644 --- a/apps/api/src/app/order/create-order.dto.ts +++ b/libs/common/src/lib/dtos/create-order.dto.ts @@ -1,5 +1,5 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; @@ -44,7 +44,8 @@ export class CreateOrderDto { customCurrency?: string; @IsEnum(DataSource) - dataSource: DataSource; + @IsOptional() // Optional for type FEE, INTEREST, and LIABILITY (default data source is resolved in the backend) + dataSource?: DataSource; @IsISO8601() @Validate(IsAfter1970Constraint) diff --git a/apps/api/src/app/platform/create-platform.dto.ts b/libs/common/src/lib/dtos/create-platform.dto.ts similarity index 100% rename from apps/api/src/app/platform/create-platform.dto.ts rename to libs/common/src/lib/dtos/create-platform.dto.ts diff --git a/apps/api/src/app/endpoints/tags/create-tag.dto.ts b/libs/common/src/lib/dtos/create-tag.dto.ts similarity index 100% rename from apps/api/src/app/endpoints/tags/create-tag.dto.ts rename to libs/common/src/lib/dtos/create-tag.dto.ts diff --git a/apps/api/src/app/endpoints/watchlist/create-watchlist-item.dto.ts b/libs/common/src/lib/dtos/create-watchlist-item.dto.ts similarity index 100% rename from apps/api/src/app/endpoints/watchlist/create-watchlist-item.dto.ts rename to libs/common/src/lib/dtos/create-watchlist-item.dto.ts diff --git a/apps/api/src/app/user/delete-own-user.dto.ts b/libs/common/src/lib/dtos/delete-own-user.dto.ts similarity index 100% rename from apps/api/src/app/user/delete-own-user.dto.ts rename to libs/common/src/lib/dtos/delete-own-user.dto.ts diff --git a/libs/common/src/lib/dtos/index.ts b/libs/common/src/lib/dtos/index.ts new file mode 100644 index 000000000..3631d6eae --- /dev/null +++ b/libs/common/src/lib/dtos/index.ts @@ -0,0 +1,51 @@ +import { AuthDeviceDto } from './auth-device.dto'; +import { CreateAccessDto } from './create-access.dto'; +import { CreateAccountBalanceDto } from './create-account-balance.dto'; +import { CreateAccountWithBalancesDto } from './create-account-with-balances.dto'; +import { CreateAccountDto } from './create-account.dto'; +import { CreateAssetProfileWithMarketDataDto } from './create-asset-profile-with-market-data.dto'; +import { CreateAssetProfileDto } from './create-asset-profile.dto'; +import { CreateOrderDto } from './create-order.dto'; +import { CreatePlatformDto } from './create-platform.dto'; +import { CreateTagDto } from './create-tag.dto'; +import { CreateWatchlistItemDto } from './create-watchlist-item.dto'; +import { DeleteOwnUserDto } from './delete-own-user.dto'; +import { TransferBalanceDto } from './transfer-balance.dto'; +import { UpdateAccessDto } from './update-access.dto'; +import { UpdateAccountDto } from './update-account.dto'; +import { UpdateAssetProfileDto } from './update-asset-profile.dto'; +import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto'; +import { UpdateMarketDataDto } from './update-market-data.dto'; +import { UpdateOrderDto } from './update-order.dto'; +import { UpdateOwnAccessTokenDto } from './update-own-access-token.dto'; +import { UpdatePlatformDto } from './update-platform.dto'; +import { UpdatePropertyDto } from './update-property.dto'; +import { UpdateTagDto } from './update-tag.dto'; +import { UpdateUserSettingDto } from './update-user-setting.dto'; + +export { + AuthDeviceDto, + CreateAccessDto, + CreateAccountBalanceDto, + CreateAccountDto, + CreateAccountWithBalancesDto, + CreateAssetProfileDto, + CreateAssetProfileWithMarketDataDto, + CreateOrderDto, + CreatePlatformDto, + CreateTagDto, + CreateWatchlistItemDto, + DeleteOwnUserDto, + TransferBalanceDto, + UpdateAccessDto, + UpdateAccountDto, + UpdateAssetProfileDto, + UpdateBulkMarketDataDto, + UpdateMarketDataDto, + UpdateOrderDto, + UpdateOwnAccessTokenDto, + UpdatePlatformDto, + UpdatePropertyDto, + UpdateTagDto, + UpdateUserSettingDto +}; diff --git a/apps/api/src/app/account/transfer-balance.dto.ts b/libs/common/src/lib/dtos/transfer-balance.dto.ts similarity index 100% rename from apps/api/src/app/account/transfer-balance.dto.ts rename to libs/common/src/lib/dtos/transfer-balance.dto.ts diff --git a/apps/api/src/app/access/update-access.dto.ts b/libs/common/src/lib/dtos/update-access.dto.ts similarity index 100% rename from apps/api/src/app/access/update-access.dto.ts rename to libs/common/src/lib/dtos/update-access.dto.ts diff --git a/apps/api/src/app/account/update-account.dto.ts b/libs/common/src/lib/dtos/update-account.dto.ts similarity index 89% rename from apps/api/src/app/account/update-account.dto.ts rename to libs/common/src/lib/dtos/update-account.dto.ts index 3a721d873..066bacbfd 100644 --- a/apps/api/src/app/account/update-account.dto.ts +++ b/libs/common/src/lib/dtos/update-account.dto.ts @@ -1,4 +1,4 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { Transform, TransformFnParams } from 'class-transformer'; import { diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/libs/common/src/lib/dtos/update-asset-profile.dto.ts similarity index 93% rename from apps/api/src/app/admin/update-asset-profile.dto.ts rename to libs/common/src/lib/dtos/update-asset-profile.dto.ts index 5056dccdb..43f5aa617 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/update-asset-profile.dto.ts @@ -1,4 +1,4 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { diff --git a/apps/api/src/app/admin/update-bulk-market-data.dto.ts b/libs/common/src/lib/dtos/update-bulk-market-data.dto.ts similarity index 79% rename from apps/api/src/app/admin/update-bulk-market-data.dto.ts rename to libs/common/src/lib/dtos/update-bulk-market-data.dto.ts index da0da1272..f92112f24 100644 --- a/apps/api/src/app/admin/update-bulk-market-data.dto.ts +++ b/libs/common/src/lib/dtos/update-bulk-market-data.dto.ts @@ -1,8 +1,8 @@ +import { UpdateMarketDataDto } from '@ghostfolio/common/dtos'; + import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray } from 'class-validator'; -import { UpdateMarketDataDto } from './update-market-data.dto'; - export class UpdateBulkMarketDataDto { @ArrayNotEmpty() @IsArray() diff --git a/apps/api/src/app/admin/update-market-data.dto.ts b/libs/common/src/lib/dtos/update-market-data.dto.ts similarity index 100% rename from apps/api/src/app/admin/update-market-data.dto.ts rename to libs/common/src/lib/dtos/update-market-data.dto.ts diff --git a/apps/api/src/app/order/update-order.dto.ts b/libs/common/src/lib/dtos/update-order.dto.ts similarity index 94% rename from apps/api/src/app/order/update-order.dto.ts rename to libs/common/src/lib/dtos/update-order.dto.ts index 25c92532a..3656a8043 100644 --- a/apps/api/src/app/order/update-order.dto.ts +++ b/libs/common/src/lib/dtos/update-order.dto.ts @@ -1,5 +1,5 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; diff --git a/apps/api/src/app/user/update-own-access-token.dto.ts b/libs/common/src/lib/dtos/update-own-access-token.dto.ts similarity index 100% rename from apps/api/src/app/user/update-own-access-token.dto.ts rename to libs/common/src/lib/dtos/update-own-access-token.dto.ts diff --git a/apps/api/src/app/platform/update-platform.dto.ts b/libs/common/src/lib/dtos/update-platform.dto.ts similarity index 100% rename from apps/api/src/app/platform/update-platform.dto.ts rename to libs/common/src/lib/dtos/update-platform.dto.ts diff --git a/apps/api/src/services/property/property.dto.ts b/libs/common/src/lib/dtos/update-property.dto.ts similarity index 76% rename from apps/api/src/services/property/property.dto.ts rename to libs/common/src/lib/dtos/update-property.dto.ts index 037b4703c..77115759a 100644 --- a/apps/api/src/services/property/property.dto.ts +++ b/libs/common/src/lib/dtos/update-property.dto.ts @@ -1,6 +1,6 @@ import { IsOptional, IsString } from 'class-validator'; -export class PropertyDto { +export class UpdatePropertyDto { @IsOptional() @IsString() value: string; diff --git a/apps/api/src/app/endpoints/tags/update-tag.dto.ts b/libs/common/src/lib/dtos/update-tag.dto.ts similarity index 100% rename from apps/api/src/app/endpoints/tags/update-tag.dto.ts rename to libs/common/src/lib/dtos/update-tag.dto.ts diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/libs/common/src/lib/dtos/update-user-setting.dto.ts similarity index 96% rename from apps/api/src/app/user/update-user-setting.dto.ts rename to libs/common/src/lib/dtos/update-user-setting.dto.ts index 3ee59f7dd..cf7dff7e8 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/libs/common/src/lib/dtos/update-user-setting.dto.ts @@ -1,4 +1,3 @@ -import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { XRayRulesSettings } from '@ghostfolio/common/interfaces'; import type { ColorScheme, @@ -6,6 +5,7 @@ import type { HoldingsViewMode, ViewMode } from '@ghostfolio/common/types'; +import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; import { IsArray, diff --git a/apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.type.ts b/libs/common/src/lib/enums/confirmation-dialog.type.ts similarity index 100% rename from apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.type.ts rename to libs/common/src/lib/enums/confirmation-dialog.type.ts diff --git a/libs/common/src/lib/enums/index.ts b/libs/common/src/lib/enums/index.ts new file mode 100644 index 000000000..7384741de --- /dev/null +++ b/libs/common/src/lib/enums/index.ts @@ -0,0 +1,4 @@ +import { ConfirmationDialogType } from './confirmation-dialog.type'; +import { SubscriptionType } from './subscription-type.type'; + +export { ConfirmationDialogType, SubscriptionType }; diff --git a/libs/common/src/lib/types/subscription-type.type.ts b/libs/common/src/lib/enums/subscription-type.type.ts similarity index 100% rename from libs/common/src/lib/types/subscription-type.type.ts rename to libs/common/src/lib/enums/subscription-type.type.ts diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 97b762267..7452b604c 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -375,7 +375,7 @@ export function parseDate(date: string): Date { // Transform 'yyyyMMdd' format to supported format by parse function if (date?.length === 8) { - const match = date.match(/^(\d{4})(\d{2})(\d{2})$/); + const match = /^(\d{4})(\d{2})(\d{2})$/.exec(date); if (match) { const [, year, month, day] = match; diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/libs/common/src/lib/interfaces/activities.interface.ts similarity index 89% rename from apps/api/src/app/order/interfaces/activities.interface.ts rename to libs/common/src/lib/interfaces/activities.interface.ts index 01a5a60f0..8c88cc2cf 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/libs/common/src/lib/interfaces/activities.interface.ts @@ -3,11 +3,6 @@ import { AccountWithPlatform } from '@ghostfolio/common/types'; import { Order, Tag } from '@prisma/client'; -export interface Activities { - activities: Activity[]; - count: number; -} - export interface Activity extends Order { account?: AccountWithPlatform; error?: ActivityError; diff --git a/libs/common/src/lib/interfaces/admin-user.interface.ts b/libs/common/src/lib/interfaces/admin-user.interface.ts new file mode 100644 index 000000000..4cb02b16e --- /dev/null +++ b/libs/common/src/lib/interfaces/admin-user.interface.ts @@ -0,0 +1,15 @@ +import { Provider, Role, Subscription } from '@prisma/client'; + +export interface AdminUser { + accountCount: number; + activityCount: number; + country: string; + createdAt: Date; + dailyApiRequests: number; + engagement: number; + id: string; + lastActivity: Date; + provider: Provider; + role: Role; + subscription?: Subscription; +} diff --git a/libs/common/src/lib/interfaces/admin-users.interface.ts b/libs/common/src/lib/interfaces/admin-users.interface.ts deleted file mode 100644 index 79031425a..000000000 --- a/libs/common/src/lib/interfaces/admin-users.interface.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Role } from '@prisma/client'; - -export interface AdminUsers { - count: number; - users: { - accountCount: number; - activityCount: number; - country: string; - createdAt: Date; - dailyApiRequests: number; - engagement: number; - id: string; - lastActivity: Date; - role: Role; - }[]; -} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 578d9cec8..1d7991e40 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -1,5 +1,6 @@ import type { Access } from './access.interface'; import type { AccountBalance } from './account-balance.interface'; +import type { Activity, ActivityError } from './activities.interface'; import type { AdminData } from './admin-data.interface'; import type { AdminJobs } from './admin-jobs.interface'; import type { AdminMarketDataDetails } from './admin-market-data-details.interface'; @@ -7,7 +8,7 @@ import type { AdminMarketData, AdminMarketDataItem } from './admin-market-data.interface'; -import type { AdminUsers } from './admin-users.interface'; +import type { AdminUser } from './admin-user.interface'; import type { AssetClassSelectorOption } from './asset-class-selector-option.interface'; import type { AssetProfileIdentifier } from './asset-profile-identifier.interface'; import type { BenchmarkProperty } from './benchmark-property.interface'; @@ -15,7 +16,6 @@ import type { Benchmark } from './benchmark.interface'; import type { Coupon } from './coupon.interface'; import type { DataProviderInfo } from './data-provider-info.interface'; import type { EnhancedSymbolProfile } from './enhanced-symbol-profile.interface'; -import type { Export } from './export.interface'; import type { FilterGroup } from './filter-group.interface'; import type { Filter } from './filter.interface'; import type { FireWealth } from './fire-wealth.interface'; @@ -37,25 +37,38 @@ import type { Position } from './position.interface'; import type { Product } from './product'; import type { AccessTokenResponse } from './responses/access-token-response.interface'; import type { AccountBalancesResponse } from './responses/account-balances-response.interface'; +import type { AccountResponse } from './responses/account-response.interface'; import type { AccountsResponse } from './responses/accounts-response.interface'; +import type { ActivitiesResponse } from './responses/activities-response.interface'; +import type { ActivityResponse } from './responses/activity-response.interface'; +import type { AdminUserResponse } from './responses/admin-user-response.interface'; +import type { AdminUsersResponse } from './responses/admin-users-response.interface'; import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; +import type { AssetResponse } from './responses/asset-response.interface'; import type { BenchmarkMarketDataDetailsResponse } from './responses/benchmark-market-data-details-response.interface'; import type { BenchmarkResponse } from './responses/benchmark-response.interface'; +import type { CreateStripeCheckoutSessionResponse } from './responses/create-stripe-checkout-session-response.interface'; import type { DataEnhancerHealthResponse } from './responses/data-enhancer-health-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 { DataProviderHealthResponse } from './responses/data-provider-health-response.interface'; +import type { + DataProviderResponse, + DataProviderHistoricalResponse +} from './responses/data-provider-response.interface'; import type { DividendsResponse } from './responses/dividends-response.interface'; import type { ResponseError } from './responses/errors.interface'; +import type { ExportResponse } from './responses/export-response.interface'; import type { HistoricalResponse } from './responses/historical-response.interface'; import type { ImportResponse } from './responses/import-response.interface'; +import type { InfoResponse } from './responses/info-response.interface'; import type { LookupResponse } from './responses/lookup-response.interface'; import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface'; import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; import type { PortfolioDividendsResponse } from './responses/portfolio-dividends-response.interface'; -import { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface'; +import type { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface'; import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface'; import type { PortfolioInvestmentsResponse } from './responses/portfolio-investments.interface'; import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface'; @@ -63,13 +76,22 @@ import type { PortfolioReportResponse } from './responses/portfolio-report.inter import type { PublicPortfolioResponse } from './responses/public-portfolio-response.interface'; import type { QuotesResponse } from './responses/quotes-response.interface'; import type { WatchlistResponse } from './responses/watchlist-response.interface'; +import type { RuleSettings } from './rule-settings.interface'; import type { ScraperConfiguration } from './scraper-configuration.interface'; +import type { + AssertionCredentialJSON, + AttestationCredentialJSON, + PublicKeyCredentialCreationOptionsJSON, + PublicKeyCredentialRequestOptionsJSON +} from './simplewebauthn.interface'; import type { Statistics } from './statistics.interface'; import type { SubscriptionOffer } from './subscription-offer.interface'; +import type { SymbolItem } from './symbol-item.interface'; import type { SymbolMetrics } from './symbol-metrics.interface'; import type { SystemMessage } from './system-message.interface'; import type { TabConfiguration } from './tab-configuration.interface'; import type { ToggleOption } from './toggle-option.interface'; +import type { UserItem } from './user-item.interface'; import type { UserSettings } from './user-settings.interface'; import type { User } from './user.interface'; import type { XRayRulesSettings } from './x-ray-rules-settings.interface'; @@ -79,30 +101,43 @@ export { AccessTokenResponse, AccountBalance, AccountBalancesResponse, + AccountResponse, AccountsResponse, + ActivitiesResponse, + Activity, + ActivityError, + ActivityResponse, AdminData, AdminJobs, AdminMarketData, AdminMarketDataDetails, AdminMarketDataItem, - AdminUsers, + AdminUser, + AdminUserResponse, + AdminUsersResponse, AiPromptResponse, ApiKeyResponse, + AssertionCredentialJSON, AssetClassSelectorOption, AssetProfileIdentifier, + AssetResponse, + AttestationCredentialJSON, Benchmark, BenchmarkMarketDataDetailsResponse, BenchmarkProperty, BenchmarkResponse, Coupon, + CreateStripeCheckoutSessionResponse, DataEnhancerHealthResponse, DataProviderGhostfolioAssetProfileResponse, DataProviderGhostfolioStatusResponse, DataProviderHealthResponse, + DataProviderHistoricalResponse, DataProviderInfo, + DataProviderResponse, DividendsResponse, EnhancedSymbolProfile, - Export, + ExportResponse, Filter, FilterGroup, FireWealth, @@ -112,6 +147,7 @@ export { HoldingWithParents, ImportResponse, InfoItem, + InfoResponse, InvestmentItem, LineChartItem, LookupItem, @@ -134,17 +170,22 @@ export { PortfolioSummary, Position, Product, + PublicKeyCredentialCreationOptionsJSON, + PublicKeyCredentialRequestOptionsJSON, PublicPortfolioResponse, QuotesResponse, ResponseError, + RuleSettings, ScraperConfiguration, Statistics, SubscriptionOffer, - SystemMessage, + SymbolItem, SymbolMetrics, + SystemMessage, TabConfiguration, ToggleOption, User, + UserItem, UserSettings, WatchlistResponse, XRayRulesSettings diff --git a/libs/common/src/lib/interfaces/responses/account-response.interface.ts b/libs/common/src/lib/interfaces/responses/account-response.interface.ts new file mode 100644 index 000000000..3e954dc72 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/account-response.interface.ts @@ -0,0 +1,3 @@ +import { AccountWithValue } from '@ghostfolio/common/types'; + +export interface AccountResponse extends AccountWithValue {} diff --git a/libs/common/src/lib/interfaces/responses/activities-response.interface.ts b/libs/common/src/lib/interfaces/responses/activities-response.interface.ts new file mode 100644 index 000000000..863ae4665 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/activities-response.interface.ts @@ -0,0 +1,6 @@ +import { Activity } from '@ghostfolio/common/interfaces'; + +export interface ActivitiesResponse { + activities: Activity[]; + count: number; +} diff --git a/libs/common/src/lib/interfaces/responses/activity-response.interface.ts b/libs/common/src/lib/interfaces/responses/activity-response.interface.ts new file mode 100644 index 000000000..d26f13a5a --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/activity-response.interface.ts @@ -0,0 +1,3 @@ +import { Activity } from '@ghostfolio/common/interfaces'; + +export interface ActivityResponse extends Activity {} diff --git a/libs/common/src/lib/interfaces/responses/admin-user-response.interface.ts b/libs/common/src/lib/interfaces/responses/admin-user-response.interface.ts new file mode 100644 index 000000000..8e93fc097 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/admin-user-response.interface.ts @@ -0,0 +1,3 @@ +import { AdminUser } from '../admin-user.interface'; + +export interface AdminUserResponse extends AdminUser {} diff --git a/libs/common/src/lib/interfaces/responses/admin-users-response.interface.ts b/libs/common/src/lib/interfaces/responses/admin-users-response.interface.ts new file mode 100644 index 000000000..8dd058030 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/admin-users-response.interface.ts @@ -0,0 +1,6 @@ +import { AdminUser } from '../admin-user.interface'; + +export interface AdminUsersResponse { + count: number; + users: AdminUser[]; +} diff --git a/libs/common/src/lib/interfaces/responses/asset-response.interface.ts b/libs/common/src/lib/interfaces/responses/asset-response.interface.ts new file mode 100644 index 000000000..452ec0d3d --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/asset-response.interface.ts @@ -0,0 +1,3 @@ +import type { AdminMarketDataDetails } from '../admin-market-data-details.interface'; + +export interface AssetResponse extends AdminMarketDataDetails {} diff --git a/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts b/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts new file mode 100644 index 000000000..18c9e4400 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts @@ -0,0 +1,3 @@ +export interface CreateStripeCheckoutSessionResponse { + sessionId: string; +} diff --git a/libs/common/src/lib/interfaces/responses/data-provider-response.interface.ts b/libs/common/src/lib/interfaces/responses/data-provider-response.interface.ts new file mode 100644 index 000000000..ff152b1b2 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/data-provider-response.interface.ts @@ -0,0 +1,16 @@ +import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { MarketState } from '@ghostfolio/common/types'; + +import { DataSource } from '@prisma/client'; + +export interface DataProviderHistoricalResponse { + marketPrice: number; +} + +export interface DataProviderResponse { + currency: string; + dataProviderInfo?: DataProviderInfo; + dataSource: DataSource; + marketPrice: number; + marketState: MarketState; +} diff --git a/libs/common/src/lib/interfaces/responses/dividends-response.interface.ts b/libs/common/src/lib/interfaces/responses/dividends-response.interface.ts index f7cacf89a..8bbd8b755 100644 --- a/libs/common/src/lib/interfaces/responses/dividends-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/dividends-response.interface.ts @@ -1,7 +1,7 @@ -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderHistoricalResponse } from '@ghostfolio/common/interfaces'; export interface DividendsResponse { dividends: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; } diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/responses/export-response.interface.ts similarity index 64% rename from libs/common/src/lib/interfaces/export.interface.ts rename to libs/common/src/lib/interfaces/responses/export-response.interface.ts index 16a49b0ef..8b1697ca4 100644 --- a/libs/common/src/lib/interfaces/export.interface.ts +++ b/libs/common/src/lib/interfaces/responses/export-response.interface.ts @@ -7,10 +7,11 @@ import { Tag } from '@prisma/client'; -import { AccountBalance } from './account-balance.interface'; -import { MarketData } from './market-data.interface'; +import { AccountBalance } from '../account-balance.interface'; +import { MarketData } from '../market-data.interface'; +import { UserSettings } from '../user-settings.interface'; -export interface Export { +export interface ExportResponse { accounts: (Omit & { balances: AccountBalance[]; })[]; @@ -36,5 +37,10 @@ export interface Export { }; platforms: Platform[]; tags: Omit[]; - user: { settings: { currency: string } }; + user: { + settings: { + currency: UserSettings['baseCurrency']; + performanceCalculationType: UserSettings['performanceCalculationType']; + }; + }; } diff --git a/libs/common/src/lib/interfaces/responses/historical-response.interface.ts b/libs/common/src/lib/interfaces/responses/historical-response.interface.ts index 12309a352..211b19b4d 100644 --- a/libs/common/src/lib/interfaces/responses/historical-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/historical-response.interface.ts @@ -1,7 +1,7 @@ -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderHistoricalResponse } from '@ghostfolio/common/interfaces'; export interface HistoricalResponse { historicalData: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; } diff --git a/libs/common/src/lib/interfaces/responses/import-response.interface.ts b/libs/common/src/lib/interfaces/responses/import-response.interface.ts index be2da9837..24b0e4f4b 100644 --- a/libs/common/src/lib/interfaces/responses/import-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/import-response.interface.ts @@ -1,4 +1,4 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; +import { Activity } from '@ghostfolio/common/interfaces'; export interface ImportResponse { activities: Activity[]; diff --git a/libs/common/src/lib/interfaces/responses/info-response.interface.ts b/libs/common/src/lib/interfaces/responses/info-response.interface.ts new file mode 100644 index 000000000..45e62db73 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/info-response.interface.ts @@ -0,0 +1,3 @@ +import { InfoItem } from '../index'; + +export interface InfoResponse extends InfoItem {} diff --git a/libs/common/src/lib/interfaces/responses/market-data-of-markets-response.interface.ts b/libs/common/src/lib/interfaces/responses/market-data-of-markets-response.interface.ts index aecfbb28b..997a42737 100644 --- a/libs/common/src/lib/interfaces/responses/market-data-of-markets-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/market-data-of-markets-response.interface.ts @@ -1,4 +1,4 @@ -import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface'; +import { SymbolItem } from '@ghostfolio/common/interfaces'; export interface MarketDataOfMarketsResponse { fearAndGreedIndex: { diff --git a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts index b82a8f85d..31f027ee9 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts @@ -1,5 +1,5 @@ -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { + Activity, Benchmark, DataProviderInfo, EnhancedSymbolProfile, diff --git a/libs/common/src/lib/interfaces/responses/quotes-response.interface.ts b/libs/common/src/lib/interfaces/responses/quotes-response.interface.ts index 79c9d3024..933220ed7 100644 --- a/libs/common/src/lib/interfaces/responses/quotes-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/quotes-response.interface.ts @@ -1,5 +1,5 @@ -import { IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderResponse } from '@ghostfolio/common/interfaces'; export interface QuotesResponse { - quotes: { [symbol: string]: IDataProviderResponse }; + quotes: { [symbol: string]: DataProviderResponse }; } diff --git a/apps/api/src/models/interfaces/rule-settings.interface.ts b/libs/common/src/lib/interfaces/rule-settings.interface.ts similarity index 75% rename from apps/api/src/models/interfaces/rule-settings.interface.ts rename to libs/common/src/lib/interfaces/rule-settings.interface.ts index 377bab52b..ff22650ca 100644 --- a/apps/api/src/models/interfaces/rule-settings.interface.ts +++ b/libs/common/src/lib/interfaces/rule-settings.interface.ts @@ -1,3 +1,4 @@ export interface RuleSettings { isActive: boolean; + locale: string; } diff --git a/apps/api/src/app/auth/interfaces/simplewebauthn.ts b/libs/common/src/lib/interfaces/simplewebauthn.interface.ts similarity index 100% rename from apps/api/src/app/auth/interfaces/simplewebauthn.ts rename to libs/common/src/lib/interfaces/simplewebauthn.interface.ts diff --git a/apps/api/src/app/symbol/interfaces/symbol-item.interface.ts b/libs/common/src/lib/interfaces/symbol-item.interface.ts similarity index 100% rename from apps/api/src/app/symbol/interfaces/symbol-item.interface.ts rename to libs/common/src/lib/interfaces/symbol-item.interface.ts diff --git a/libs/common/src/lib/interfaces/system-message.interface.ts b/libs/common/src/lib/interfaces/system-message.interface.ts index 253bd5a27..617d40ea2 100644 --- a/libs/common/src/lib/interfaces/system-message.interface.ts +++ b/libs/common/src/lib/interfaces/system-message.interface.ts @@ -1,4 +1,4 @@ -import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; +import { SubscriptionType } from '@ghostfolio/common/enums'; export interface SystemMessage { message: string; diff --git a/apps/api/src/app/user/interfaces/user-item.interface.ts b/libs/common/src/lib/interfaces/user-item.interface.ts similarity index 100% rename from apps/api/src/app/user/interfaces/user-item.interface.ts rename to libs/common/src/lib/interfaces/user-item.interface.ts diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index a48317fad..e60f01915 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -1,6 +1,7 @@ -import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; +import { SubscriptionType } from '@ghostfolio/common/enums'; +import { AccountWithPlatform } from '@ghostfolio/common/types'; -import { Access, Account, Tag } from '@prisma/client'; +import { Access, Tag } from '@prisma/client'; import { SubscriptionOffer } from './subscription-offer.interface'; import { SystemMessage } from './system-message.interface'; @@ -9,7 +10,7 @@ import { UserSettings } from './user-settings.interface'; // TODO: Compare with UserWithSettings export interface User { access: Pick[]; - accounts: Account[]; + accounts: AccountWithPlatform[]; activitiesCount: number; dateOfFirstActivity: Date; id: string; diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index 51f327d32..597c27690 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -28,11 +28,12 @@ export const permissions = { deleteTag: 'deleteTag', deleteUser: 'deleteUser', deleteWatchlistItem: 'deleteWatchlistItem', + enableAuthGoogle: 'enableAuthGoogle', + enableAuthToken: 'enableAuthToken', enableDataProviderGhostfolio: 'enableDataProviderGhostfolio', enableFearAndGreedIndex: 'enableFearAndGreedIndex', enableImport: 'enableImport', enableBlog: 'enableBlog', - enableSocialLogin: 'enableSocialLogin', enableStatistics: 'enableStatistics', enableSubscription: 'enableSubscription', enableSubscriptionInterstitial: 'enableSubscriptionInterstitial', @@ -157,7 +158,7 @@ export function filterGlobalPermissions( if (aUtmSource === 'ios') { return globalPermissions.filter((permission) => { return ( - permission !== permissions.enableSocialLogin && + permission !== permissions.enableAuthGoogle && permission !== permissions.enableSubscription ); }); diff --git a/libs/common/src/lib/pipes/index.ts b/libs/common/src/lib/pipes/index.ts new file mode 100644 index 000000000..7b5ca4bac --- /dev/null +++ b/libs/common/src/lib/pipes/index.ts @@ -0,0 +1,3 @@ +import { GfSymbolPipe } from './symbol.pipe'; + +export { GfSymbolPipe }; diff --git a/apps/client/src/app/pipes/symbol/symbol.pipe.ts b/libs/common/src/lib/pipes/symbol.pipe.ts similarity index 100% rename from apps/client/src/app/pipes/symbol/symbol.pipe.ts rename to libs/common/src/lib/pipes/symbol.pipe.ts diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts index 903d9c96a..781e50c55 100644 --- a/libs/common/src/lib/types/index.ts +++ b/libs/common/src/lib/types/index.ts @@ -18,7 +18,6 @@ import type { Market } from './market.type'; import type { OrderWithAccount } from './order-with-account.type'; import type { RequestWithUser } from './request-with-user.type'; import type { SubscriptionOfferKey } from './subscription-offer-key.type'; -import type { SubscriptionType } from './subscription-type.type'; import type { UserWithSettings } from './user-with-settings.type'; import type { ViewMode } from './view-mode.type'; @@ -43,7 +42,6 @@ export type { OrderWithAccount, RequestWithUser, SubscriptionOfferKey, - SubscriptionType, UserWithSettings, ViewMode }; diff --git a/libs/common/src/lib/types/subscription-offer-key.type.ts b/libs/common/src/lib/types/subscription-offer-key.type.ts index f6d898a01..89322a400 100644 --- a/libs/common/src/lib/types/subscription-offer-key.type.ts +++ b/libs/common/src/lib/types/subscription-offer-key.type.ts @@ -2,4 +2,5 @@ export type SubscriptionOfferKey = | 'default' | 'renewal' | 'renewal-early-bird-2023' - | 'renewal-early-bird-2024'; + | 'renewal-early-bird-2024' + | 'renewal-early-bird-2025'; diff --git a/libs/common/src/lib/types/user-with-settings.type.ts b/libs/common/src/lib/types/user-with-settings.type.ts index 18fc90a5c..3c6adfec0 100644 --- a/libs/common/src/lib/types/user-with-settings.type.ts +++ b/libs/common/src/lib/types/user-with-settings.type.ts @@ -1,5 +1,5 @@ +import { SubscriptionType } from '@ghostfolio/common/enums'; import { SubscriptionOffer, UserSettings } from '@ghostfolio/common/interfaces'; -import { SubscriptionType } from '@ghostfolio/common/types'; import { Access, Account, Settings, User } from '@prisma/client'; diff --git a/apps/client/src/app/util/form.util.ts b/libs/common/src/lib/utils/form.util.ts similarity index 100% rename from apps/client/src/app/util/form.util.ts rename to libs/common/src/lib/utils/form.util.ts diff --git a/libs/common/src/lib/utils/index.ts b/libs/common/src/lib/utils/index.ts new file mode 100644 index 000000000..2bdd03fdc --- /dev/null +++ b/libs/common/src/lib/utils/index.ts @@ -0,0 +1,3 @@ +import { validateObjectForForm } from './form.util'; + +export { validateObjectForForm }; diff --git a/apps/api/src/validators/is-currency-code.ts b/libs/common/src/lib/validators/is-currency-code.ts similarity index 100% rename from apps/api/src/validators/is-currency-code.ts rename to libs/common/src/lib/validators/is-currency-code.ts diff --git a/libs/ui/src/lib/account-balances/account-balances.component.ts b/libs/ui/src/lib/account-balances/account-balances.component.ts index caeaebc64..5fe47347e 100644 --- a/libs/ui/src/lib/account-balances/account-balances.component.ts +++ b/libs/ui/src/lib/account-balances/account-balances.component.ts @@ -1,9 +1,10 @@ -import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/create-account-balance.dto'; -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; +/* eslint-disable @nx/enforce-module-boundaries */ import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; -import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; +import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { DATE_FORMAT, getLocale } from '@ghostfolio/common/helper'; import { AccountBalancesResponse } from '@ghostfolio/common/interfaces'; +import { validateObjectForForm } from '@ghostfolio/common/utils'; import { CUSTOM_ELEMENTS_SCHEMA, diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.html b/libs/ui/src/lib/accounts-table/accounts-table.component.html index b4b31bdee..f3e231fcd 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.html +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.html @@ -7,7 +7,7 @@ (click)="onTransferBalance()" > - Transfer Cash Balance... + Transfer Cash Balance...
    } @@ -291,7 +291,7 @@ - + +
    @@ -321,11 +327,7 @@
    - +
    diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.ts b/libs/ui/src/lib/accounts-table/accounts-table.component.ts index 607fa67dc..898231168 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.ts +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.ts @@ -1,5 +1,6 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; +/* eslint-disable @nx/enforce-module-boundaries */ import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale } from '@ghostfolio/common/helper'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -29,7 +30,8 @@ import { documentTextOutline, ellipsisHorizontal, eyeOffOutline, - trashOutline + trashOutline, + walletOutline } from 'ionicons/icons'; import { get } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -93,7 +95,8 @@ export class GfAccountsTableComponent implements OnChanges, OnDestroy { documentTextOutline, ellipsisHorizontal, eyeOffOutline, - trashOutline + trashOutline, + walletOutline }); } diff --git a/libs/ui/src/lib/activities-filter/activities-filter.component.ts b/libs/ui/src/lib/activities-filter/activities-filter.component.ts index cb659988a..34f883c67 100644 --- a/libs/ui/src/lib/activities-filter/activities-filter.component.ts +++ b/libs/ui/src/lib/activities-filter/activities-filter.component.ts @@ -1,5 +1,5 @@ -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { Filter, FilterGroup } from '@ghostfolio/common/interfaces'; +import { GfSymbolPipe } from '@ghostfolio/common/pipes'; import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { CommonModule } from '@angular/common'; 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 e62cfc3c5..58b6ea5e9 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -6,7 +6,7 @@ (click)="onImport()" > - Import Activities... + Import Activities... @if (hasPermissionToExportActivities) { @if (hasPermissionToExportActivities) { @@ -59,7 +59,9 @@
    - @if (!isUUID(element.SymbolProfile?.symbol)) { + @if ( + element.SymbolProfile?.dataSource !== 'MANUAL' && + !isUUID(element.SymbolProfile?.symbol) + ) {
    {{ element.SymbolProfile?.symbol | gfSymbol @@ -375,7 +380,9 @@ > - Import Activities... + Import Activities... } @@ -387,7 +394,9 @@ > - Import Dividends... + Import Dividends... } @@ -435,16 +444,24 @@ class="no-max-width" xPosition="before" > + @if (canClickActivity(element)) { + + } - - -
    + @if (!searchFormControl.value) { +
    + + Date Range + + @for ( + dateRangeOption of dateRangeOptions; + track dateRangeOption.value + ) { + {{ + dateRangeOption.label + }} + } + + +
    +
    + +
    + + +
    - } - +
    + }
    diff --git a/libs/ui/src/lib/assistant/interfaces/interfaces.ts b/libs/ui/src/lib/assistant/interfaces/interfaces.ts index 247641094..e018e0eb6 100644 --- a/libs/ui/src/lib/assistant/interfaces/interfaces.ts +++ b/libs/ui/src/lib/assistant/interfaces/interfaces.ts @@ -3,38 +3,38 @@ import { AccountWithValue, DateRange } from '@ghostfolio/common/types'; import { SearchMode } from '../enums/search-mode'; -export interface IAccountSearchResultItem +export interface AccountSearchResultItem extends Pick { mode: SearchMode.ACCOUNT; routerLink: string[]; } -export interface IAssetSearchResultItem extends AssetProfileIdentifier { +export interface AssetSearchResultItem extends AssetProfileIdentifier { assetSubClassString: string; currency: string; mode: SearchMode.ASSET_PROFILE | SearchMode.HOLDING; name: string; } -export interface IDateRangeOption { +export interface DateRangeOption { label: string; value: DateRange; } -export interface IQuickLinkSearchResultItem { +export interface QuickLinkSearchResultItem { mode: SearchMode.QUICK_LINK; name: string; routerLink: string[]; } -export type ISearchResultItem = - | IAccountSearchResultItem - | IAssetSearchResultItem - | IQuickLinkSearchResultItem; +export type SearchResultItem = + | AccountSearchResultItem + | AssetSearchResultItem + | QuickLinkSearchResultItem; -export interface ISearchResults { - accounts: ISearchResultItem[]; - assetProfiles: ISearchResultItem[]; - holdings: ISearchResultItem[]; - quickLinks: ISearchResultItem[]; +export interface SearchResults { + accounts: SearchResultItem[]; + assetProfiles: SearchResultItem[]; + holdings: SearchResultItem[]; + quickLinks: SearchResultItem[]; } 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 8f7d30847..59c1e6e17 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 @@ -1,11 +1,12 @@ -import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component'; -import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; +/* eslint-disable @nx/enforce-module-boundaries */ import { DataService } from '@ghostfolio/client/services/data.service'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { AdminMarketDataDetails, LineChartItem } from '@ghostfolio/common/interfaces'; +import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; +import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { CUSTOM_ELEMENTS_SCHEMA, diff --git a/libs/ui/src/lib/benchmark/benchmark.component.html b/libs/ui/src/lib/benchmark/benchmark.component.html index 2b75e5672..ab6db8887 100644 --- a/libs/ui/src/lib/benchmark/benchmark.component.html +++ b/libs/ui/src/lib/benchmark/benchmark.component.html @@ -8,8 +8,8 @@ [dataSource]="dataSource" > - - Name + + Name
    diff --git a/libs/ui/src/lib/benchmark/benchmark.component.ts b/libs/ui/src/lib/benchmark/benchmark.component.ts index 3af9bc674..5793300c1 100644 --- a/libs/ui/src/lib/benchmark/benchmark.component.ts +++ b/libs/ui/src/lib/benchmark/benchmark.component.ts @@ -1,5 +1,6 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; +/* eslint-disable @nx/enforce-module-boundaries */ import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; +import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, resolveMarketCondition } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, @@ -155,14 +156,17 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy { dataSource, symbol }: AssetProfileIdentifier) { - const dialogRef = this.dialog.open(GfBenchmarkDetailDialogComponent, { + const dialogRef = this.dialog.open< + GfBenchmarkDetailDialogComponent, + BenchmarkDetailDialogParams + >(GfBenchmarkDetailDialogComponent, { data: { dataSource, symbol, colorScheme: this.user?.settings?.colorScheme, deviceType: this.deviceType, locale: this.locale - } as BenchmarkDetailDialogParams, + }, height: this.deviceType === 'mobile' ? '98vh' : undefined, width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); diff --git a/apps/client/src/app/components/dialog-footer/dialog-footer.component.html b/libs/ui/src/lib/dialog-footer/dialog-footer.component.html similarity index 100% rename from apps/client/src/app/components/dialog-footer/dialog-footer.component.html rename to libs/ui/src/lib/dialog-footer/dialog-footer.component.html diff --git a/apps/client/src/app/components/dialog-footer/dialog-footer.component.scss b/libs/ui/src/lib/dialog-footer/dialog-footer.component.scss similarity index 100% rename from apps/client/src/app/components/dialog-footer/dialog-footer.component.scss rename to libs/ui/src/lib/dialog-footer/dialog-footer.component.scss diff --git a/apps/client/src/app/components/dialog-footer/dialog-footer.component.ts b/libs/ui/src/lib/dialog-footer/dialog-footer.component.ts similarity index 100% rename from apps/client/src/app/components/dialog-footer/dialog-footer.component.ts rename to libs/ui/src/lib/dialog-footer/dialog-footer.component.ts diff --git a/libs/ui/src/lib/dialog-footer/index.ts b/libs/ui/src/lib/dialog-footer/index.ts new file mode 100644 index 000000000..822be3e98 --- /dev/null +++ b/libs/ui/src/lib/dialog-footer/index.ts @@ -0,0 +1 @@ +export * from './dialog-footer.component'; diff --git a/apps/client/src/app/components/dialog-header/dialog-header.component.html b/libs/ui/src/lib/dialog-header/dialog-header.component.html similarity index 100% rename from apps/client/src/app/components/dialog-header/dialog-header.component.html rename to libs/ui/src/lib/dialog-header/dialog-header.component.html diff --git a/apps/client/src/app/components/dialog-header/dialog-header.component.scss b/libs/ui/src/lib/dialog-header/dialog-header.component.scss similarity index 100% rename from apps/client/src/app/components/dialog-header/dialog-header.component.scss rename to libs/ui/src/lib/dialog-header/dialog-header.component.scss diff --git a/apps/client/src/app/components/dialog-header/dialog-header.component.ts b/libs/ui/src/lib/dialog-header/dialog-header.component.ts similarity index 100% rename from apps/client/src/app/components/dialog-header/dialog-header.component.ts rename to libs/ui/src/lib/dialog-header/dialog-header.component.ts diff --git a/libs/ui/src/lib/dialog-header/index.ts b/libs/ui/src/lib/dialog-header/index.ts new file mode 100644 index 000000000..9beb9d4ac --- /dev/null +++ b/libs/ui/src/lib/dialog-header/index.ts @@ -0,0 +1 @@ +export * from './dialog-header.component'; 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 df7ca79fa..44276ec43 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -185,7 +185,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { 'principalInvestmentAmount' ).value, projectedTotalAmount: - Number(this.getProjectedTotalAmount().toFixed(0)) ?? 0, + Math.round(this.getProjectedTotalAmount()) || 0, retirementDate: this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE }, 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 d2d53f7ca..21202981d 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,3 +1,4 @@ +/* eslint-disable @nx/enforce-module-boundaries */ import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts index 7fbb1e621..f857e6e53 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts @@ -1,5 +1,6 @@ -import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto'; +/* eslint-disable @nx/enforce-module-boundaries */ import { DataService } from '@ghostfolio/client/services/data.service'; +import { UpdateMarketDataDto } from '@ghostfolio/common/dtos'; import { DATE_FORMAT, getDateFormatString, @@ -199,21 +200,21 @@ export class GfHistoricalMarketDataEditorComponent }) { const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice; - const dialogRef = this.dialog.open( + const dialogRef = this.dialog.open< GfHistoricalMarketDataEditorDialogComponent, - { - data: { - marketPrice, - currency: this.currency, - dataSource: this.dataSource, - dateString: `${yearMonth}-${day}`, - symbol: this.symbol, - user: this.user - } as HistoricalMarketDataEditorDialogParams, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - } - ); + HistoricalMarketDataEditorDialogParams + >(GfHistoricalMarketDataEditorDialogComponent, { + data: { + marketPrice, + currency: this.currency, + dataSource: this.dataSource, + dateString: `${yearMonth}-${day}`, + symbol: this.symbol, + user: this.user + }, + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); dialogRef .afterClosed() diff --git a/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss b/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss index 18c3a26cb..352f52ee8 100644 --- a/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss +++ b/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss @@ -113,6 +113,10 @@ mask-image: url('/assets/images/logo-openalternative.svg'); } + &.logo-oss-gallery { + mask-image: url('/assets/images/logo-oss-gallery.svg'); + } + &.logo-privacy-tools { mask-image: url('/assets/images/logo-privacy-tools.svg'); } @@ -139,6 +143,15 @@ max-height: 1.25rem; } + &.logo-selfhostedhub { + background-image: url('/assets/images/logo-selfhostedhub.svg'); + background-position: center; + background-repeat: no-repeat; + background-size: contain; + filter: grayscale(1); + opacity: 0.5; + } + &.logo-sourceforge { mask-image: url('/assets/images/logo-sourceforge.svg'); } diff --git a/libs/ui/src/lib/logo-carousel/logo-carousel.component.ts b/libs/ui/src/lib/logo-carousel/logo-carousel.component.ts index d7d3fa6af..9c1a90809 100644 --- a/libs/ui/src/lib/logo-carousel/logo-carousel.component.ts +++ b/libs/ui/src/lib/logo-carousel/logo-carousel.component.ts @@ -48,6 +48,13 @@ export class GfLogoCarouselComponent { title: 'OpenAlternative: Open Source Alternatives to Popular Software', url: 'https://openalternative.co' }, + { + className: 'logo-oss-gallery', + isMask: true, + name: 'OSS Gallery', + title: 'OSS Gallery: Discover the best open-source projects', + url: 'https://oss.gallery' + }, { className: 'logo-privacy-tools', isMask: true, @@ -82,6 +89,12 @@ export class GfLogoCarouselComponent { title: 'selfh.st — Self-hosted content and software', url: 'https://selfh.st' }, + { + className: 'logo-selfhostedhub', + name: 'SelfhostedHub', + title: 'SelfhostedHub — Discover best self-hosted software', + url: 'https://selfhostedhub.com' + }, { className: 'logo-sourceforge', isMask: true, diff --git a/libs/ui/src/lib/portfolio-filter-form/index.ts b/libs/ui/src/lib/portfolio-filter-form/index.ts new file mode 100644 index 000000000..51d22c034 --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/index.ts @@ -0,0 +1,2 @@ +export * from './interfaces'; +export * from './portfolio-filter-form.component'; diff --git a/libs/ui/src/lib/portfolio-filter-form/interfaces/index.ts b/libs/ui/src/lib/portfolio-filter-form/interfaces/index.ts new file mode 100644 index 000000000..62feaa56a --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/interfaces/index.ts @@ -0,0 +1 @@ +export * from './portfolio-filter-form-value.interface'; diff --git a/libs/ui/src/lib/portfolio-filter-form/interfaces/portfolio-filter-form-value.interface.ts b/libs/ui/src/lib/portfolio-filter-form/interfaces/portfolio-filter-form-value.interface.ts new file mode 100644 index 000000000..21ff0ae3b --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/interfaces/portfolio-filter-form-value.interface.ts @@ -0,0 +1,8 @@ +import { PortfolioPosition } from '@ghostfolio/common/interfaces'; + +export interface PortfolioFilterFormValue { + account: string; + assetClass: string; + holding: PortfolioPosition; + tag: string; +} diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html new file mode 100644 index 000000000..e017d33d6 --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html @@ -0,0 +1,75 @@ +
    +
    + + Account + + + @for (account of accounts; track account.id) { + +
    + @if (account.platform?.url) { + + } + {{ account.name }} +
    +
    + } +
    +
    +
    +
    + + Holding + + {{ + filterForm.get('holding')?.value?.name + }} + + @for (holding of holdings; track holding.name) { + +
    + {{ holding.name }} +
    + {{ holding.symbol | gfSymbol }} · {{ holding.currency }} +
    +
    + } +
    +
    +
    +
    + + Tag + + + @for (tag of tags; track tag.id) { + {{ tag.label }} + } + + +
    +
    + + Asset Class + + + @for (assetClass of assetClasses; track assetClass.id) { + {{ + assetClass.label + }} + } + + +
    +
    diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.scss b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.scss new file mode 100644 index 000000000..5d4e87f30 --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts new file mode 100644 index 000000000..710a4e9c5 --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts @@ -0,0 +1,79 @@ +import '@angular/localize/init'; +import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; + +import { GfPortfolioFilterFormComponent } from './portfolio-filter-form.component'; + +const meta: Meta = { + title: 'Portfolio Filter Form', + component: GfPortfolioFilterFormComponent, + decorators: [ + moduleMetadata({ + imports: [GfPortfolioFilterFormComponent] + }) + ] +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + accounts: [ + { + id: '733110b6-7c55-44eb-8cc5-c4c3e9d48a79', + name: 'Trading Account', + platform: { + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + { + id: '24ba27d6-e04b-4fb4-b856-b24c2ef0422a', + name: 'Investment Account', + platform: { + name: 'Fidelity', + url: 'https://fidelity.com' + } + } + ] as any, + assetClasses: [ + { id: 'COMMODITY', label: 'Commodity', type: 'ASSET_CLASS' }, + { id: 'EQUITY', label: 'Equity', type: 'ASSET_CLASS' }, + { id: 'FIXED_INCOME', label: 'Fixed Income', type: 'ASSET_CLASS' } + ] as any, + holdings: [ + { + currency: 'USD', + dataSource: 'YAHOO', + name: 'Apple Inc.', + symbol: 'AAPL' + }, + { + currency: 'USD', + dataSource: 'YAHOO', + name: 'Microsoft Corporation', + symbol: 'MSFT' + } + ] as any, + tags: [ + { + id: 'EMERGENCY_FUND', + label: 'Emergency Fund', + type: 'TAG' + }, + { + id: 'RETIREMENT_FUND', + label: 'Retirement Fund', + type: 'TAG' + } + ] as any, + disabled: false + } +}; + +export const Disabled: Story = { + args: { + ...Default.args, + disabled: true + } +}; diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts new file mode 100644 index 000000000..afbe5af4e --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts @@ -0,0 +1,177 @@ +import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; +import { Filter, PortfolioPosition } from '@ghostfolio/common/interfaces'; +import { GfSymbolPipe } from '@ghostfolio/common/pipes'; +import { AccountWithPlatform } from '@ghostfolio/common/types'; + +import { + CUSTOM_ELEMENTS_SCHEMA, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + Input, + OnChanges, + OnDestroy, + OnInit, + forwardRef +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + FormsModule, + NG_VALUE_ACCESSOR, + ReactiveFormsModule +} from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { Subject, takeUntil } from 'rxjs'; + +import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component'; +import { PortfolioFilterFormValue } from './interfaces'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + GfEntityLogoComponent, + GfSymbolPipe, + MatFormFieldModule, + MatSelectModule, + ReactiveFormsModule + ], + providers: [ + { + multi: true, + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GfPortfolioFilterFormComponent) + } + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + selector: 'gf-portfolio-filter-form', + styleUrls: ['./portfolio-filter-form.component.scss'], + templateUrl: './portfolio-filter-form.component.html' +}) +export class GfPortfolioFilterFormComponent + implements ControlValueAccessor, OnInit, OnChanges, OnDestroy +{ + @Input() accounts: AccountWithPlatform[] = []; + @Input() assetClasses: Filter[] = []; + @Input() holdings: PortfolioPosition[] = []; + @Input() tags: Filter[] = []; + @Input() disabled = false; + + public filterForm: FormGroup; + + private unsubscribeSubject = new Subject(); + + public constructor( + private changeDetectorRef: ChangeDetectorRef, + private formBuilder: FormBuilder + ) { + this.filterForm = this.formBuilder.group({ + account: new FormControl(null), + assetClass: new FormControl(null), + holding: new FormControl(null), + tag: new FormControl(null) + }); + } + + public ngOnInit() { + this.filterForm.valueChanges + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((value) => { + this.onChange(value as PortfolioFilterFormValue); + this.onTouched(); + }); + } + + public hasFilters() { + const formValue = this.filterForm.value; + + return Object.values(formValue).some((value) => { + return !!value; + }); + } + + public holdingComparisonFunction( + option: PortfolioPosition, + value: PortfolioPosition + ) { + if (value === null) { + return false; + } + + return ( + getAssetProfileIdentifier(option) === getAssetProfileIdentifier(value) + ); + } + + public ngOnChanges() { + if (this.disabled) { + this.filterForm.disable({ emitEvent: false }); + } else { + this.filterForm.enable({ emitEvent: false }); + } + + const tagControl = this.filterForm.get('tag'); + + if (this.tags.length === 0) { + tagControl?.disable({ emitEvent: false }); + } else if (!this.disabled) { + tagControl?.enable({ emitEvent: false }); + } + + this.changeDetectorRef.markForCheck(); + } + + public registerOnChange(fn: (value: PortfolioFilterFormValue) => void) { + this.onChange = fn; + } + + public registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + public setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + + if (this.disabled) { + this.filterForm.disable({ emitEvent: false }); + } else { + this.filterForm.enable({ emitEvent: false }); + } + + this.changeDetectorRef.markForCheck(); + } + + public writeValue(value: PortfolioFilterFormValue | null) { + if (value) { + this.filterForm.setValue( + { + account: value.account ?? null, + assetClass: value.assetClass ?? null, + holding: value.holding ?? null, + tag: value.tag ?? null + }, + { emitEvent: false } + ); + } else { + this.filterForm.reset({}, { emitEvent: false }); + } + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private onChange = (_value: PortfolioFilterFormValue): void => { + // ControlValueAccessor onChange callback + }; + + private onTouched = (): void => { + // ControlValueAccessor onTouched callback + }; +} diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts index 37010954a..b5a3d6819 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts @@ -22,7 +22,7 @@ export default { type Story = StoryObj; -export const Simple: Story = { +export const Default: Story = { args: { baseCurrency: 'USD', data: { @@ -37,3 +37,47 @@ export const Simple: Story = { locale: 'en-US' } }; + +export const InPercentage: Story = { + args: { + data: { + US: { name: 'United States', value: 0.6515000000000001 }, + NL: { name: 'Netherlands', value: 0.006 }, + DE: { name: 'Germany', value: 0.0031 }, + GB: { name: 'United Kingdom', value: 0.0124 }, + CA: { name: 'Canada', value: 0.0247 }, + IE: { name: 'Ireland', value: 0.0112 }, + SE: { name: 'Sweden', value: 0.0016 }, + ES: { name: 'Spain', value: 0.0042 }, + AU: { name: 'Australia', value: 0.0022 }, + FR: { name: 'France', value: 0.0012 }, + UY: { name: 'Uruguay', value: 0.0012 }, + CH: { name: 'Switzerland', value: 0.004099999999999999 }, + LU: { name: 'Luxembourg', value: 0.0012 }, + BR: { name: 'Brazil', value: 0.0006 }, + HK: { name: 'Hong Kong', value: 0.0006 }, + IT: { name: 'Italy', value: 0.0005 }, + CN: { name: 'China', value: 0.002 }, + KR: { name: 'South Korea', value: 0.0006 }, + BM: { name: 'Bermuda', value: 0.0011 }, + ZA: { name: 'South Africa', value: 0.0004 }, + SG: { name: 'Singapore', value: 0.0003 }, + IL: { name: 'Israel', value: 0.001 }, + DK: { name: 'Denmark', value: 0.0002 }, + PE: { name: 'Peru', value: 0.0002 }, + NO: { name: 'Norway', value: 0.0002 }, + KY: { name: 'Cayman Islands', value: 0.0001 }, + IN: { name: 'India', value: 0.0001 }, + TW: { name: 'Taiwan', value: 0.0002 }, + GR: { name: 'Greece', value: 0.0001 }, + CL: { name: 'Chile', value: 0.0001 }, + MX: { name: 'Mexico', value: 0 }, + RU: { name: 'Russia', value: 0 }, + IS: { name: 'Iceland', value: 0 }, + JP: { name: 'Japan', value: 0 }, + BE: { name: 'Belgium', value: 0 } + }, + isInPercent: true, + keys: ['name'] + } +}; 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 3f062a374..2d8a03ac0 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 @@ -31,6 +31,7 @@ import ChartDataLabels from 'chartjs-plugin-datalabels'; import { isUUID } from 'class-validator'; import Color from 'color'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import OpenColor from 'open-color'; import { translate } from '../i18n'; @@ -47,7 +48,7 @@ const { teal, violet, yellow -} = require('open-color'); +} = OpenColor; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index 80315fc06..05a2c06c3 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -1,6 +1,7 @@ -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; +/* eslint-disable @nx/enforce-module-boundaries */ import { DataService } from '@ghostfolio/client/services/data.service'; import { LookupItem } from '@ghostfolio/common/interfaces'; +import { GfSymbolPipe } from '@ghostfolio/common/pipes'; import { FocusMonitor } from '@angular/cdk/a11y'; import { 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 05a4b3e7a..7f1a8805e 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts @@ -5,12 +5,10 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, - EventEmitter, Input, OnChanges, OnDestroy, OnInit, - Output, signal, ViewChild } from '@angular/core'; @@ -66,8 +64,6 @@ export class GfTagsSelectorComponent @Input() tags: Tag[]; @Input() tagsAvailable: Tag[]; - @Output() tagsChanged = new EventEmitter(); - @ViewChild('tagInput') tagInput: ElementRef; public filteredOptions: Subject = new BehaviorSubject([]); @@ -115,7 +111,6 @@ export class GfTagsSelectorComponent }); const newTags = this.tagsSelected(); - this.tagsChanged.emit(newTags); this.onChange(newTags); this.onTouched(); this.tagInput.nativeElement.value = ''; @@ -130,7 +125,6 @@ export class GfTagsSelectorComponent }); const newTags = this.tagsSelected(); - this.tagsChanged.emit(newTags); this.onChange(newTags); this.onTouched(); this.updateFilters(); diff --git a/libs/ui/src/lib/top-holdings/top-holdings.component.ts b/libs/ui/src/lib/top-holdings/top-holdings.component.ts index c9f7e0372..75a96fc5c 100644 --- a/libs/ui/src/lib/top-holdings/top-holdings.component.ts +++ b/libs/ui/src/lib/top-holdings/top-holdings.component.ts @@ -1,9 +1,9 @@ -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { getLocale } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, HoldingWithParents } from '@ghostfolio/common/interfaces'; +import { GfSymbolPipe } from '@ghostfolio/common/pipes'; import { animate, 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 4e06d49cc..6ae958b83 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -33,10 +33,11 @@ import { isUUID } from 'class-validator'; import { differenceInDays, max } from 'date-fns'; import { orderBy } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import OpenColor from 'open-color'; import { GetColorParams } from './interfaces/interfaces'; -const { gray, green, red } = require('open-color'); +const { gray, green, red } = OpenColor; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/package-lock.json b/package-lock.json index 51b33d298..986e45e36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.209.0", + "version": "2.221.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.209.0", + "version": "2.221.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -23,27 +23,22 @@ "@angular/service-worker": "20.2.4", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.0", - "@dfinity/agent": "0.15.7", - "@dfinity/auth-client": "0.15.7", - "@dfinity/candid": "0.15.7", - "@dfinity/identity": "0.15.7", - "@dfinity/principal": "0.15.7", - "@internationalized/number": "3.6.3", - "@ionic/angular": "8.7.3", + "@internationalized/number": "3.6.5", + "@ionic/angular": "8.7.8", "@keyv/redis": "4.4.0", - "@nestjs/bull": "11.0.2", + "@nestjs/bull": "11.0.4", "@nestjs/cache-manager": "3.0.1", - "@nestjs/common": "11.1.3", + "@nestjs/common": "11.1.8", "@nestjs/config": "4.0.2", - "@nestjs/core": "11.1.3", + "@nestjs/core": "11.1.8", "@nestjs/event-emitter": "3.0.1", - "@nestjs/jwt": "11.0.0", + "@nestjs/jwt": "11.0.1", "@nestjs/passport": "11.0.5", - "@nestjs/platform-express": "11.1.3", - "@nestjs/schedule": "6.0.0", - "@nestjs/serve-static": "5.0.3", + "@nestjs/platform-express": "11.1.8", + "@nestjs/schedule": "6.0.1", + "@nestjs/serve-static": "5.0.4", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.17.1", + "@prisma/client": "6.19.0", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -52,7 +47,7 @@ "big.js": "7.0.1", "bootstrap": "4.6.2", "bull": "4.16.5", - "chart.js": "4.5.0", + "chart.js": "4.5.1", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-treemap": "3.1.0", "chartjs-plugin-annotation": "3.1.0", @@ -60,12 +55,14 @@ "cheerio": "1.0.0", "class-transformer": "0.5.1", "class-validator": "0.14.2", - "color": "5.0.0", + "color": "5.0.3", "countries-and-timezones": "3.8.0", - "countries-list": "3.1.1", + "countries-list": "3.2.0", "countup.js": "2.9.0", "date-fns": "4.1.0", - "envalid": "8.1.0", + "dotenv": "17.2.3", + "dotenv-expand": "12.0.3", + "envalid": "8.1.1", "fuse.js": "7.1.0", "google-spreadsheet": "3.2.0", "helmet": "7.0.0", @@ -75,7 +72,7 @@ "lodash": "4.17.21", "marked": "15.0.4", "ms": "3.0.0-canary.1", - "ng-extract-i18n-merge": "3.0.0", + "ng-extract-i18n-merge": "3.1.0", "ngx-device-detector": "10.1.0", "ngx-markdown": "20.0.0", "ngx-skeleton-loader": "11.3.0", @@ -89,11 +86,10 @@ "reflect-metadata": "0.2.2", "rxjs": "7.8.1", "stripe": "18.5.0", - "svgmap": "2.12.2", - "tablemark": "3.1.0", - "twitter-api-v2": "1.23.0", - "uuid": "11.1.0", - "yahoo-finance2": "3.10.0", + "svgmap": "2.14.0", + "tablemark": "4.1.0", + "twitter-api-v2": "1.27.0", + "yahoo-finance2": "3.10.2", "zone.js": "0.15.1" }, "devDependencies": { @@ -110,10 +106,9 @@ "@angular/pwa": "20.2.2", "@eslint/eslintrc": "3.3.1", "@eslint/js": "9.35.0", - "@nestjs/schematics": "11.0.5", - "@nestjs/testing": "11.1.3", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.8", "@nx/angular": "21.5.1", - "@nx/cypress": "21.5.1", "@nx/eslint-plugin": "21.5.1", "@nx/jest": "21.5.1", "@nx/js": "21.5.1", @@ -136,10 +131,8 @@ "@types/passport-google-oauth20": "2.0.16", "@typescript-eslint/eslint-plugin": "8.43.0", "@typescript-eslint/parser": "8.43.0", - "cypress": "6.2.1", "eslint": "9.35.0", "eslint-config-prettier": "10.1.8", - "eslint-plugin-cypress": "4.2.0", "eslint-plugin-import": "2.32.0", "eslint-plugin-storybook": "9.1.5", "husky": "9.1.7", @@ -147,9 +140,9 @@ "jest-environment-jsdom": "29.7.0", "jest-preset-angular": "14.6.0", "nx": "21.5.1", - "prettier": "3.6.2", + "prettier": "3.7.3", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.17.1", + "prisma": "6.19.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", @@ -4688,6 +4681,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=0.1.90" } @@ -4696,6 +4690,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -4708,6 +4703,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -4720,6 +4716,8 @@ "integrity": "sha512-EDiBsVPWC27DDLEJCo+dpl9ODHhdrwU57ccr9tspwCdG2ni0QVkf6LF0FGbhfujcjPxnXLIwsaks4sOrwrA4Qw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^1.1.3", "cli-cursor": "^1.0.2", @@ -4736,6 +4734,8 @@ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4746,6 +4746,8 @@ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4756,6 +4758,8 @@ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -4772,7 +4776,9 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@cypress/listr-verbose-renderer/node_modules/escape-string-regexp": { "version": "1.0.5", @@ -4780,6 +4786,8 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -4790,6 +4798,8 @@ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -4803,6 +4813,8 @@ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -4813,6 +4825,8 @@ "integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -4843,6 +4857,8 @@ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -4858,6 +4874,8 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -4868,6 +4886,8 @@ "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" @@ -4879,6 +4899,8 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -4888,7 +4910,9 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@date-fns/utc": { "version": "2.1.0", @@ -4936,77 +4960,6 @@ "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@dfinity/agent": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/@dfinity/agent/-/agent-0.15.7.tgz", - "integrity": "sha512-w34yvlUTpPBG8nLOD0t/ao3k2xonOFq4QGvfJ1HiS/nIggdza/3xC3nLBszGrjVYWj1jqu8BLFvQXCAeWin75A==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "base64-arraybuffer": "^0.2.0", - "bignumber.js": "^9.0.0", - "borc": "^2.1.1", - "js-sha256": "0.9.0", - "simple-cbor": "^0.4.1", - "ts-node": "^10.8.2" - }, - "peerDependencies": { - "@dfinity/candid": "^0.15.7", - "@dfinity/principal": "^0.15.7" - } - }, - "node_modules/@dfinity/auth-client": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/@dfinity/auth-client/-/auth-client-0.15.7.tgz", - "integrity": "sha512-f6cRqXayCf+7+9gNcDnAZZwJrgBYKIzfxjxeRLlpsueQeo+E/BX2yVSANxzTkCNc4U3p+ttHI1RNtasLunYTcA==", - "license": "Apache-2.0", - "dependencies": { - "idb": "^7.0.2" - }, - "peerDependencies": { - "@dfinity/agent": "^0.15.7", - "@dfinity/identity": "^0.15.7", - "@dfinity/principal": "^0.15.7" - } - }, - "node_modules/@dfinity/candid": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/@dfinity/candid/-/candid-0.15.7.tgz", - "integrity": "sha512-lTcjK/xrSyT7wvUQ2pApG+yklQAwxaofQ04D1IWv0/8gKbY0eUbh8G2w6+CypJ15Hb1CH24ijUj8nWdeX/z3jg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "ts-node": "^10.8.2" - } - }, - "node_modules/@dfinity/identity": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/@dfinity/identity/-/identity-0.15.7.tgz", - "integrity": "sha512-kBAkx9wq78jSQf6T5aayLyWm8YgtOZw8bW6+OuzX6tR3hkAEa85A9TcKA7BjkmMWSIskjEDVQub4fFfKWS2vOQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "borc": "^2.1.1", - "js-sha256": "^0.9.0", - "tweetnacl": "^1.0.1" - }, - "peerDependencies": { - "@dfinity/agent": "^0.15.7", - "@dfinity/principal": "^0.15.7", - "@peculiar/webcrypto": "^1.4.0" - } - }, - "node_modules/@dfinity/principal": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/@dfinity/principal/-/principal-0.15.7.tgz", - "integrity": "sha512-6/AkYzpGEH6Jw/0+B/EeeQn+5u2GDDvRLt1kQPhIG4txQYFnOy04H3VvyrymmfAj6/CXUgrOrux6OxgYSLYVJg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "js-sha256": "^0.9.0", - "ts-node": "^10.8.2" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", @@ -6064,21 +6017,21 @@ } }, "node_modules/@internationalized/number": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.3.tgz", - "integrity": "sha512-p+Zh1sb6EfrfVaS86jlHGQ9HA66fJhV9x5LiE5vCbZtXEHAuhcmUZUdZ4WrFpUBfNalr2OkAJI5AcKEQF+Lebw==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz", + "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==", "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" } }, "node_modules/@ionic/angular": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-8.7.3.tgz", - "integrity": "sha512-Fd2bsluwsi88d8AEvSVANn3a7xZ7NEmlvgVTLnuF9VTI0TgdkLQptgEolty00axnQdjCaxSXxgFJd/m0gVpKIg==", + "version": "8.7.8", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-8.7.8.tgz", + "integrity": "sha512-IBN5h3nIOwbuglLit48S7wNeg7NHtl/vaKAHDggICyzI92cSg5yYL07Fz59pszhkBlZQUB5SQnml990Zj2bZUg==", "license": "MIT", "dependencies": { - "@ionic/core": "8.7.3", + "@ionic/core": "8.7.8", "ionicons": "^8.0.13", "jsonc-parser": "^3.0.0", "tslib": "^2.3.0" @@ -6092,20 +6045,20 @@ } }, "node_modules/@ionic/core": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.7.3.tgz", - "integrity": "sha512-KdyMxpMDQj+uqpztpK6yvN/T96hqcDiGXQ4T+aAZ+LW3wV3+0it6/rbh9C1B/wCl4Isnm4IRltPabgEfNJ50nw==", + "version": "8.7.8", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.7.8.tgz", + "integrity": "sha512-GLWb/lz3kocpzTZTeQQ5xxoWz4CKHD6zpnbwJknTKsncebohAaw2KTe7uOw5toKQEDdohTseFuSGoDDBoRQ1Ug==", "license": "MIT", "dependencies": { - "@stencil/core": "4.36.2", + "@stencil/core": "4.38.0", "ionicons": "^8.0.13", "tslib": "^2.1.0" } }, "node_modules/@ioredis/commands": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.3.0.tgz", - "integrity": "sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", "license": "MIT" }, "node_modules/@isaacs/balanced-match": { @@ -8987,12 +8940,12 @@ } }, "node_modules/@nestjs/bull": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/bull/-/bull-11.0.2.tgz", - "integrity": "sha512-RjyP9JZUuLmMhmq1TMNIZqolkAd14az1jyXMMVki+C9dYvaMjWzBSwcZAtKs9Pk15Rm7qN1xn3R11aMV2Xv4gg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/bull/-/bull-11.0.4.tgz", + "integrity": "sha512-QVz2PR/rJF/isy7otVnMTSqLf/O71p9Ka7lBZt9Gm+NQFv8fcH2L11GL7TA0whyCcw/kAX5iRepUXz/wed4JoA==", "license": "MIT", "dependencies": { - "@nestjs/bull-shared": "^11.0.2", + "@nestjs/bull-shared": "^11.0.4", "tslib": "2.8.1" }, "peerDependencies": { @@ -9002,9 +8955,9 @@ } }, "node_modules/@nestjs/bull-shared": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.3.tgz", - "integrity": "sha512-CaHniPkLAxis6fAB1DB8WZELQv8VPCLedbj7iP0VQ1pz74i6NSzG9mBg6tOomXq/WW4la4P4OMGEQ48UAJh20A==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.4.tgz", + "integrity": "sha512-VBJcDHSAzxQnpcDfA0kt9MTGUD1XZzfByV70su0W0eDCQ9aqIEBlzWRW21tv9FG9dIut22ysgDidshdjlnczLw==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -9028,15 +8981,15 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.8.tgz", + "integrity": "sha512-bbsOqwld/GdBfiRNc4nnjyWWENDEicq4SH+R5AuYatvf++vf1x5JIsHB1i1KtfZMD3eRte0D4K9WXuAYil6XAg==", "license": "MIT", "peer": true, "dependencies": { "file-type": "21.0.0", "iterare": "1.2.1", - "load-esm": "1.0.2", + "load-esm": "1.0.3", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -9074,10 +9027,37 @@ "rxjs": "^7.1.0" } }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv-expand": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", + "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.8.tgz", + "integrity": "sha512-7riWfmTmMhCJHZ5ZiaG+crj4t85IPCq/wLRuOUSigBYyFT2JZj0lVHtAdf4Davp9ouNI8GINBDt9h9b5Gz9nTw==", "hasInstallScript": true, "license": "MIT", "peer": true, @@ -9085,7 +9065,7 @@ "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "8.2.0", + "path-to-regexp": "8.3.0", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -9130,12 +9110,12 @@ } }, "node_modules/@nestjs/jwt": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.0.tgz", - "integrity": "sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.1.tgz", + "integrity": "sha512-HXSsc7SAnCnjA98TsZqrE7trGtHDnYXWp4Ffy6LwSmck1QvbGYdMzBquXofX5l6tIRpeY4Qidl2Ti2CVG77Pdw==", "license": "MIT", "dependencies": { - "@types/jsonwebtoken": "9.0.7", + "@types/jsonwebtoken": "9.0.10", "jsonwebtoken": "9.0.2" }, "peerDependencies": { @@ -9153,16 +9133,16 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.8.tgz", + "integrity": "sha512-rL6pZH9BW7BnL5X2eWbJMtt86uloAKjFgyY5+L2UkizgfEp7rgAs0+Z1z0BcW2Pgu5+q8O7RKPNyHJ/9ZNz/ZQ==", "license": "MIT", "peer": true, "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "2.0.1", - "path-to-regexp": "8.2.0", + "multer": "2.0.2", + "path-to-regexp": "8.3.0", "tslib": "2.8.1" }, "funding": { @@ -9175,12 +9155,12 @@ } }, "node_modules/@nestjs/schedule": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.0.tgz", - "integrity": "sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.1.tgz", + "integrity": "sha512-v3yO6cSPAoBSSyH67HWnXHzuhPhSNZhRmLY38JvCt2sqY8sPMOODpcU1D79iUMFf7k16DaMEbL4Mgx61ZhiC8Q==", "license": "MIT", "dependencies": { - "cron": "4.3.0" + "cron": "4.3.3" }, "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", @@ -9188,15 +9168,15 @@ } }, "node_modules/@nestjs/schematics": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.5.tgz", - "integrity": "sha512-T50SCNyqCZ/fDssaOD7meBKLZ87ebRLaJqZTJPvJKjlib1VYhMOCwXYsr7bjMPmuPgiQHOwvppz77xN/m6GM7A==", + "version": "11.0.9", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", + "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", - "comment-json": "4.2.5", + "@angular-devkit/core": "19.2.17", + "@angular-devkit/schematics": "19.2.17", + "comment-json": "4.4.1", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, @@ -9205,9 +9185,9 @@ } }, "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", + "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9233,13 +9213,13 @@ } }, "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.6.tgz", - "integrity": "sha512-YTAxNnT++5eflx19OUHmOWu597/TbTel+QARiZCv1xQw99+X8DCKKOUXtqBRd53CAHlREDI33Rn/JLY3NYgMLQ==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", + "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", + "@angular-devkit/core": "19.2.17", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -9337,12 +9317,12 @@ "license": "ISC" }, "node_modules/@nestjs/serve-static": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-5.0.3.tgz", - "integrity": "sha512-0jFjTlSVSLrI+mot8lfm+h2laXtKzCvgsVStv9T1ZBZTDwS26gM5czIhIESmWAod0PfrbCDFiu9C1MglObL8VA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-5.0.4.tgz", + "integrity": "sha512-3kO1M9D3vsPyWPFardxIjUYeuolS58PnhCoBTkS7t3BrdZFZCKHnBZ15js+UOzOR2Q6HmD7ssGjLd0DVYVdvOw==", "license": "MIT", "dependencies": { - "path-to-regexp": "8.2.0" + "path-to-regexp": "8.3.0" }, "peerDependencies": { "@fastify/static": "^8.0.4", @@ -9364,9 +9344,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.8.tgz", + "integrity": "sha512-E6K+0UTKztcPxJzLnQa7S34lFjZbrj3Z1r7c5y5WDrL1m5HD1H4AeyBhicHgdaFmxjLAva2bq0sYKy/S7cdeYA==", "dev": true, "license": "MIT", "dependencies": { @@ -11874,35 +11854,6 @@ "tslib": "^2.8.1" } }, - "node_modules/@peculiar/json-schema": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", - "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@peculiar/webcrypto": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz", - "integrity": "sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2", - "webcrypto-core": "^1.8.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/@phenomnomnominal/tsquery": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", @@ -11948,9 +11899,9 @@ "license": "MIT" }, "node_modules/@prisma/client": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.17.1.tgz", - "integrity": "sha512-zL58jbLzYamjnNnmNA51IOZdbk5ci03KviXCuB0Tydc9btH2kDWsi1pQm2VecviRTM7jGia0OPPkgpGnT3nKvw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.0.tgz", + "integrity": "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -11970,66 +11921,66 @@ } }, "node_modules/@prisma/config": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.17.1.tgz", - "integrity": "sha512-fs8wY6DsvOCzuiyWVckrVs1LOcbY4LZNz8ki4uUIQ28jCCzojTGqdLhN2Jl5lDnC1yI8/gNIKpsWDM8pLhOdwA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", + "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", - "effect": "3.16.12", + "effect": "3.18.4", "empathic": "2.0.0" } }, "node_modules/@prisma/debug": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.17.1.tgz", - "integrity": "sha512-Vf7Tt5Wh9XcndpbmeotuqOMLWPTjEKCsgojxXP2oxE1/xYe7PtnP76hsouG9vis6fctX+TxgmwxTuYi/+xc7dQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", + "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.17.1.tgz", - "integrity": "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", + "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.17.1", - "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "@prisma/fetch-engine": "6.17.1", - "@prisma/get-platform": "6.17.1" + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/fetch-engine": "6.19.0", + "@prisma/get-platform": "6.19.0" } }, "node_modules/@prisma/engines-version": { - "version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac.tgz", - "integrity": "sha512-17140E3huOuD9lMdJ9+SF/juOf3WR3sTJMVyyenzqUPbuH+89nPhSWcrY+Mf7tmSs6HvaO+7S+HkELinn6bhdg==", + "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", + "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.17.1.tgz", - "integrity": "sha512-AYZiHOs184qkDMiTeshyJCtyL4yERkjfTkJiSJdYuSfc24m94lTNL5+GFinZ6vVz+ktX4NJzHKn1zIFzGTWrWg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", + "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.17.1", - "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "@prisma/get-platform": "6.17.1" + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/get-platform": "6.19.0" } }, "node_modules/@prisma/get-platform": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.17.1.tgz", - "integrity": "sha512-AKEn6fsfz0r482S5KRDFlIGEaq9wLNcgalD1adL+fPcFFblIKs1sD81kY/utrHdqKuVC6E1XSRpegDK3ZLL4Qg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", + "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.17.1" + "@prisma/debug": "6.19.0" } }, "node_modules/@redis/client": { @@ -12891,6 +12842,8 @@ "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "any-observable": "^0.3.0" }, @@ -13062,9 +13015,9 @@ "license": "MIT" }, "node_modules/@stencil/core": { - "version": "4.36.2", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.2.tgz", - "integrity": "sha512-PRFSpxNzX9Oi0Wfh02asztN9Sgev/MacfZwmd+VVyE6ZxW+a/kEpAYZhzGAmE+/aKVOGYuug7R9SulanYGxiDQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.38.0.tgz", + "integrity": "sha512-oC3QFKO0X1yXVvETgc8OLY525MNKhn9vISBrbtKnGoPlokJ6rI8Vk1RK22TevnNrHLI4SExNLbcDnqilKR35JQ==", "license": "MIT", "bin": { "stencil": "bin/stencil" @@ -13718,24 +13671,28 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, "license": "MIT" }, "node_modules/@tufjs/canonical-json": { @@ -14413,11 +14370,12 @@ "license": "MIT" }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", - "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { + "@types/ms": "*", "@types/node": "*" } }, @@ -14429,9 +14387,9 @@ "license": "MIT" }, "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", "license": "MIT" }, "node_modules/@types/mdx": { @@ -14448,6 +14406,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.15.17", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", @@ -14605,14 +14569,18 @@ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/sizzle": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/sockjs": { "version": "0.3.36", @@ -15647,6 +15615,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "devOptional": true, "license": "MIT", "peer": true, "bin": { @@ -15694,6 +15663,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -15959,6 +15929,8 @@ "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -16015,12 +15987,15 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -16204,6 +16179,8 @@ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safer-buffer": "~2.1.0" } @@ -16228,6 +16205,8 @@ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8" } @@ -16349,6 +16328,8 @@ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -16358,7 +16339,9 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/axios": { "version": "1.11.0", @@ -16673,14 +16656,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-arraybuffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", - "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -16743,6 +16718,8 @@ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "tweetnacl": "^0.14.3" } @@ -16752,7 +16729,9 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true, - "license": "Unlicense" + "license": "Unlicense", + "optional": true, + "peer": true }, "node_modules/beasties": { "version": "0.3.5", @@ -16923,14 +16902,18 @@ "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/body-parser": { "version": "2.2.0", @@ -17004,30 +16987,6 @@ "popper.js": "^1.16.1" } }, - "node_modules/borc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/borc/-/borc-2.1.2.tgz", - "integrity": "sha512-Sy9eoUi4OiKzq7VovMn246iTo17kzuyHJKomCfpWMlI6RpfN1gk95w7d7gH264nApVLg0HZfcpz62/g4VH1Y4w==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0", - "buffer": "^5.5.0", - "commander": "^2.15.0", - "ieee754": "^1.1.13", - "iso-url": "~0.4.7", - "json-text-sequence": "~0.1.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/borc/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -17125,6 +17084,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -17158,6 +17118,8 @@ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -17404,6 +17366,8 @@ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -17535,7 +17499,9 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/chai": { "version": "5.2.1", @@ -17568,6 +17534,12 @@ "node": ">=8" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "license": "MIT" + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -17586,9 +17558,9 @@ "license": "MIT" }, "node_modules/chart.js": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", - "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", "peer": true, "dependencies": { @@ -17651,6 +17623,8 @@ "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -17794,7 +17768,9 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/citty": { "version": "0.1.6", @@ -17861,6 +17837,8 @@ "integrity": "sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "restore-cursor": "^1.0.1" }, @@ -17887,6 +17865,8 @@ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "string-width": "^4.2.0" }, @@ -17903,6 +17883,8 @@ "integrity": "sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "slice-ansi": "0.0.4", "string-width": "^1.0.1" @@ -17917,6 +17899,8 @@ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -17927,6 +17911,8 @@ "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "number-is-nan": "^1.0.0" }, @@ -17940,6 +17926,8 @@ "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -17955,6 +17943,8 @@ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -18080,6 +18070,8 @@ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -18092,13 +18084,13 @@ "license": "MIT" }, "node_modules/color": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-5.0.0.tgz", - "integrity": "sha512-16BlyiuyLq3MLxpRWyOTiWsO3ii/eLQLJUQXBSNcxMBBSnyt1ee9YUdaozQp03ifwm5woztEZGDbk9RGVuCsdw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", "dependencies": { - "color-convert": "^3.0.1", - "color-string": "^2.0.0" + "color-convert": "^3.1.3", + "color-string": "^2.1.3" }, "engines": { "node": ">=18" @@ -18123,9 +18115,9 @@ "license": "MIT" }, "node_modules/color-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.0.1.tgz", - "integrity": "sha512-5z9FbYTZPAo8iKsNEqRNv+OlpBbDcoE+SY9GjLfDUHEfcNNV7tS9eSAlFHEaub/r5tBL9LtskAeq1l9SaoZ5tQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", "dependencies": { "color-name": "^2.0.0" @@ -18135,18 +18127,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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", "engines": { "node": ">=12.20" } }, "node_modules/color/node_modules/color-convert": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.0.tgz", - "integrity": "sha512-TVoqAq8ZDIpK5lsQY874DDnu65CSsc9vzq0wLpNQ6UMBq81GSZocVazPiBbYGzngzBOIRahpkTzCLVe2at4MfA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", "dependencies": { "color-name": "^2.0.0" @@ -18156,9 +18148,9 @@ } }, "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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", "engines": { "node": ">=12.20" @@ -18222,17 +18214,15 @@ } }, "node_modules/comment-json": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", - "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", "dev": true, "license": "MIT", "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" + "esprima": "^4.0.1" }, "engines": { "node": ">= 6" @@ -18251,6 +18241,8 @@ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4.0.0" } @@ -18330,6 +18322,8 @@ "node >= 0.8" ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -18342,7 +18336,9 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/concat-stream/node_modules/readable-stream": { "version": "2.3.8", @@ -18350,6 +18346,8 @@ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -18365,7 +18363,9 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/concat-stream/node_modules/string_decoder": { "version": "1.1.1", @@ -18373,6 +18373,8 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -18598,9 +18600,9 @@ } }, "node_modules/countries-list": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.1.1.tgz", - "integrity": "sha512-nPklKJ5qtmY5MdBKw1NiBAoyx5Sa7p2yPpljZyQ7gyCN1m+eMFs9I6CT37Mxt8zvR5L3VzD3DJBE4WQzX3WF4A==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.2.0.tgz", + "integrity": "sha512-HYHAo2fwEsG3TmbsNdVmIQPHizRlqeYMTtLEAl0IANG/3jRYX7p3NR6VapDqKP0n60TmsRy1dyRjVN5JbywDbA==", "license": "MIT" }, "node_modules/countup.js": { @@ -19276,16 +19278,17 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, "license": "MIT" }, "node_modules/cron": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.0.tgz", - "integrity": "sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.3.tgz", + "integrity": "sha512-B/CJj5yL3sjtlun6RtYHvoSB26EmQ2NUmhq9ZiJSyKIM4K/fqfh9aelDFlIayD2YMeFZqWLi9hHV+c+pq2Djkw==", "license": "MIT", "dependencies": { - "@types/luxon": "~3.6.0", - "luxon": "~3.6.0" + "@types/luxon": "~3.7.0", + "luxon": "~3.7.0" }, "engines": { "node": ">=18.x" @@ -19786,6 +19789,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@cypress/listr-verbose-renderer": "^0.4.1", @@ -19840,6 +19844,8 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -19857,6 +19863,8 @@ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 6" } @@ -20398,6 +20406,8 @@ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0" }, @@ -20740,12 +20750,6 @@ "dev": true, "license": "MIT" }, - "node_modules/delimit-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/delimit-stream/-/delimit-stream-0.1.0.tgz", - "integrity": "sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ==", - "license": "BSD-2-Clause" - }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -20840,6 +20844,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -21008,9 +21013,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -21020,9 +21025,9 @@ } }, "node_modules/dotenv-expand": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", - "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.5" @@ -21034,6 +21039,18 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -21068,6 +21085,8 @@ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -21078,7 +21097,9 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -21096,9 +21117,9 @@ "license": "MIT" }, "node_modules/effect": { - "version": "3.16.12", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", - "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -21134,6 +21155,8 @@ "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -21277,9 +21300,9 @@ } }, "node_modules/envalid": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.0.tgz", - "integrity": "sha512-OT6+qVhKVyCidaGoXflb2iK1tC8pd0OV2Q+v9n33wNhUJ+lus+rJobUj4vJaQBPxPZ0vYrPGuxdrenyCAIJcow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.1.tgz", + "integrity": "sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -21765,32 +21788,6 @@ "dev": true, "license": "MIT" }, - "node_modules/eslint-plugin-cypress": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-4.2.0.tgz", - "integrity": "sha512-v5cyt0VYb1tEEODBJSE44PocYOwQsckyexJhCs7LtdD3FGO6D2GjnZB2s2Sts4RcxdxECTWX01nObOZRs26bQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "globals": "^15.11.0" - }, - "peerDependencies": { - "eslint": ">=9" - } - }, - "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", @@ -22181,6 +22178,8 @@ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -22204,7 +22203,9 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/executable": { "version": "4.1.1", @@ -22212,6 +22213,8 @@ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "pify": "^2.2.0" }, @@ -22234,6 +22237,8 @@ "integrity": "sha512-MsG3prOVw1WtLXAZbM3KiYtooKR1LvxHh3VHsVtIy0uiUu8usxgB/94DP2HxtD/661lLdB6yzQ09lGJSQr6nkg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -22522,6 +22527,8 @@ "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "concat-stream": "^1.6.2", "debug": "^2.6.9", @@ -22538,6 +22545,8 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "2.0.0" } @@ -22547,7 +22556,9 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/extsprintf": { "version": "1.3.0", @@ -22557,7 +22568,9 @@ "engines": [ "node >=0.6.0" ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/fast-check": { "version": "3.23.2", @@ -22715,6 +22728,8 @@ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "pend": "~1.2.0" } @@ -22758,6 +22773,8 @@ "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "escape-string-regexp": "^1.0.5", "object-assign": "^4.1.0" @@ -22772,6 +22789,8 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -23088,6 +23107,8 @@ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -23632,21 +23653,14 @@ "node": ">= 0.4" } }, - "node_modules/get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "pump": "^3.0.0" }, @@ -23688,6 +23702,8 @@ "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "async": "^3.2.0" } @@ -23698,6 +23714,8 @@ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0" } @@ -23809,6 +23827,8 @@ "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ini": "1.3.7" }, @@ -23824,7 +23844,9 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/global-modules": { "version": "1.0.0", @@ -24105,6 +24127,8 @@ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -24118,6 +24142,8 @@ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -24145,16 +24171,6 @@ "node": ">=8" } }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -24685,6 +24701,8 @@ "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", @@ -24719,6 +24737,8 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=8.12.0" } @@ -24786,12 +24806,6 @@ "postcss": "^8.1.0" } }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", - "license": "ISC" - }, "node_modules/identity-obj-proxy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", @@ -24946,6 +24960,8 @@ "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -25022,36 +25038,13 @@ "@stencil/core": "^4.35.3" } }, - "node_modules/ionicons/node_modules/@stencil/core": { - "version": "4.36.3", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.3.tgz", - "integrity": "sha512-C9DOaAjm+hSYRuVoUuYWG/lrYT8+4DG0AL0m1Ea9+G5v2Y6ApVpNJLbXvFlRZIdDMGecH86s6v0Gp39uockLxg==", - "license": "MIT", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.10.0" - }, - "optionalDependencies": { - "@rollup/rollup-darwin-arm64": "4.34.9", - "@rollup/rollup-darwin-x64": "4.34.9", - "@rollup/rollup-linux-arm64-gnu": "4.34.9", - "@rollup/rollup-linux-arm64-musl": "4.34.9", - "@rollup/rollup-linux-x64-gnu": "4.34.9", - "@rollup/rollup-linux-x64-musl": "4.34.9", - "@rollup/rollup-win32-arm64-msvc": "4.34.9", - "@rollup/rollup-win32-x64-msvc": "4.34.9" - } - }, "node_modules/ioredis": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", - "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz", + "integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==", "license": "MIT", "dependencies": { - "@ioredis/commands": "^1.1.1", + "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", @@ -25212,6 +25205,8 @@ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ci-info": "^2.0.0" }, @@ -25388,6 +25383,8 @@ "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "global-dirs": "^2.0.1", "is-path-inside": "^3.0.1" @@ -25483,6 +25480,8 @@ "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "symbol-observable": "^1.1.0" }, @@ -25496,6 +25495,8 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -25535,7 +25536,9 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/is-regex": { "version": "1.2.1", @@ -25653,7 +25656,9 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", @@ -25783,15 +25788,6 @@ "dev": true, "license": "ISC" }, - "node_modules/iso-url": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/iso-url/-/iso-url-0.4.7.tgz", - "integrity": "sha512-27fFRDnPAMnHGLq36bWTpKET+eiXct3ENlCcdcMdk+mjXrb2kw3mhBUg1B7ewAC0kVzlOPhADzQgz1SE6Tglog==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -25817,7 +25813,9 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -30047,12 +30045,6 @@ "license": "MIT", "peer": true }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -30263,16 +30255,9 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, - "license": "ISC" - }, - "node_modules/json-text-sequence": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/json-text-sequence/-/json-text-sequence-0.1.1.tgz", - "integrity": "sha512-L3mEegEWHRekSHjc7+sc8eJhba9Clq1PZ8kMkzf8OxElhXc8O4TS5MwcVlj9aEbm5dr81N90WHC5nAz3UO971w==", - "license": "MIT", - "dependencies": { - "delimit-stream": "0.1.0" - } + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -30462,6 +30447,8 @@ "node >=0.6.0" ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -30768,6 +30755,8 @@ "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "> 0.8" } @@ -30948,6 +30937,8 @@ "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@samverschueren/stream-to-observable": "^0.3.0", "is-observable": "^1.1.0", @@ -30969,6 +30960,8 @@ "integrity": "sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -30979,6 +30972,8 @@ "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^1.1.3", "cli-truncate": "^0.2.1", @@ -31002,6 +30997,8 @@ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -31012,6 +31009,8 @@ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -31022,6 +31021,8 @@ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -31039,6 +31040,8 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -31049,6 +31052,8 @@ "integrity": "sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^1.0.0" }, @@ -31062,6 +31067,8 @@ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -31075,6 +31082,8 @@ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -31085,6 +31094,8 @@ "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^2.4.1", "cli-cursor": "^2.1.0", @@ -31101,6 +31112,8 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -31114,6 +31127,8 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -31129,6 +31144,8 @@ "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "restore-cursor": "^2.0.0" }, @@ -31142,6 +31159,8 @@ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-name": "1.1.3" } @@ -31151,14 +31170,18 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/listr-verbose-renderer/node_modules/date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/listr-verbose-renderer/node_modules/escape-string-regexp": { "version": "1.0.5", @@ -31166,6 +31189,8 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -31176,6 +31201,8 @@ "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -31189,6 +31216,8 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31199,6 +31228,8 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31209,6 +31240,8 @@ "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "mimic-fn": "^1.0.0" }, @@ -31222,6 +31255,8 @@ "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -31235,7 +31270,9 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/listr-verbose-renderer/node_modules/supports-color": { "version": "5.5.0", @@ -31243,6 +31280,8 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -31256,6 +31295,8 @@ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -31266,6 +31307,8 @@ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^1.9.0" }, @@ -31278,7 +31321,9 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/listr2": { "version": "9.0.1", @@ -31566,9 +31611,9 @@ } }, "node_modules/load-esm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", - "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", "funding": [ { "type": "github", @@ -31780,6 +31825,8 @@ "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-escapes": "^3.0.0", "cli-cursor": "^2.0.0", @@ -31795,6 +31842,8 @@ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31805,6 +31854,8 @@ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31815,6 +31866,8 @@ "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "restore-cursor": "^2.0.0" }, @@ -31828,6 +31881,8 @@ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31838,6 +31893,8 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -31848,6 +31905,8 @@ "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "mimic-fn": "^1.0.0" }, @@ -31861,6 +31920,8 @@ "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -31874,7 +31935,9 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/log-update/node_modules/string-width": { "version": "2.1.1", @@ -31882,6 +31945,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -31896,6 +31961,8 @@ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^3.0.0" }, @@ -31909,6 +31976,8 @@ "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0" @@ -31964,6 +32033,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.3" @@ -31985,9 +32055,9 @@ "license": "ISC" }, "node_modules/luxon": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", - "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", "license": "MIT", "engines": { "node": ">=12" @@ -32032,6 +32102,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, "license": "ISC" }, "node_modules/make-fetch-happen": { @@ -32524,6 +32595,8 @@ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -32578,9 +32651,9 @@ } }, "node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", @@ -32730,9 +32803,9 @@ "license": "MIT" }, "node_modules/ng-extract-i18n-merge": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ng-extract-i18n-merge/-/ng-extract-i18n-merge-3.0.0.tgz", - "integrity": "sha512-vTWtAz6a/wVYxnUMFHp1ur6o4JSLm+LcxdSMV8o8Ml2p5oCsSB4iFd5E6h8Yb8X8D596qyBz0ELgiDmbn4YyRQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ng-extract-i18n-merge/-/ng-extract-i18n-merge-3.1.0.tgz", + "integrity": "sha512-4rJRcpTcP54xf5cjoz3S1By0T04X2RoyQcMDxr4wLdRx3fVxkeP8jeuLzmj9F4G5n0yMQb+6jhUiFERxpkfs1w==", "license": "MIT", "dependencies": { "@angular-devkit/architect": "^0.2000.0", @@ -32867,6 +32940,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, "license": "MIT", "dependencies": { "lower-case": "^2.0.2", @@ -33277,6 +33351,8 @@ "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -33444,6 +33520,19 @@ "node": ">=8" } }, + "node_modules/nx/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/nx/node_modules/dotenv-expand": { "version": "11.0.7", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", @@ -34138,7 +34227,9 @@ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/own-keys": { "version": "1.0.1", @@ -34225,6 +34316,8 @@ "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -34689,12 +34782,13 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/path-type": { @@ -34734,7 +34828,9 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/perfect-debounce": { "version": "1.0.0", @@ -34748,7 +34844,9 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/picocolors": { "version": "1.1.1", @@ -35634,9 +35732,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.3.tgz", + "integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==", "dev": true, "license": "MIT", "peer": true, @@ -35669,6 +35767,8 @@ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" }, @@ -35736,16 +35836,16 @@ } }, "node_modules/prisma": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.17.1.tgz", - "integrity": "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", + "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@prisma/config": "6.17.1", - "@prisma/engines": "6.17.1" + "@prisma/config": "6.19.0", + "@prisma/engines": "6.19.0" }, "bin": { "prisma": "build/index.js" @@ -35872,6 +35972,8 @@ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -35993,7 +36095,9 @@ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/randombytes": { "version": "2.1.0", @@ -36452,16 +36556,6 @@ "entities": "^2.0.0" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/replace-in-file": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.3.0.tgz", @@ -36518,6 +36612,8 @@ "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "throttleit": "^1.0.0" } @@ -36528,6 +36624,8 @@ "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -36683,6 +36781,8 @@ "integrity": "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "exit-hook": "^1.0.0", "onetime": "^1.0.0" @@ -36697,6 +36797,8 @@ "integrity": "sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -37631,17 +37733,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -38047,12 +38138,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/simple-cbor": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/simple-cbor/-/simple-cbor-0.4.1.tgz", - "integrity": "sha512-rijcxtwx2b4Bje3sqeIqw5EeW7UlOIC4YfOdwqIKacpvRQ/D78bWg/4/0m5e0U91oKvlGh7LlJuZCu07ISCC7w==", - "license": "ISC" - }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -38091,6 +38176,8 @@ "integrity": "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -38291,19 +38378,6 @@ "wbuf": "^1.7.3" } }, - "node_modules/split-text-to-chunks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split-text-to-chunks/-/split-text-to-chunks-1.0.0.tgz", - "integrity": "sha512-HLtEwXK/T4l7QZSJ/kOSsZC0o5e2Xg3GzKKFxm0ZexJXw0Bo4CaEl39l7MCSRHk9EOOL5jT8JIDjmhTtcoe6lQ==", - "license": "MIT", - "dependencies": { - "get-stdin": "^5.0.1", - "minimist": "^1.2.0" - }, - "bin": { - "wordwrap": "cli.js" - } - }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -38317,6 +38391,8 @@ "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -38342,14 +38418,18 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/sshpk/node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true, - "license": "Unlicense" + "license": "Unlicense", + "optional": true, + "peer": true }, "node_modules/ssri": { "version": "12.0.0", @@ -38960,9 +39040,9 @@ "license": "BSD-2-Clause" }, "node_modules/svgmap": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/svgmap/-/svgmap-2.12.2.tgz", - "integrity": "sha512-SCX1Oys3v1dz3mTEbQha+6lrHGyu3LwXBhcgW0HlTh7waQDMFqNUKD8hADvDaPkPapRvNCLMnXaVD1Pbxbnhow==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/svgmap/-/svgmap-2.14.0.tgz", + "integrity": "sha512-+Vklx4DO1uv1SFq6wnJWl/dRjX4uRT9CcsIHuADxAcZ+h5X1OSyDVbNdIu837fx5TtYYuaGRhWuFCXIioN/1ww==", "license": "MIT", "dependencies": { "svg-pan-zoom": "^3.6.2" @@ -39023,6 +39103,8 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -39074,16 +39156,114 @@ } }, "node_modules/tablemark": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tablemark/-/tablemark-3.1.0.tgz", - "integrity": "sha512-IwO6f0SEzp1Z+zqz/7ANUmeEac4gaNlknWyj/S9aSg11wZmWYnLeyI/xXvEOU88BYUIf8y30y0wxB58xIKrVlQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tablemark/-/tablemark-4.1.0.tgz", + "integrity": "sha512-B3LDjbDo+ac+D5RwkBOPZZ6ua8716KdT+6NO3DKOCHJq0ezE6vV2r92rjrC1ci2H+ocuysl5ytf1T0QqV65yoA==", "license": "MIT", "dependencies": { - "sentence-case": "^3.0.4", - "split-text-to-chunks": "^1.0.0" + "ansi-regex": "^6.2.2", + "change-case": "^5.4.4", + "string-width": "^8.1.0", + "wordwrapjs": "^5.1.0", + "wrap-ansi": "^9.0.2" }, "engines": { - "node": ">=14.16" + "node": ">=20" + } + }, + "node_modules/tablemark/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/tablemark/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tablemark/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/tablemark/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tablemark/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tablemark/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/tablemark/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/tapable": { @@ -39925,6 +40105,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -40096,6 +40277,8 @@ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -40103,16 +40286,10 @@ "node": "*" } }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense" - }, "node_modules/twitter-api-v2": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.23.0.tgz", - "integrity": "sha512-5i1agETVpTuY68Zuk9i2B3N9wHzc4JIWw0WKyG4CEaFv9mRKmU87roa+U1oYYXTChWb0HMcqfkwoBJHYmLbeDA==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.27.0.tgz", + "integrity": "sha512-hbIFwzg0NeOcFOdmJqtKMCXjLjc0INff/7NwhnZ2zpnw65oku8i+0eMxo5M0iTc1hs+inD/IpDw3S0Xh2c45QQ==", "license": "Apache-2.0" }, "node_modules/type-check": { @@ -40522,6 +40699,8 @@ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -40567,15 +40746,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -40592,6 +40762,8 @@ "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" @@ -40622,7 +40794,9 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/url/node_modules/qs": { "version": "6.14.0", @@ -40630,6 +40804,8 @@ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -40680,6 +40856,7 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", + "optional": true, "bin": { "uuid": "dist/esm/bin/uuid" } @@ -40688,6 +40865,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { @@ -40767,6 +40945,8 @@ "node >=0.6.0" ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -40778,7 +40958,9 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/vite": { "version": "7.1.5", @@ -41001,19 +41183,6 @@ "license": "MIT", "optional": true }, - "node_modules/webcrypto-core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.8.1.tgz", - "integrity": "sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/json-schema": "^1.1.12", - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.7.0" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -41979,6 +42148,15 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrapjs": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -42116,9 +42294,9 @@ } }, "node_modules/yahoo-finance2": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.10.0.tgz", - "integrity": "sha512-0mnvefEAapMS6M3tnqLmQlyE2W38AQqByaTS09l2dawLaVU7NNc0hJ4qI4F3qi3C7MU+ZWAb8DFVKpW6Zsj0Nw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.10.2.tgz", + "integrity": "sha512-MH4EdugRurygLTMd1UryPwfYR8aWSOeyh++JSarMrf+bROfvNGmE0lAi/C9TuTc3mH8ORuRdt+O9PEeCCmzTLg==", "license": "MIT", "dependencies": { "@deno/shim-deno": "~0.18.0", @@ -42301,6 +42479,8 @@ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -42310,6 +42490,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" diff --git a/package.json b/package.json index ff8adc51f..dee96482a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.209.0", + "version": "2.221.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -9,7 +9,6 @@ "affected:apps": "nx affected:apps", "affected:build": "nx affected:build", "affected:dep-graph": "nx affected:dep-graph", - "affected:e2e": "nx affected:e2e", "affected:libs": "nx affected:libs", "affected:lint": "nx affected:lint", "affected:test": "nx affected:test", @@ -27,7 +26,6 @@ "database:setup": "npm run database:push && npm run database:seed", "database:validate-schema": "prisma validate", "dep-graph": "nx dep-graph", - "e2e": "ng e2e", "extract-locales": "nx run client:extract-i18n --output-path ./apps/client/src/locales", "format": "nx format:write", "format:check": "nx format:check", @@ -69,27 +67,22 @@ "@angular/service-worker": "20.2.4", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.0", - "@dfinity/agent": "0.15.7", - "@dfinity/auth-client": "0.15.7", - "@dfinity/candid": "0.15.7", - "@dfinity/identity": "0.15.7", - "@dfinity/principal": "0.15.7", - "@internationalized/number": "3.6.3", - "@ionic/angular": "8.7.3", + "@internationalized/number": "3.6.5", + "@ionic/angular": "8.7.8", "@keyv/redis": "4.4.0", - "@nestjs/bull": "11.0.2", + "@nestjs/bull": "11.0.4", "@nestjs/cache-manager": "3.0.1", - "@nestjs/common": "11.1.3", + "@nestjs/common": "11.1.8", "@nestjs/config": "4.0.2", - "@nestjs/core": "11.1.3", + "@nestjs/core": "11.1.8", "@nestjs/event-emitter": "3.0.1", - "@nestjs/jwt": "11.0.0", + "@nestjs/jwt": "11.0.1", "@nestjs/passport": "11.0.5", - "@nestjs/platform-express": "11.1.3", - "@nestjs/schedule": "6.0.0", - "@nestjs/serve-static": "5.0.3", + "@nestjs/platform-express": "11.1.8", + "@nestjs/schedule": "6.0.1", + "@nestjs/serve-static": "5.0.4", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.17.1", + "@prisma/client": "6.19.0", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -98,7 +91,7 @@ "big.js": "7.0.1", "bootstrap": "4.6.2", "bull": "4.16.5", - "chart.js": "4.5.0", + "chart.js": "4.5.1", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-treemap": "3.1.0", "chartjs-plugin-annotation": "3.1.0", @@ -106,12 +99,14 @@ "cheerio": "1.0.0", "class-transformer": "0.5.1", "class-validator": "0.14.2", - "color": "5.0.0", + "color": "5.0.3", "countries-and-timezones": "3.8.0", - "countries-list": "3.1.1", + "countries-list": "3.2.0", "countup.js": "2.9.0", "date-fns": "4.1.0", - "envalid": "8.1.0", + "dotenv": "17.2.3", + "dotenv-expand": "12.0.3", + "envalid": "8.1.1", "fuse.js": "7.1.0", "google-spreadsheet": "3.2.0", "helmet": "7.0.0", @@ -121,7 +116,7 @@ "lodash": "4.17.21", "marked": "15.0.4", "ms": "3.0.0-canary.1", - "ng-extract-i18n-merge": "3.0.0", + "ng-extract-i18n-merge": "3.1.0", "ngx-device-detector": "10.1.0", "ngx-markdown": "20.0.0", "ngx-skeleton-loader": "11.3.0", @@ -135,11 +130,10 @@ "reflect-metadata": "0.2.2", "rxjs": "7.8.1", "stripe": "18.5.0", - "svgmap": "2.12.2", - "tablemark": "3.1.0", - "twitter-api-v2": "1.23.0", - "uuid": "11.1.0", - "yahoo-finance2": "3.10.0", + "svgmap": "2.14.0", + "tablemark": "4.1.0", + "twitter-api-v2": "1.27.0", + "yahoo-finance2": "3.10.2", "zone.js": "0.15.1" }, "devDependencies": { @@ -156,10 +150,9 @@ "@angular/pwa": "20.2.2", "@eslint/eslintrc": "3.3.1", "@eslint/js": "9.35.0", - "@nestjs/schematics": "11.0.5", - "@nestjs/testing": "11.1.3", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.8", "@nx/angular": "21.5.1", - "@nx/cypress": "21.5.1", "@nx/eslint-plugin": "21.5.1", "@nx/jest": "21.5.1", "@nx/js": "21.5.1", @@ -182,10 +175,8 @@ "@types/passport-google-oauth20": "2.0.16", "@typescript-eslint/eslint-plugin": "8.43.0", "@typescript-eslint/parser": "8.43.0", - "cypress": "6.2.1", "eslint": "9.35.0", "eslint-config-prettier": "10.1.8", - "eslint-plugin-cypress": "4.2.0", "eslint-plugin-import": "2.32.0", "eslint-plugin-storybook": "9.1.5", "husky": "9.1.7", @@ -193,9 +184,9 @@ "jest-environment-jsdom": "29.7.0", "jest-preset-angular": "14.6.0", "nx": "21.5.1", - "prettier": "3.6.2", + "prettier": "3.7.3", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.17.1", + "prisma": "6.19.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", diff --git a/prisma.config.ts b/prisma.config.ts deleted file mode 100644 index 24da6d886..000000000 --- a/prisma.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import 'dotenv/config'; -import { join } from 'node:path'; -import { defineConfig } from 'prisma/config'; - -export default defineConfig({ - migrations: { - path: join('prisma', 'migrations'), - seed: `node ${join('prisma', 'seed.mts')}` - }, - schema: join('prisma', 'schema.prisma') -}); diff --git a/test/import/not-ok/invalid-data-source.json b/test/import/not-ok/invalid-data-source.json index 472e295ee..f8e920c67 100644 --- a/test/import/not-ok/invalid-data-source.json +++ b/test/import/not-ok/invalid-data-source.json @@ -14,5 +14,11 @@ "type": "BUY", "unitPrice": 100.0 } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/not-ok/invalid-date-before-min.json b/test/import/not-ok/invalid-date-before-min.json index 3040581b2..260d79166 100644 --- a/test/import/not-ok/invalid-date-before-min.json +++ b/test/import/not-ok/invalid-date-before-min.json @@ -14,5 +14,11 @@ "type": "BUY", "unitPrice": 100.0 } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/not-ok/invalid-date.json b/test/import/not-ok/invalid-date.json index 99cd6d156..4522c6dcc 100644 --- a/test/import/not-ok/invalid-date.json +++ b/test/import/not-ok/invalid-date.json @@ -14,5 +14,11 @@ "type": "BUY", "unitPrice": 100.0 } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/not-ok/invalid-symbol.json b/test/import/not-ok/invalid-symbol.json index 14f0051ec..0bbbf53db 100644 --- a/test/import/not-ok/invalid-symbol.json +++ b/test/import/not-ok/invalid-symbol.json @@ -14,5 +14,11 @@ "type": "BUY", "unitPrice": 100.0 } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/not-ok/invalid-type.json b/test/import/not-ok/invalid-type.json index a23f72411..a8967d81a 100644 --- a/test/import/not-ok/invalid-type.json +++ b/test/import/not-ok/invalid-type.json @@ -14,5 +14,11 @@ "type": "", "unitPrice": 100.0 } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/not-ok/unavailable-exchange-rate.json b/test/import/not-ok/unavailable-exchange-rate.json index 4d8be156a..66c7044d7 100644 --- a/test/import/not-ok/unavailable-exchange-rate.json +++ b/test/import/not-ok/unavailable-exchange-rate.json @@ -15,5 +15,11 @@ "date": "1990-01-01T00:00:00.000Z", "symbol": "MSFT" } - ] + ], + "user": { + "settings": { + "currency": "USD", + "performanceCalculationType": "ROAI" + } + } } diff --git a/test/import/ok/500-activities.json b/test/import/ok/500-activities.json index b691a6f9f..2793c695e 100644 --- a/test/import/ok/500-activities.json +++ b/test/import/ok/500-activities.json @@ -6019,7 +6019,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/btceur.json b/test/import/ok/btceur.json index b370682f9..ae9eb8921 100644 --- a/test/import/ok/btceur.json +++ b/test/import/ok/btceur.json @@ -23,7 +23,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/btcusd-short.json b/test/import/ok/btcusd-short.json index bc4152de9..6f25a7740 100644 --- a/test/import/ok/btcusd-short.json +++ b/test/import/ok/btcusd-short.json @@ -36,7 +36,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/btcusd.json b/test/import/ok/btcusd.json index fc2e1f66e..4a85f967e 100644 --- a/test/import/ok/btcusd.json +++ b/test/import/ok/btcusd.json @@ -23,7 +23,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/derived-currency.json b/test/import/ok/derived-currency.json index e740c1ae3..637ab21b6 100644 --- a/test/import/ok/derived-currency.json +++ b/test/import/ok/derived-currency.json @@ -31,7 +31,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/novn-buy-and-sell-partially.json b/test/import/ok/novn-buy-and-sell-partially.json index 8c5778566..3bdd7eb7e 100644 --- a/test/import/ok/novn-buy-and-sell-partially.json +++ b/test/import/ok/novn-buy-and-sell-partially.json @@ -27,7 +27,8 @@ ], "user": { "settings": { - "currency": "CHF" + "currency": "CHF", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/novn-buy-and-sell.json b/test/import/ok/novn-buy-and-sell.json index 71ee9b7a9..6ae519d87 100644 --- a/test/import/ok/novn-buy-and-sell.json +++ b/test/import/ok/novn-buy-and-sell.json @@ -27,7 +27,8 @@ ], "user": { "settings": { - "currency": "CHF" + "currency": "CHF", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/penthouse-apartment.json b/test/import/ok/penthouse-apartment.json index 2bc7f0cf8..0c35521e6 100644 --- a/test/import/ok/penthouse-apartment.json +++ b/test/import/ok/penthouse-apartment.json @@ -47,7 +47,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/sample.json b/test/import/ok/sample.json index 21277129f..bc2798718 100644 --- a/test/import/ok/sample.json +++ b/test/import/ok/sample.json @@ -147,7 +147,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/vti-buy-long-history.json b/test/import/ok/vti-buy-long-history.json index c8cd25e60..88b38d2b1 100644 --- a/test/import/ok/vti-buy-long-history.json +++ b/test/import/ok/vti-buy-long-history.json @@ -40,7 +40,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } } diff --git a/test/import/ok/without-accounts.json b/test/import/ok/without-accounts.json index 8a24f86fc..2283dd889 100644 --- a/test/import/ok/without-accounts.json +++ b/test/import/ok/without-accounts.json @@ -47,7 +47,8 @@ ], "user": { "settings": { - "currency": "USD" + "currency": "USD", + "performanceCalculationType": "ROAI" } } }