From 8fab73f122af289a0266db1fb900efbdd453da14 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:12:16 +0200 Subject: [PATCH 01/16] Feature/update OSS friends (#3718) --- apps/client/src/assets/oss-friends.json | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/client/src/assets/oss-friends.json b/apps/client/src/assets/oss-friends.json index 88037573f..0fe726510 100644 --- a/apps/client/src/assets/oss-friends.json +++ b/apps/client/src/assets/oss-friends.json @@ -1,5 +1,5 @@ { - "createdAt": "2024-04-09T00:00:00.000Z", + "createdAt": "2024-08-31T00:00:00.000Z", "data": [ { "name": "Aptabase", @@ -46,11 +46,6 @@ "description": "dyrector.io is an open-source continuous delivery & deployment platform with version management.", "href": "https://dyrector.io" }, - { - "name": "Erxes", - "description": "The Open-Source HubSpot Alternative. A single XOS enables to create unique and life-changing experiences that work for all types of business.", - "href": "https://erxes.io" - }, { "name": "Firecamp", "description": "vscode for apis, open-source postman/insomnia alternative", @@ -86,11 +81,6 @@ "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": "Keep", - "description": "Open source alert management and AIOps platform.", - "href": "https://keephq.dev" - }, { "name": "Langfuse", "description": "Open source LLM engineering platform. Debug, analyze and iterate together.", @@ -116,6 +106,11 @@ "description": "Open-source monitoring platform with beautiful status pages", "href": "https://www.openstatus.dev" }, + { + "name": "Portkey AI", + "description": "AI Gateway with integrated Guardrails. Route to 250+ LLMs and 50+ Guardrails with 1-fast API. Supports caching, retries, and edge deployment for low latency.", + "href": "https://www.portkey.ai" + }, { "name": "Prisma", "description": "Simplify working with databases. Build, optimize, and grow your app easily with an intuitive data model, type-safety, automated migrations, connection pooling, caching, and real-time db subscriptions.", @@ -126,11 +121,6 @@ "description": "Makes frontend development cycle 10x faster with API Client, Mock Server, Intercept & Modify HTTP Requests and Session Replays.", "href": "https://requestly.com" }, - { - "name": "Revert", - "description": "The open-source unified API to build B2B integrations remarkably fast", - "href": "https://revert.dev" - }, { "name": "Rivet", "description": "Open-source solution to deploy, scale, and operate your multiplayer game.", From a6d9f5dd69f7935904ceb402c3072a9613cbbbbd Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:24:14 +0200 Subject: [PATCH 02/16] Bugfix/load data once on portfolio activities page (#3726) * Fetch activities only once * Update changelog --- CHANGELOG.md | 3 ++- .../pages/portfolio/activities/activities-page.component.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25cae43aa..d5945c854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 2.106.0-beta.5 - 2024-08-31 +## Unreleased ### Added @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental) +- Fixed an issue on the portfolio activities page by loading the data only once ## 2.105.0 - 2024-08-21 diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index 7cd89d62f..a7638c561 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -108,8 +108,6 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit { this.changeDetectorRef.markForCheck(); } }); - - this.fetchActivities(); } public fetchActivities() { From c48e4ec4c632b0e2f5188b83463742a3703e26da Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:42:49 +0200 Subject: [PATCH 03/16] Feature/improve usage of portfolio calculator in holding endpoint (#3727) * Improve usage of portfolio calculator --- .../src/app/portfolio/portfolio.service.ts | 65 +++++++++---------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 01dd55112..0cd602046 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -602,14 +602,7 @@ export class PortfolioService { userId }); - const orders = activities.filter(({ SymbolProfile }) => { - return ( - SymbolProfile.dataSource === aDataSource && - SymbolProfile.symbol === aSymbol - ); - }); - - if (orders.length <= 0) { + if (activities.length === 0) { return { accounts: [], averagePrice: undefined, @@ -646,10 +639,8 @@ export class PortfolioService { ]); const portfolioCalculator = this.calculatorFactory.createCalculator({ + activities, userId, - activities: orders.filter((order) => { - return ['BUY', 'DIVIDEND', 'ITEM', 'SELL'].includes(order.type); - }), calculationType: PerformanceCalculationType.TWR, currency: userCurrency }); @@ -659,8 +650,8 @@ export class PortfolioService { const { positions } = await portfolioCalculator.getSnapshot(); - const position = positions.find(({ symbol }) => { - return symbol === aSymbol; + const position = positions.find(({ dataSource, symbol }) => { + return dataSource === aDataSource && symbol === aSymbol; }); if (position) { @@ -673,14 +664,22 @@ export class PortfolioService { firstBuyDate, marketPrice, quantity, + symbol, tags, timeWeightedInvestment, timeWeightedInvestmentWithCurrencyEffect, transactionCount } = position; + const activitiesOfPosition = activities.filter(({ SymbolProfile }) => { + return ( + SymbolProfile.dataSource === dataSource && + SymbolProfile.symbol === symbol + ); + }); + const accounts: PortfolioHoldingDetail['accounts'] = uniqBy( - orders.filter(({ Account }) => { + activitiesOfPosition.filter(({ Account }) => { return Account; }), 'Account.id' @@ -715,8 +714,8 @@ export class PortfolioService { ); const historicalDataArray: HistoricalDataItem[] = []; - let maxPrice = Math.max(orders[0].unitPrice, marketPrice); - let minPrice = Math.min(orders[0].unitPrice, marketPrice); + let maxPrice = Math.max(activitiesOfPosition[0].unitPrice, marketPrice); + let minPrice = Math.min(activitiesOfPosition[0].unitPrice, marketPrice); if (historicalData[aSymbol]) { let j = -1; @@ -760,10 +759,10 @@ export class PortfolioService { } else { // Add historical entry for buy date, if no historical data available historicalDataArray.push({ - averagePrice: orders[0].unitPrice, + averagePrice: activitiesOfPosition[0].unitPrice, date: firstBuyDate, - marketPrice: orders[0].unitPrice, - quantity: orders[0].quantity + marketPrice: activitiesOfPosition[0].unitPrice, + quantity: activitiesOfPosition[0].quantity }); } @@ -773,7 +772,6 @@ export class PortfolioService { marketPrice, maxPrice, minPrice, - orders, SymbolProfile, tags, transactionCount, @@ -805,6 +803,7 @@ export class PortfolioService { ]?.toNumber(), netPerformanceWithCurrencyEffect: position.netPerformanceWithCurrencyEffectMap?.['max']?.toNumber(), + orders: activitiesOfPosition, quantity: quantity.toNumber(), value: this.exchangeRateDataService.toCurrency( quantity.mul(marketPrice ?? 0).toNumber(), @@ -862,7 +861,6 @@ export class PortfolioService { marketPrice, maxPrice, minPrice, - orders, SymbolProfile, accounts: [], averagePrice: 0, @@ -882,6 +880,7 @@ export class PortfolioService { netPerformancePercent: undefined, netPerformancePercentWithCurrencyEffect: undefined, netPerformanceWithCurrencyEffect: undefined, + orders: [], quantity: 0, tags: [], transactionCount: undefined, @@ -912,7 +911,7 @@ export class PortfolioService { userCurrency: this.getUserCurrency() }); - if (activities?.length <= 0) { + if (activities.length === 0) { return { hasErrors: false, positions: [] @@ -1037,14 +1036,12 @@ export class PortfolioService { dateRange = 'max', filters, impersonationId, - portfolioCalculator, userId, withExcludedAccounts = false }: { dateRange?: DateRange; filters?: Filter[]; impersonationId: string; - portfolioCalculator?: PortfolioCalculator; userId: string; withExcludedAccounts?: boolean; }): Promise { @@ -1089,7 +1086,7 @@ export class PortfolioService { userId }); - if (accountBalanceItems?.length <= 0 && activities?.length <= 0) { + if (accountBalanceItems.length === 0 && activities.length === 0) { return { chart: [], firstOrderDate: undefined, @@ -1106,16 +1103,14 @@ export class PortfolioService { }; } - portfolioCalculator = - portfolioCalculator ?? - this.calculatorFactory.createCalculator({ - accountBalanceItems, - activities, - filters, - userId, - calculationType: PerformanceCalculationType.TWR, - currency: userCurrency - }); + const portfolioCalculator = this.calculatorFactory.createCalculator({ + accountBalanceItems, + activities, + filters, + userId, + calculationType: PerformanceCalculationType.TWR, + currency: userCurrency + }); const { errors, hasErrors, historicalData } = await portfolioCalculator.getSnapshot(); From d6dbc0d9e3a166c31d1253b9f7bf2c52bc6c4122 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:44:42 +0200 Subject: [PATCH 04/16] Release 2.106.0-beta.6 (#3728) --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5945c854..5427d6a3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.106.0-beta.6 ### Added diff --git a/package.json b/package.json index e66dc3a3f..79ed8f8c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.106.0-beta.5", + "version": "2.106.0-beta.6", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 80182369424e92ecc5c4b5b4c4cb4bd0346bfb77 Mon Sep 17 00:00:00 2001 From: Daniel Idem <36500470+dinirichard@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:19:59 +0100 Subject: [PATCH 05/16] Bugfix/fix carousel component (#3709) * Fix carousel component * Update changelog --- CHANGELOG.md | 1 + .../src/app/pages/landing/landing-page.html | 2 +- .../lib/carousel/carousel-item.directive.ts | 12 +--- .../src/lib/carousel/carousel.component.html | 7 +- .../ui/src/lib/carousel/carousel.component.ts | 64 +++---------------- 5 files changed, 13 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5427d6a3b..d598db05c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental) - Fixed an issue on the portfolio activities page by loading the data only once +- Fixed an issue in the carousel component for the testimonial section on the landing page ## 2.105.0 - 2024-08-21 diff --git a/apps/client/src/app/pages/landing/landing-page.html b/apps/client/src/app/pages/landing/landing-page.html index 72de38c20..f726a6020 100644 --- a/apps/client/src/app/pages/landing/landing-page.html +++ b/apps/client/src/app/pages/landing/landing-page.html @@ -331,7 +331,7 @@
@for (testimonial of testimonials; track testimonial) { -
+
) {} - - public focus() { - this.element.nativeElement.focus({ preventScroll: true }); - } } diff --git a/libs/ui/src/lib/carousel/carousel.component.html b/libs/ui/src/lib/carousel/carousel.component.html index 27d94dfd5..9cf34fe02 100644 --- a/libs/ui/src/lib/carousel/carousel.component.html +++ b/libs/ui/src/lib/carousel/carousel.component.html @@ -11,12 +11,7 @@ } -
+
diff --git a/libs/ui/src/lib/carousel/carousel.component.ts b/libs/ui/src/lib/carousel/carousel.component.ts index 7f93297dd..8b766aa6d 100644 --- a/libs/ui/src/lib/carousel/carousel.component.ts +++ b/libs/ui/src/lib/carousel/carousel.component.ts @@ -1,24 +1,18 @@ -import { FocusKeyManager } from '@angular/cdk/a11y'; -import { LEFT_ARROW, RIGHT_ARROW, TAB } from '@angular/cdk/keycodes'; import { - AfterContentInit, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, - ContentChildren, + contentChildren, ElementRef, HostBinding, Inject, Input, Optional, - QueryList, ViewChild } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations'; -import { CarouselItem } from './carousel-item.directive'; - @Component({ changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatButtonModule], @@ -28,9 +22,7 @@ import { CarouselItem } from './carousel-item.directive'; styleUrls: ['./carousel.component.scss'], templateUrl: './carousel.component.html' }) -export class GfCarouselComponent implements AfterContentInit { - @ContentChildren(CarouselItem) public items!: QueryList; - +export class GfCarouselComponent { @HostBinding('class.animations-disabled') public readonly animationsDisabled: boolean; @@ -38,11 +30,11 @@ export class GfCarouselComponent implements AfterContentInit { @ViewChild('list') public list!: ElementRef; + public items = contentChildren('carouselItem', { read: ElementRef }); public showPrevArrow = false; public showNextArrow = true; private index = 0; - private keyManager!: FocusKeyManager; private position = 0; public constructor( @@ -51,12 +43,8 @@ export class GfCarouselComponent implements AfterContentInit { this.animationsDisabled = animationsModule === 'NoopAnimations'; } - public ngAfterContentInit() { - this.keyManager = new FocusKeyManager(this.items); - } - public next() { - for (let i = this.index; i < this.items.length; i++) { + for (let i = this.index; i < this.items().length; i++) { if (this.isOutOfView(i)) { this.index = i; this.scrollToActiveItem(); @@ -65,31 +53,6 @@ export class GfCarouselComponent implements AfterContentInit { } } - public onKeydown({ keyCode }: KeyboardEvent) { - const manager = this.keyManager; - const previousActiveIndex = manager.activeItemIndex; - - if (keyCode === LEFT_ARROW) { - manager.setPreviousItemActive(); - } else if (keyCode === RIGHT_ARROW) { - manager.setNextItemActive(); - } else if (keyCode === TAB && !manager.activeItem) { - manager.setFirstItemActive(); - } - - if ( - manager.activeItemIndex != null && - manager.activeItemIndex !== previousActiveIndex - ) { - this.index = manager.activeItemIndex; - this.updateItemTabIndices(); - - if (this.isOutOfView(this.index)) { - this.scrollToActiveItem(); - } - } - } - public previous() { for (let i = this.index; i > -1; i--) { if (this.isOutOfView(i)) { @@ -101,8 +64,7 @@ export class GfCarouselComponent implements AfterContentInit { } private isOutOfView(index: number, side?: 'start' | 'end') { - const { offsetWidth, offsetLeft } = - this.items.toArray()[index].element.nativeElement; + const { offsetWidth, offsetLeft } = this.items()[index].nativeElement; if ((!side || side === 'start') && offsetLeft - this.position < 0) { return true; @@ -120,33 +82,23 @@ export class GfCarouselComponent implements AfterContentInit { return; } - const itemsArray = this.items.toArray(); let targetItemIndex = this.index; if (this.index > 0 && !this.isOutOfView(this.index - 1)) { targetItemIndex = - itemsArray.findIndex((_, i) => !this.isOutOfView(i)) + 1; + this.items().findIndex((_, i) => !this.isOutOfView(i)) + 1; } - this.position = - itemsArray[targetItemIndex].element.nativeElement.offsetLeft; + this.position = this.items()[targetItemIndex].nativeElement.offsetLeft; this.list.nativeElement.style.transform = `translateX(-${this.position}px)`; this.showPrevArrow = this.index > 0; this.showNextArrow = false; - for (let i = itemsArray.length - 1; i > -1; i--) { + for (let i = this.items().length - 1; i > -1; i--) { if (this.isOutOfView(i, 'end')) { this.showNextArrow = true; break; } } } - - private updateItemTabIndices() { - this.items.forEach((item: CarouselItem) => { - if (this.keyManager != null) { - item.tabindex = item === this.keyManager.activeItem ? '0' : '-1'; - } - }); - } } From 7ea906185219f608473a1737894fbb0278a75c39 Mon Sep 17 00:00:00 2001 From: Shaunak Das <51281688+shaun-ak@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:47:38 +0530 Subject: [PATCH 06/16] Feature/execute scraper configuration instantly (#3723) * Execute scraper configuration instantly * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + .../data-provider/manual/manual.service.ts | 35 +++++++++++++++++-- .../symbol-profile/symbol-profile.service.ts | 2 ++ .../scraper-configuration.interface.ts | 1 + 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d598db05c..4639d2485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Set up a performance logging service +- Added the attribute `mode` to the scraper configuration to get quotes instantly ### Changed 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 0655d2318..02e50a145 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -166,11 +166,42 @@ export class ManualService implements DataProviderInterface { } }); + const symbolProfilesWithScraperConfigurationAndInstantMode = + symbolProfiles.filter(({ scraperConfiguration }) => { + return scraperConfiguration?.mode === 'instant'; + }); + + const scraperResultPromises = + symbolProfilesWithScraperConfigurationAndInstantMode.map( + async ({ scraperConfiguration, symbol }) => { + try { + const marketPrice = await this.scrape(scraperConfiguration); + return { marketPrice, symbol }; + } catch (error) { + Logger.error( + `Could not get quote for ${symbol} (${this.getName()}): [${error.name}] ${error.message}`, + 'ManualService' + ); + return { symbol, marketPrice: undefined }; + } + } + ); + + // Wait for all scraping requests to complete concurrently + const scraperResults = await Promise.all(scraperResultPromises); + for (const { currency, symbol } of symbolProfiles) { - let marketPrice = + let { marketPrice } = + scraperResults.find((result) => { + return result.symbol === symbol; + }) ?? {}; + + marketPrice = + marketPrice ?? marketData.find((marketDataItem) => { return marketDataItem.symbol === symbol; - })?.marketPrice ?? 0; + })?.marketPrice ?? + 0; response[symbol] = { currency, diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 50cb25000..283da7b52 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -275,6 +275,8 @@ export class SymbolProfileService { headers: scraperConfiguration.headers as ScraperConfiguration['headers'], locale: scraperConfiguration.locale as string, + mode: + (scraperConfiguration.mode as ScraperConfiguration['mode']) ?? 'lazy', selector: scraperConfiguration.selector as string, url: scraperConfiguration.url as string }; diff --git a/libs/common/src/lib/interfaces/scraper-configuration.interface.ts b/libs/common/src/lib/interfaces/scraper-configuration.interface.ts index ef5506328..70fcd939d 100644 --- a/libs/common/src/lib/interfaces/scraper-configuration.interface.ts +++ b/libs/common/src/lib/interfaces/scraper-configuration.interface.ts @@ -2,6 +2,7 @@ export interface ScraperConfiguration { defaultMarketPrice?: number; headers?: { [key: string]: string }; locale?: string; + mode?: 'instant' | 'lazy'; selector: string; url: string; } From fb44933c9cd168b34e90a83eceeaf44cf6f9a61b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:21:32 +0200 Subject: [PATCH 07/16] Feature/improve error logs in scraper configuration test (#3730) * Improve error logs * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/admin/admin.controller.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4639d2485..448e1c5a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed the data format of the environment variable `CACHE_QUOTES_TTL` from seconds to milliseconds - Changed the data format of the environment variable `CACHE_TTL` from seconds to milliseconds - Removed the environment variable `MAX_ITEM_IN_CACHE` +- Improved the error logs of the scraper configuration test in the asset profile details dialog of the admin control - Improved the language localization for Polish (`pl`) - Migrated from `cache-manager-redis-store` to `cache-manager-redis-yet` - Upgraded `cache-manager` from version `3.4.3` to `5.7.6` diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 69e6955c1..6d201be23 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -239,9 +239,11 @@ export class AdminController { return { price }; } - throw new Error('Could not parse the current market price'); + throw new Error( + `Could not parse the current market price for ${symbol} (${dataSource})` + ); } catch (error) { - Logger.error(error); + Logger.error(error, 'AdminController'); throw new HttpException(error.message, StatusCodes.BAD_REQUEST); } From df5e2f5f0e7041ad73f27f77c983edff61aa6d18 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:23:38 +0200 Subject: [PATCH 08/16] Feature/extract common CACHE_TTL as constants (#3722) Extract CACHE_TTL * CACHE_TTL_NO_CACHE * CACHE_TTL_INFINITE --- apps/api/src/app/benchmark/benchmark.service.ts | 7 +++++-- .../src/app/portfolio/calculator/portfolio-calculator.ts | 3 ++- .../src/services/configuration/configuration.service.ts | 7 +++++-- libs/common/src/lib/config.ts | 3 +++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index 6cb375e0f..f57f5fa30 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -7,7 +7,10 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data/market-d 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'; -import { PROPERTY_BENCHMARKS } from '@ghostfolio/common/config'; +import { + CACHE_TTL_INFINITE, + PROPERTY_BENCHMARKS +} from '@ghostfolio/common/config'; import { DATE_FORMAT, calculateBenchmarkTrend, @@ -443,7 +446,7 @@ export class BenchmarkService { benchmarks, expiration: expiration.getTime() }), - 0 + CACHE_TTL_INFINITE ); } diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 5384fd6d8..2938bd734 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -11,6 +11,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; +import { CACHE_TTL_INFINITE } from '@ghostfolio/common/config'; import { DATE_FORMAT, getSum, @@ -882,7 +883,7 @@ export abstract class PortfolioCalculator { expiration: expiration.getTime(), portfolioSnapshot: snapshot })), - 0 + CACHE_TTL_INFINITE ); return snapshot; diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 4c5a60cce..26ef38c0a 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -1,5 +1,8 @@ import { Environment } from '@ghostfolio/api/services/interfaces/environment.interface'; -import { DEFAULT_ROOT_URL } from '@ghostfolio/common/config'; +import { + CACHE_TTL_NO_CACHE, + DEFAULT_ROOT_URL +} from '@ghostfolio/common/config'; import { Injectable } from '@nestjs/common'; import { DataSource } from '@prisma/client'; @@ -22,7 +25,7 @@ export class ConfigurationService { API_KEY_OPEN_FIGI: str({ default: '' }), API_KEY_RAPID_API: str({ default: '' }), CACHE_QUOTES_TTL: num({ default: ms('1 minute') }), - CACHE_TTL: num({ default: 1 }), + CACHE_TTL: num({ default: CACHE_TTL_NO_CACHE }), DATA_SOURCE_EXCHANGE_RATES: str({ default: DataSource.YAHOO }), DATA_SOURCE_IMPORT: str({ default: DataSource.YAHOO }), DATA_SOURCES: json({ diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 50c02ae20..00e756810 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -30,6 +30,9 @@ export const warnColorRgb = { b: 69 }; +export const CACHE_TTL_NO_CACHE = 1; +export const CACHE_TTL_INFINITE = 0; + export const DATA_GATHERING_QUEUE = 'DATA_GATHERING_QUEUE'; export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1; export const DATA_GATHERING_QUEUE_PRIORITY_LOW = Number.MAX_SAFE_INTEGER; From 1204240ed09e4283ecef3becd40adb01fa7df577 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:29:53 +0200 Subject: [PATCH 09/16] Bugfix/fix exception in admin market data detail component (#3731) * Add check for dateOfFirstActivity * Update changelog --- CHANGELOG.md | 1 + .../admin-market-data-detail.component.ts | 76 +++++++++---------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 448e1c5a6..a1bb6525f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental) - Fixed an issue on the portfolio activities page by loading the data only once - Fixed an issue in the carousel component for the testimonial section on the landing page +- Handled an exception in the historical market data component of the asset profile details dialog in the admin control panel ## 2.105.0 - 2024-08-21 diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts b/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts index 7e7168a6e..a1261c37f 100644 --- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts +++ b/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts @@ -93,52 +93,52 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit { }; }); - let date = parseISO(this.dateOfFirstActivity); - - const missingMarketData: Partial[] = []; - - if (this.historicalDataItems?.[0]?.date) { - while ( - isBefore( - date, - parse(this.historicalDataItems[0].date, DATE_FORMAT, new Date()) - ) - ) { - missingMarketData.push({ - date, - marketPrice: undefined - }); - - date = addDays(date, 1); + if (this.dateOfFirstActivity) { + let date = parseISO(this.dateOfFirstActivity); + + const missingMarketData: Partial[] = []; + + if (this.historicalDataItems?.[0]?.date) { + while ( + isBefore( + date, + parse(this.historicalDataItems[0].date, DATE_FORMAT, new Date()) + ) + ) { + missingMarketData.push({ + date, + marketPrice: undefined + }); + + date = addDays(date, 1); + } } - } - const marketDataItems = [...missingMarketData, ...this.marketData]; + const marketDataItems = [...missingMarketData, ...this.marketData]; - if (!isToday(last(marketDataItems)?.date)) { - marketDataItems.push({ date: new Date() }); - } + if (!isToday(last(marketDataItems)?.date)) { + marketDataItems.push({ date: new Date() }); + } - this.marketDataByMonth = {}; + this.marketDataByMonth = {}; - for (const marketDataItem of marketDataItems) { - const currentDay = parseInt(format(marketDataItem.date, 'd'), 10); - const key = format(marketDataItem.date, 'yyyy-MM'); + for (const marketDataItem of marketDataItems) { + const currentDay = parseInt(format(marketDataItem.date, 'd'), 10); + const key = format(marketDataItem.date, 'yyyy-MM'); - if (!this.marketDataByMonth[key]) { - this.marketDataByMonth[key] = {}; - } + if (!this.marketDataByMonth[key]) { + this.marketDataByMonth[key] = {}; + } - this.marketDataByMonth[key][ - currentDay < 10 ? `0${currentDay}` : currentDay - ] = { - date: marketDataItem.date, - day: currentDay, - marketPrice: marketDataItem.marketPrice - }; - } + this.marketDataByMonth[key][ + currentDay < 10 ? `0${currentDay}` : currentDay + ] = { + date: marketDataItem.date, + day: currentDay, + marketPrice: marketDataItem.marketPrice + }; + } - if (this.dateOfFirstActivity) { // Fill up missing months const dates = Object.keys(this.marketDataByMonth).sort(); const startDate = min([ From 8c322b4e817075ff2510324aaa07ad8b53b8cd55 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:21:02 +0200 Subject: [PATCH 10/16] Bugfix/fix historical market data gathering in yahoo finance service (#3737) * Switch from historical() to chart() * Update changelog --- CHANGELOG.md | 1 + .../yahoo-finance/yahoo-finance.service.ts | 62 +++++++++++++------ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bb6525f..5f1102d92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental) - Fixed an issue on the portfolio activities page by loading the data only once - Fixed an issue in the carousel component for the testimonial section on the landing page +- Fixed the historical market data gathering in the _Yahoo Finance_ service by switching from `historical()` to `chart()` - Handled an exception in the historical market data component of the asset profile details dialog in the admin control panel ## 2.105.0 - 2024-08-21 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 e0d88f0c6..a8f7d261e 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 @@ -20,6 +20,11 @@ import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay } from 'date-fns'; import yahooFinance from 'yahoo-finance2'; +import { ChartResultArray } from 'yahoo-finance2/dist/esm/src/modules/chart'; +import { + HistoricalDividendsResult, + HistoricalHistoryResult +} from 'yahoo-finance2/dist/esm/src/modules/historical'; import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote'; @Injectable() @@ -60,18 +65,19 @@ export class YahooFinanceService implements DataProviderInterface { } try { - const historicalResult = await yahooFinance.historical( - this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol( - symbol - ), - { - events: 'dividends', - interval: granularity === 'month' ? '1mo' : '1d', - period1: format(from, DATE_FORMAT), - period2: format(to, DATE_FORMAT) - } + const historicalResult = this.convertToDividendResult( + await yahooFinance.chart( + this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol( + symbol + ), + { + events: 'dividends', + interval: granularity === 'month' ? '1mo' : '1d', + period1: format(from, DATE_FORMAT), + period2: format(to, DATE_FORMAT) + } + ) ); - const response: { [date: string]: IDataProviderHistoricalResponse; } = {}; @@ -108,15 +114,17 @@ export class YahooFinanceService implements DataProviderInterface { } try { - const historicalResult = await yahooFinance.historical( - this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol( - symbol - ), - { - interval: '1d', - period1: format(from, DATE_FORMAT), - period2: format(to, DATE_FORMAT) - } + const historicalResult = this.convertToHistoricalResult( + await yahooFinance.chart( + this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol( + symbol + ), + { + interval: '1d', + period1: format(from, DATE_FORMAT), + period2: format(to, DATE_FORMAT) + } + ) ); const response: { @@ -302,6 +310,20 @@ export class YahooFinanceService implements DataProviderInterface { return { items }; } + private convertToDividendResult( + result: ChartResultArray + ): HistoricalDividendsResult { + return result.events.dividends.map(({ amount: dividends, date }) => { + return { date, dividends }; + }); + } + + private convertToHistoricalResult( + result: ChartResultArray + ): HistoricalHistoryResult { + return result.quotes; + } + private async getQuotesWithQuoteSummary(aYahooFinanceSymbols: string[]) { const quoteSummaryPromises = aYahooFinanceSymbols.map((symbol) => { return yahooFinance.quoteSummary(symbol).catch(() => { From 1bc2b474527ff625db443f2ddb5f56af4b25aeb2 Mon Sep 17 00:00:00 2001 From: Shaunak Das <51281688+shaun-ak@users.noreply.github.com> Date: Sun, 8 Sep 2024 00:52:56 +0530 Subject: [PATCH 11/16] Feature/setup skeleton loader for data tables (#3735) * Setup skeleton loader for data tables * Update changelog --- CHANGELOG.md | 2 ++ .../app/components/admin-jobs/admin-jobs.component.ts | 5 +++++ .../src/app/components/admin-jobs/admin-jobs.html | 10 ++++++++++ .../src/app/components/admin-jobs/admin-jobs.module.ts | 2 ++ .../components/admin-users/admin-users.component.ts | 5 +++++ .../src/app/components/admin-users/admin-users.html | 10 ++++++++++ .../app/components/admin-users/admin-users.module.ts | 4 +++- 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1102d92..56ac6210d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Set up a performance logging service +- Added a loading indicator to the queue jobs table in the admin control panel +- Added a loading indicator to the users table in the admin control panel - Added the attribute `mode` to the scraper configuration to get quotes instantly ### Changed diff --git a/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts b/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts index 820b3651d..e828049bc 100644 --- a/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts +++ b/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts @@ -51,6 +51,7 @@ export class AdminJobsComponent implements OnDestroy, OnInit { 'status', 'actions' ]; + public isLoading = false; public statusFilterOptions = QUEUE_JOB_STATUS_LIST; public user: User; @@ -138,12 +139,16 @@ export class AdminJobsComponent implements OnDestroy, OnInit { } private fetchJobs(aStatus?: JobStatus[]) { + this.isLoading = true; + this.adminService .fetchJobs({ status: aStatus }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ jobs }) => { this.dataSource = new MatTableDataSource(jobs); + this.isLoading = false; + this.changeDetectorRef.markForCheck(); }); } diff --git a/apps/client/src/app/components/admin-jobs/admin-jobs.html b/apps/client/src/app/components/admin-jobs/admin-jobs.html index 9ea2097e2..e194b2b37 100644 --- a/apps/client/src/app/components/admin-jobs/admin-jobs.html +++ b/apps/client/src/app/components/admin-jobs/admin-jobs.html @@ -183,6 +183,16 @@ + @if (isLoading) { + + }
diff --git a/apps/client/src/app/components/admin-jobs/admin-jobs.module.ts b/apps/client/src/app/components/admin-jobs/admin-jobs.module.ts index fe717b904..cca66a04a 100644 --- a/apps/client/src/app/components/admin-jobs/admin-jobs.module.ts +++ b/apps/client/src/app/components/admin-jobs/admin-jobs.module.ts @@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatMenuModule } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { MatTableModule } from '@angular/material/table'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { AdminJobsComponent } from './admin-jobs.component'; @@ -17,6 +18,7 @@ import { AdminJobsComponent } from './admin-jobs.component'; MatMenuModule, MatSelectModule, MatTableModule, + NgxSkeletonLoaderModule, ReactiveFormsModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] 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 0a66977bf..4a419dd6c 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 @@ -32,6 +32,7 @@ export class AdminUsersComponent implements OnDestroy, OnInit { public hasPermissionForSubscription: boolean; public hasPermissionToImpersonateAllUsers: boolean; public info: InfoItem; + public isLoading = false; public user: User; private unsubscribeSubject = new Subject(); @@ -142,12 +143,16 @@ export class AdminUsersComponent implements OnDestroy, OnInit { } private fetchAdminData() { + this.isLoading = true; + this.adminService .fetchAdminData() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ users }) => { this.dataSource = new MatTableDataSource(users); + this.isLoading = false; + this.changeDetectorRef.markForCheck(); }); } 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 25ab9053d..b65b7c821 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -245,6 +245,16 @@ >
+ @if (isLoading) { + + }
diff --git a/apps/client/src/app/components/admin-users/admin-users.module.ts b/apps/client/src/app/components/admin-users/admin-users.module.ts index 3f4e9f2f7..fcf25c8b5 100644 --- a/apps/client/src/app/components/admin-users/admin-users.module.ts +++ b/apps/client/src/app/components/admin-users/admin-users.module.ts @@ -6,6 +6,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatMenuModule } from '@angular/material/menu'; import { MatTableModule } from '@angular/material/table'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { AdminUsersComponent } from './admin-users.component'; @@ -18,7 +19,8 @@ import { AdminUsersComponent } from './admin-users.component'; GfValueComponent, MatButtonModule, MatMenuModule, - MatTableModule + MatTableModule, + NgxSkeletonLoaderModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) From 728f84e7eb960a81417940ed4bfd6f4034f4a699 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:24:33 +0200 Subject: [PATCH 12/16] Release 2.106.0 (#3738) --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56ac6210d..f91b3b62e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 2.106.0-beta.6 +## 2.106.0 - 2024-09-07 ### Added diff --git a/package.json b/package.json index 79ed8f8c1..81eb3859f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.106.0-beta.6", + "version": "2.106.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 9cd4321bd02c2f3cb6eaf7010c020882937a0d84 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 8 Sep 2024 09:56:08 +0200 Subject: [PATCH 13/16] Feature/extract users from admin control panel endpoint to dedicated endpoint (#3740) * Introduce GET api/v1/admin/user endpoint * Update changelog --- CHANGELOG.md | 6 ++++++ apps/api/src/app/admin/admin.controller.ts | 8 ++++++++ apps/api/src/app/admin/admin.service.ts | 10 +++++++--- .../admin-users/admin-users.component.ts | 12 ++++++------ apps/client/src/app/services/admin.service.ts | 5 +++++ .../src/lib/interfaces/admin-data.interface.ts | 12 ------------ .../src/lib/interfaces/admin-users.interface.ts | 14 ++++++++++++++ libs/common/src/lib/interfaces/index.ts | 2 ++ 8 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 libs/common/src/lib/interfaces/admin-users.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f91b3b62e..eae074550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Extracted the users from the admin control panel endpoint to a dedicated endpoint + ## 2.106.0 - 2024-09-07 ### Added diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 6d201be23..da4b5dd7e 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -17,6 +17,7 @@ import { AdminData, AdminMarketData, AdminMarketDataDetails, + AdminUsers, EnhancedSymbolProfile } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; @@ -347,4 +348,11 @@ export class AdminController { ) { return this.adminService.putSetting(key, data.value); } + + @Get('user') + @HasPermission(permissions.accessAdminControl) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getUsers(): Promise { + return this.adminService.getUsers(); + } } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 50b781f54..3f5274285 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -21,6 +21,7 @@ import { AdminMarketData, AdminMarketDataDetails, AdminMarketDataItem, + AdminUsers, AssetProfileIdentifier, EnhancedSymbolProfile, Filter @@ -135,7 +136,6 @@ export class AdminService { settings: await this.propertyService.get(), transactionCount: await this.prismaService.order.count(), userCount: await this.prismaService.user.count(), - users: await this.getUsersWithAnalytics(), version: environment.version }; } @@ -377,6 +377,10 @@ export class AdminService { }; } + public async getUsers(): Promise { + return { users: await this.getUsersWithAnalytics() }; + } + public async patchAssetProfileData({ assetClass, assetSubClass, @@ -546,11 +550,11 @@ export class AdminService { return { marketData, count: marketData.length }; } - private async getUsersWithAnalytics(): Promise { + private async getUsersWithAnalytics(): Promise { let orderBy: any = { createdAt: 'desc' }; - let where; + let where: Prisma.UserWhereInput; if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { orderBy = { 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 4a419dd6c..c5264c3b3 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 @@ -5,7 +5,7 @@ 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 { getDateFormatString, getEmojiFlag } from '@ghostfolio/common/helper'; -import { AdminData, InfoItem, User } from '@ghostfolio/common/interfaces'; +import { AdminUsers, InfoItem, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; @@ -24,7 +24,7 @@ import { takeUntil } from 'rxjs/operators'; templateUrl: './admin-users.html' }) export class AdminUsersComponent implements OnDestroy, OnInit { - public dataSource: MatTableDataSource = + public dataSource: MatTableDataSource = new MatTableDataSource(); public defaultDateFormat: string; public displayedColumns: string[] = []; @@ -94,7 +94,7 @@ export class AdminUsersComponent implements OnDestroy, OnInit { } public ngOnInit() { - this.fetchAdminData(); + this.fetchUsers(); } public formatDistanceToNow(aDateString: string) { @@ -119,7 +119,7 @@ export class AdminUsersComponent implements OnDestroy, OnInit { .deleteUser(aId) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { - this.fetchAdminData(); + this.fetchUsers(); }); }, confirmType: ConfirmationDialogType.Warn, @@ -142,11 +142,11 @@ export class AdminUsersComponent implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } - private fetchAdminData() { + private fetchUsers() { this.isLoading = true; this.adminService - .fetchAdminData() + .fetchUsers() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ users }) => { this.dataSource = new MatTableDataSource(users); diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index e5ea176d1..4c011e8c1 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -12,6 +12,7 @@ import { AdminJobs, AdminMarketData, AdminMarketDataDetails, + AdminUsers, EnhancedSymbolProfile, Filter } from '@ghostfolio/common/interfaces'; @@ -155,6 +156,10 @@ export class AdminService { return this.http.get('/api/v1/tag'); } + public fetchUsers() { + return this.http.get('/api/v1/admin/user'); + } + public gather7Days() { return this.http.post('/api/v1/admin/gather', {}); } diff --git a/libs/common/src/lib/interfaces/admin-data.interface.ts b/libs/common/src/lib/interfaces/admin-data.interface.ts index 6b139026b..3dc476df8 100644 --- a/libs/common/src/lib/interfaces/admin-data.interface.ts +++ b/libs/common/src/lib/interfaces/admin-data.interface.ts @@ -1,7 +1,5 @@ import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; -import { Role } from '@prisma/client'; - export interface AdminData { exchangeRates: ({ label1: string; @@ -11,15 +9,5 @@ export interface AdminData { settings: { [key: string]: boolean | object | string | string[] }; transactionCount: number; userCount: number; - users: { - accountCount: number; - country: string; - createdAt: Date; - engagement: number; - id: string; - lastActivity: Date; - role: Role; - transactionCount: number; - }[]; version: string; } diff --git a/libs/common/src/lib/interfaces/admin-users.interface.ts b/libs/common/src/lib/interfaces/admin-users.interface.ts new file mode 100644 index 000000000..24eb45c85 --- /dev/null +++ b/libs/common/src/lib/interfaces/admin-users.interface.ts @@ -0,0 +1,14 @@ +import { Role } from '@prisma/client'; + +export interface AdminUsers { + users: { + accountCount: number; + country: string; + createdAt: Date; + engagement: number; + id: string; + lastActivity: Date; + role: Role; + transactionCount: number; + }[]; +} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index f7224407b..efab780fd 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -7,6 +7,7 @@ import type { AdminMarketData, AdminMarketDataItem } from './admin-market-data.interface'; +import type { AdminUsers } from './admin-users.interface'; import type { AssetProfileIdentifier } from './asset-profile-identifier.interface'; import type { BenchmarkMarketDataDetails } from './benchmark-market-data-details.interface'; import type { BenchmarkProperty } from './benchmark-property.interface'; @@ -61,6 +62,7 @@ export { AdminMarketData, AdminMarketDataDetails, AdminMarketDataItem, + AdminUsers, AssetProfileIdentifier, Benchmark, BenchmarkMarketDataDetails, From bdb3a8f1dc5f89ebda2f081320a2033c774a0ded Mon Sep 17 00:00:00 2001 From: Andrea Date: Sun, 8 Sep 2024 21:41:41 +0200 Subject: [PATCH 14/16] Feature/improve language localization for Italian (#3744) * Update translations * Update changelog --- CHANGELOG.md | 1 + apps/client/src/locales/messages.it.xlf | 326 ++++++++++++------------ 2 files changed, 164 insertions(+), 163 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae074550..510ccddf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Extracted the users from the admin control panel endpoint to a dedicated endpoint +- Improved the language localization for Italian (`it`) ## 2.106.0 - 2024-09-07 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index e8fb9e562..592df3152 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -404,7 +404,7 @@ Asset Profiles - Profilo dell’asset + Profilo dell’asset libs/ui/src/lib/assistant/assistant.html 67 @@ -412,7 +412,7 @@ Historical Market Data - Dati storici del mercato + Dati storici del mercato apps/client/src/app/components/admin-jobs/admin-jobs.html 37 @@ -796,7 +796,7 @@ Last Request - Ultima richiesta + Ultima richiesta apps/client/src/app/components/admin-users/admin-users.html 181 @@ -1768,7 +1768,7 @@ Sign in with fingerprint - Accesso con impronta digitale + Accesso con impronta digitale apps/client/src/app/components/user-account-settings/user-account-settings.html 191 @@ -3460,7 +3460,7 @@ Protection for sensitive information like absolute performances and quantity values - Protezione delle informazioni sensibili come le prestazioni assolute e i valori quantitativi + Protezione delle informazioni sensibili come le prestazioni assolute e i valori quantitativi apps/client/src/app/components/user-account-settings/user-account-settings.html 8 @@ -3468,7 +3468,7 @@ Distraction-free experience for turbulent times - Esperienza priva di distrazioni per i periodi più turbolenti + Esperienza priva di distrazioni per i periodi più turbolenti apps/client/src/app/components/user-account-settings/user-account-settings.html 174 @@ -3476,7 +3476,7 @@ Sneak peek at upcoming functionality - Un’anteprima delle funzionalità in arrivo + Un’anteprima delle funzionalità in arrivo apps/client/src/app/components/user-account-settings/user-account-settings.html 208 @@ -3660,7 +3660,7 @@ For new investors who are just getting started with trading. - Per i nuovi investitori che hanno appena iniziato a fare trading. + Per i nuovi investitori che hanno appena iniziato a fare trading. apps/client/src/app/pages/pricing/pricing-page.html 123 @@ -3688,7 +3688,7 @@ One-time payment, no auto-renewal. - Pagamento una tantum, senza rinnovo automatico. + Pagamento una tantum, senza rinnovo automatico. apps/client/src/app/pages/pricing/pricing-page.html 280 @@ -3864,7 +3864,7 @@ Renew Plan - Rinnova il piano + Rinnova il piano apps/client/src/app/components/header/header.component.html 183 @@ -3880,7 +3880,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 the costs of the hosting infrastructure and to fund ongoing development. - La nostra offerta cloud ufficiale Ghostfolio Premium è il modo più semplice per iniziare. Grazie al risparmio di tempo, questa è l’opzione migliore per la maggior parte delle persone. I ricavi vengono utilizzati per coprire l’infrastruttura di hosting e per finanziare lo sviluppo continuo di Ghostfolio. + La nostra offerta cloud ufficiale Ghostfolio Premium è il modo più semplice per iniziare. Grazie al risparmio di tempo, questa è l’opzione migliore per la maggior parte delle persone. I ricavi vengono utilizzati per coprire l’infrastruttura di hosting e per finanziare lo sviluppo continuo di Ghostfolio. apps/client/src/app/pages/pricing/pricing-page.html 6 @@ -3904,7 +3904,7 @@ Do you really want to delete these activities? - Vuoi davvero eliminare tutte le tue attività? + Vuoi davvero eliminare tutte le tue attività? libs/ui/src/lib/activities-table/activities-table.component.ts 225 @@ -4744,7 +4744,7 @@ New Users - Nuovi utenti + Nuovi utenti apps/client/src/app/pages/open/open-page.html 51 @@ -5132,7 +5132,7 @@ Are you ready? - Seipronto? + Sei pronto? apps/client/src/app/pages/landing/landing-page.html 431 @@ -5584,7 +5584,7 @@ Open Source Alternative to - L’alternativa open source a + L’alternativa open source a apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.html 42 @@ -5624,7 +5624,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. + Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 38 @@ -5644,7 +5644,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. + Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 226 @@ -5720,7 +5720,7 @@ Choose or drop a file here - Choose or drop a file here + Seleziona o trascina qui un file apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html 84 @@ -5728,7 +5728,7 @@ You are using the Live Demo. - You are using the Live Demo. + Stai usando la Live Demo. apps/client/src/app/app.component.html 12 @@ -5736,7 +5736,7 @@ One-time fee, annual account fees - One-time fee, annual account fees + Commissione una tantum, commissioni annuali per l'account apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 33 @@ -5744,7 +5744,7 @@ Distribution of corporate earnings - Distribution of corporate earnings + A distribuzione dei dividendi apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 41 @@ -5752,7 +5752,7 @@ Oops! Could not get the historical exchange rate from - Oops! Could not get the historical exchange rate from + Ops! Impossibile ottenere il tasso di cambio storico da apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 318 @@ -5760,7 +5760,7 @@ Fee - Fee + Commissione libs/ui/src/lib/i18n.ts 36 @@ -5768,7 +5768,7 @@ Interest - Interest + Interesse apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html 331 @@ -5776,7 +5776,7 @@ Revenue for lending out money - Revenue for lending out money + Ricavi dal prestito di soldi apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html 49 @@ -5784,7 +5784,7 @@ Add Tag - Add Tag + Aggiungi un Tag apps/client/src/app/components/admin-tag/admin-tag.component.html 11 @@ -5792,7 +5792,7 @@ Do you really want to delete this tag? - Do you really want to delete this tag? + Sei sicuro di voler eliminare questo tag? apps/client/src/app/components/admin-tag/admin-tag.component.ts 86 @@ -5800,7 +5800,7 @@ Update tag - Update tag + Aggiorna il tag apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html 8 @@ -5808,7 +5808,7 @@ Add tag - Add tag + Aggiungi un tag apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html 10 @@ -5816,7 +5816,7 @@ Ghostfolio X-ray uses static analysis to identify potential issues and risks in your portfolio. - Ghostfolio X-ray uses static analysis to identify potential issues and risks in your portfolio. + Ghostfolio X-ray usa l'analisi statica per identificare potenziali problemi e rischi del tuo portafoglio. apps/client/src/app/pages/portfolio/fire/fire-page.html 111 @@ -5840,7 +5840,7 @@ Transfer Cash Balance - Transfer Cash Balance + Trasferisci il saldo di cassa apps/client/src/app/components/accounts-table/accounts-table.component.html 10 @@ -5852,7 +5852,7 @@ Benchmark - Benchmark + Benchmark apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html 286 @@ -5860,7 +5860,7 @@ Version - Version + Versione apps/client/src/app/components/admin-overview/admin-overview.html 7 @@ -5868,7 +5868,7 @@ Settings - Settings + Impostazioni apps/client/src/app/components/user-account-settings/user-account-settings.html 2 @@ -5876,7 +5876,7 @@ From - From + Da apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html 11 @@ -5884,7 +5884,7 @@ To - To + A apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html 32 @@ -5892,7 +5892,7 @@ Transfer - Transfer + Trasferisci apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html 72 @@ -5900,7 +5900,7 @@ Membership - Membership + Iscrizione apps/client/src/app/pages/user-account/user-account-page-routing.module.ts 23 @@ -5912,7 +5912,7 @@ Access - Access + Accesso apps/client/src/app/pages/user-account/user-account-page-routing.module.ts 28 @@ -5924,7 +5924,7 @@ Find holding... - Find holding... + Trova possedimenti... libs/ui/src/lib/assistant/assistant.component.ts 138 @@ -5932,7 +5932,7 @@ No entries... - No entries... + Nessun risultato... libs/ui/src/lib/assistant/assistant.html 63 @@ -5944,7 +5944,7 @@ Asset Profile - Asset Profile + Profilo dell'asset apps/client/src/app/components/admin-jobs/admin-jobs.html 35 @@ -5952,7 +5952,7 @@ Do you really want to delete this asset profile? - Do you really want to delete this asset profile? + Vuoi veramente eliminare il profilo di questo asset? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts 33 @@ -5960,7 +5960,7 @@ Search - Search + Cerca apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 16 @@ -5968,7 +5968,7 @@ Add Manually - Add Manually + Aggiungi manualmente apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 19 @@ -5976,7 +5976,7 @@ Ghostfolio is a personal finance dashboard to keep track of your net worth including cash, stocks, ETFs and cryptocurrencies across multiple platforms. - Ghostfolio è un dashboard di finanza personale per tenere traccia delle vostre attività come azioni, ETF o criptovalute su più piattaforme. + Ghostfolio è un dashboard di finanza personale per tenere traccia delle vostre attività come azioni, ETF o criptovalute su più piattaforme. apps/client/src/app/pages/i18n/i18n-page.html 4 @@ -5984,7 +5984,7 @@ Last All Time High - Last All Time High + Ultimo massimo storico libs/ui/src/lib/benchmark/benchmark.component.html 65 @@ -5992,7 +5992,7 @@ User - User + Utente apps/client/src/app/components/admin-users/admin-users.html 29 @@ -6008,7 +6008,7 @@ Open Source Wealth Management Software - Open Source Wealth Management Software + Software Open Source per la gestione della tua ricchezza apps/client/src/app/pages/i18n/i18n-page.html 14 @@ -6016,7 +6016,7 @@ app, asset, cryptocurrency, dashboard, etf, finance, management, performance, portfolio, software, stock, trading, wealth, web3 - app, asset, cryptocurrency, dashboard, etf, finance, management, performance, portfolio, software, stock, trading, wealth, web3 + app, asset, crypto, dashboard, etf, finanza, gestione patrimoniale, rendimenti, portafoglio, software, stock, azioni, titoli, obbligazioni, trading, ricchezza, web3 apps/client/src/app/pages/i18n/i18n-page.html 9 @@ -6024,7 +6024,7 @@ Oops, cash balance transfer has failed. - Oops, cash balance transfer has failed. + Ops, il trasferimento del saldo di cassa è fallito. apps/client/src/app/pages/accounts/accounts-page.component.ts 311 @@ -6032,7 +6032,7 @@ Extreme Fear - Extreme Fear + Paura estrema libs/ui/src/lib/i18n.ts 94 @@ -6040,7 +6040,7 @@ Extreme Greed - Extreme Greed + Avidità estrema libs/ui/src/lib/i18n.ts 95 @@ -6048,7 +6048,7 @@ Neutral - Neutral + Neutrale libs/ui/src/lib/i18n.ts 98 @@ -6056,7 +6056,7 @@ Oops! Could not parse historical data. - Oops! Could not parse historical data. + Ops! Impossibile elaborare i dati storici. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 237 @@ -6064,7 +6064,7 @@ Do you really want to delete this system message? - Do you really want to delete this system message? + Confermi di voler cancellare questo messaggio di sistema? apps/client/src/app/components/admin-overview/admin-overview.component.ts 178 @@ -6072,7 +6072,7 @@ 50-Day Trend - 50-Day Trend + Trend a 50 giorni libs/ui/src/lib/benchmark/benchmark.component.html 15 @@ -6080,7 +6080,7 @@ 200-Day Trend - 200-Day Trend + Trend a 200 giorni libs/ui/src/lib/benchmark/benchmark.component.html 40 @@ -6088,7 +6088,7 @@ Cash Balances - Cash Balances + Saldi di cassa apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html 122 @@ -6096,7 +6096,7 @@ Starting from - Starting from + Inizia da apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 204 @@ -6108,7 +6108,7 @@ year - year + anno apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 205 @@ -6120,7 +6120,7 @@ Do you really want to delete this account balance? - Do you really want to delete this account balance? + Vuoi veramente elimnare il saldo di questo conto? libs/ui/src/lib/account-balances/account-balances.component.ts 110 @@ -6128,7 +6128,7 @@ is an invalid currency! - is an invalid currency! + non è una valuta valida! apps/client/src/app/components/admin-overview/admin-overview.component.ts 133 @@ -6136,7 +6136,7 @@ If a translation is missing, kindly support us in extending it here. - If a translation is missing, kindly support us in extending it here. + Se manca una traduzione, puoi aiutarci modificando questo file: here. apps/client/src/app/components/user-account-settings/user-account-settings.html 50 @@ -6144,7 +6144,7 @@ The current market price is - The current market price is + L'attuale prezzo di mercato è apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 345 @@ -6152,7 +6152,7 @@ Test - Test + Prova apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html 322 @@ -6160,7 +6160,7 @@ Date Range - Date Range + Intervallo di date libs/ui/src/lib/assistant/assistant.html 93 @@ -6168,7 +6168,7 @@ Permission - Permission + Permessi apps/client/src/app/components/access-table/access-table.component.html 18 @@ -6192,7 +6192,7 @@ Oops! Could not grant access. - Oops! Could not grant access. + 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 91 @@ -6200,7 +6200,7 @@ Private - Private + Privato apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html 24 @@ -6208,7 +6208,7 @@ Job Queue - Job Queue + Coda Lavori apps/client/src/app/pages/admin/admin-page-routing.module.ts 25 @@ -6220,7 +6220,7 @@ Market data is delayed for - Market data is delayed for + I dati di mercato sono ritardati di apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts 86 @@ -6228,7 +6228,7 @@ Investment - Investment + Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts 42 @@ -6244,7 +6244,7 @@ Absolute Asset Performance - Absolute Asset Performance + Rendimento assoluto dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html 29 @@ -6252,7 +6252,7 @@ Asset Performance - Asset Performance + Rendimento dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html 50 @@ -6260,7 +6260,7 @@ Absolute Currency Performance - Absolute Currency Performance + Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html 72 @@ -6268,7 +6268,7 @@ Currency Performance - Currency Performance + Rendimento della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html 96 @@ -6276,7 +6276,7 @@ Absolute Net Performance - Absolute Net Performance + Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html 119 @@ -6284,7 +6284,7 @@ Net Performance - Net Performance + Rendimento Netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html 138 @@ -6292,7 +6292,7 @@ Week to date - Week to date + Da inizio settimana libs/ui/src/lib/assistant/assistant.component.ts 217 @@ -6308,7 +6308,7 @@ Month to date - Month to date + Da inizio mese libs/ui/src/lib/assistant/assistant.component.ts 221 @@ -6324,7 +6324,7 @@ Year to date - Year to date + Da inizio anno libs/ui/src/lib/assistant/assistant.component.ts 225 @@ -6332,7 +6332,7 @@ View - View + Visualizza apps/client/src/app/components/access-table/access-table.component.html 23 @@ -6344,7 +6344,7 @@ Oops! A data provider is experiencing the hiccups. - Oops! A data provider is experiencing the hiccups. + Ops! Un data provider sta riscontrando dei problemi. apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html 8 @@ -6352,7 +6352,7 @@ If you retire today, you would be able to withdraw per year or per month, based on your total assets of and a withdrawal rate of 4%. - If you retire today, you would be able to withdraw per year or per month, based on your total assets of and a withdrawal rate of 4%. + Se andassi in pensione oggi, saresti in grado di prelevare all'anno o al mese, calcolato sul valore totale dei tuoi asset di e un prelievo costante del 4%. apps/client/src/app/pages/portfolio/fire/fire-page.html 67 @@ -6360,7 +6360,7 @@ Reset Filters - Reset Filters + Reset Filtri libs/ui/src/lib/assistant/assistant.html 155 @@ -6368,7 +6368,7 @@ year - year + anno libs/ui/src/lib/assistant/assistant.component.ts 229 @@ -6376,7 +6376,7 @@ years - years + anni libs/ui/src/lib/assistant/assistant.component.ts 250 @@ -6384,7 +6384,7 @@ Asset Classes - Asset Classes + classi degli Asset libs/ui/src/lib/assistant/assistant.html 138 @@ -6392,7 +6392,7 @@ Apply Filters - Apply Filters + Applica i Filtri libs/ui/src/lib/assistant/assistant.html 165 @@ -6400,7 +6400,7 @@ Data Gathering - Data Gathering + Raccolta Dati apps/client/src/app/components/admin-overview/admin-overview.html 136 @@ -6408,7 +6408,7 @@ General - General + Generale apps/client/src/app/pages/faq/faq-page.component.ts 36 @@ -6416,7 +6416,7 @@ Cloud - Cloud + Cloud apps/client/src/app/pages/faq/faq-page.component.ts 41 @@ -6428,7 +6428,7 @@ Self-Hosting - Self-Hosting + Self-Hosting apps/client/src/app/pages/faq/faq-page.component.ts 47 @@ -6440,7 +6440,7 @@ self-hosting - self-hosting + self-hosting apps/client/src/app/pages/faq/faq-page.component.ts 48 @@ -6448,7 +6448,7 @@ FAQ - FAQ + FAQ apps/client/src/app/pages/faq/saas/saas-page-routing.module.ts 13 @@ -6460,7 +6460,7 @@ Oops! It looks like you’re making too many requests. Please slow down a bit. - Oops! It looks like you’re making too many requests. Please slow down a bit. + Ops! Sembra tu stia facendo troppe richieste. Rallenta un po' per favore. apps/client/src/app/core/http-response.interceptor.ts 96 @@ -6468,7 +6468,7 @@ My Account - My Account + Il mio Account apps/client/src/app/pages/i18n/i18n-page.html 13 @@ -6476,7 +6476,7 @@ Active - Active + Attivo apps/client/src/app/components/home-holdings/home-holdings.component.ts 38 @@ -6484,7 +6484,7 @@ Closed - Closed + Chiuso apps/client/src/app/components/home-holdings/home-holdings.component.ts 39 @@ -6492,7 +6492,7 @@ Activity - Activity + Attività apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 223 @@ -6500,7 +6500,7 @@ Dividend Yield - Dividend Yield + Rendimento da Dividendi apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 191 @@ -6508,7 +6508,7 @@ Execute Job - Execute Job + Esegui il lavoro apps/client/src/app/components/admin-jobs/admin-jobs.html 174 @@ -6516,7 +6516,7 @@ Priority - Priority + Priorità apps/client/src/app/components/admin-jobs/admin-jobs.html 62 @@ -6524,7 +6524,7 @@ This action is not allowed. - This action is not allowed. + Questa azione non è permessa. apps/client/src/app/core/http-response.interceptor.ts 61 @@ -6532,7 +6532,7 @@ Liquidity - Liquidity + Liquidità libs/ui/src/lib/i18n.ts 47 @@ -6540,7 +6540,7 @@ Change with currency effect - Change with currency effect + Variazione con Effetto Valutario apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 53 @@ -6548,7 +6548,7 @@ Performance with currency effect - Performance with currency effect + Rendimento con effetto valutario apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 81 @@ -6564,7 +6564,7 @@ Buy and sell - Buy and sell + Compra e vendi libs/ui/src/lib/i18n.ts 8 @@ -6572,7 +6572,7 @@ Delete Activities - Delete Activities + Elimina le attività libs/ui/src/lib/activities-table/activities-table.component.html 66 @@ -6580,7 +6580,7 @@ Internationalization - Internationalization + Internazionalizzazione apps/client/src/app/app-routing.module.ts 79 @@ -6588,7 +6588,7 @@ Do you really want to close your Ghostfolio account? - Do you really want to close your Ghostfolio account? + Confermi di voler chiudere il tuo account Ghostfolio? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts 174 @@ -6596,7 +6596,7 @@ Danger Zone - Danger Zone + Zona di Pericolo apps/client/src/app/components/user-account-settings/user-account-settings.html 244 @@ -6604,7 +6604,7 @@ Close Account - Close Account + Chiudi l'account apps/client/src/app/components/user-account-settings/user-account-settings.html 279 @@ -6612,7 +6612,7 @@ By ETF Holding - By ETF Holding + Per ETF posseduti apps/client/src/app/pages/portfolio/allocations/allocations-page.html 333 @@ -6620,7 +6620,7 @@ Approximation based on the top holdings of each ETF - Approximation based on the top holdings of each ETF + Approssimato in base ai principali ETF posseduti apps/client/src/app/pages/portfolio/allocations/allocations-page.html 340 @@ -6628,7 +6628,7 @@ Join now or check out the example account - Join now or check out the example account + Registrati adesso o prova l'account demo apps/client/src/app/pages/landing/landing-page.html 434 @@ -6636,7 +6636,7 @@ Oops! There was an error setting up biometric authentication. - Oops! There was an error setting up biometric authentication. + Ops! C'è stato un errore impostando l'autenticazione biometrica. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts 308 @@ -6644,7 +6644,7 @@ Show more - Show more + Visualizza di più libs/ui/src/lib/top-holdings/top-holdings.component.html 81 @@ -6652,7 +6652,7 @@ Benchmarks - Benchmarks + Benchmarks apps/client/src/app/components/admin-market-data/admin-market-data.component.ts 80 @@ -6660,7 +6660,7 @@ Delete Profiles - Delete Profiles + Elimina i profili apps/client/src/app/components/admin-market-data/admin-market-data.html 190 @@ -6668,7 +6668,7 @@ Do you really want to delete these profiles? - Do you really want to delete these profiles? + Confermi di voler eliminare questi profili? apps/client/src/app/components/admin-market-data/admin-market-data.service.ts 65 @@ -6676,7 +6676,7 @@ Oops! Could not delete profiles. - Oops! Could not delete profiles. + Ops! Impossibile eliminare i profili. apps/client/src/app/components/admin-market-data/admin-market-data.service.ts 52 @@ -6684,7 +6684,7 @@ Table - Table + Tabella apps/client/src/app/components/home-holdings/home-holdings.html 17 @@ -6692,7 +6692,7 @@ Chart - Chart + Grafico apps/client/src/app/components/home-holdings/home-holdings.html 20 @@ -6700,7 +6700,7 @@ Would you like to refine your personal investment strategy? - Would you like to refine your personal investment strategy? + Vorresti perfezionare la tua strategia personale di investimento? apps/client/src/app/pages/public/public-page.html 155 @@ -6708,7 +6708,7 @@ Alternative - Alternative + Alternativa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 83 @@ -6716,7 +6716,7 @@ App - App + App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 84 @@ -6724,7 +6724,7 @@ Budgeting - Budgeting + Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 85 @@ -6732,7 +6732,7 @@ Community - Community + Comunità apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 86 @@ -6748,7 +6748,7 @@ Investor - Investor + Investitore apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 90 @@ -6756,7 +6756,7 @@ Open Source - Open Source + Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 91 @@ -6764,7 +6764,7 @@ Personal Finance - Personal Finance + Finanza Personale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 93 @@ -6772,7 +6772,7 @@ Privacy - Privacy + Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 94 @@ -6780,7 +6780,7 @@ Software - Software + Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 96 @@ -6788,7 +6788,7 @@ Tool - Tool + Strumento apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 97 @@ -6804,7 +6804,7 @@ Wealth - Wealth + Ricchezza apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 99 @@ -6812,7 +6812,7 @@ Wealth Management - Wealth Management + Gestione Patrimoniale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 100 @@ -6820,7 +6820,7 @@ Australia - Australia + Australia libs/ui/src/lib/i18n.ts 72 @@ -6828,7 +6828,7 @@ Austria - Austria + Austria libs/ui/src/lib/i18n.ts 73 @@ -6836,7 +6836,7 @@ Belgium - Belgium + Belgio libs/ui/src/lib/i18n.ts 74 @@ -6844,7 +6844,7 @@ Bulgaria - Bulgaria + Bulgaria libs/ui/src/lib/i18n.ts 75 @@ -6852,7 +6852,7 @@ Canada - Canada + Canada libs/ui/src/lib/i18n.ts 76 @@ -6860,7 +6860,7 @@ Czech Republic - Czech Republic + Repubblica Ceca libs/ui/src/lib/i18n.ts 77 @@ -6868,7 +6868,7 @@ Finland - Finland + Finlandia libs/ui/src/lib/i18n.ts 78 @@ -6876,7 +6876,7 @@ France - France + Francia libs/ui/src/lib/i18n.ts 79 @@ -6884,7 +6884,7 @@ Germany - Germany + Germania libs/ui/src/lib/i18n.ts 80 @@ -6892,7 +6892,7 @@ India - India + India libs/ui/src/lib/i18n.ts 81 @@ -6900,7 +6900,7 @@ Italy - Italy + Italia libs/ui/src/lib/i18n.ts 82 @@ -6908,7 +6908,7 @@ Netherlands - Netherlands + Olanda libs/ui/src/lib/i18n.ts 84 @@ -6916,7 +6916,7 @@ New Zealand - New Zealand + Nuova Zelanda libs/ui/src/lib/i18n.ts 85 @@ -6924,7 +6924,7 @@ Poland - Poland + Polonia libs/ui/src/lib/i18n.ts 86 @@ -6932,7 +6932,7 @@ Romania - Romania + Romania libs/ui/src/lib/i18n.ts 87 @@ -6940,7 +6940,7 @@ South Africa - South Africa + Sud Africa libs/ui/src/lib/i18n.ts 88 @@ -6948,7 +6948,7 @@ Thailand - Thailand + Tailandia libs/ui/src/lib/i18n.ts 90 @@ -6956,7 +6956,7 @@ United States - United States + Stati Uniti libs/ui/src/lib/i18n.ts 91 @@ -6964,7 +6964,7 @@ Error - Error + Errore apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 336 @@ -6972,7 +6972,7 @@ Deactivate - Deactivate + Disattiva apps/client/src/app/components/rule/rule.component.html 67 @@ -6980,7 +6980,7 @@ Activate - Activate + Attiva apps/client/src/app/components/rule/rule.component.html 69 @@ -6988,7 +6988,7 @@ Inactive - Inactive + Inattivo apps/client/src/app/pages/portfolio/fire/fire-page.html 194 @@ -6996,7 +6996,7 @@ Cancel - Cancel + Annulla libs/ui/src/lib/i18n.ts 9 @@ -7004,7 +7004,7 @@ Close - Close + Chiudi libs/ui/src/lib/i18n.ts 11 @@ -7012,7 +7012,7 @@ Yes - Yes + Si libs/ui/src/lib/i18n.ts 31 From 557a0bf8080870943f70dfda37f6ae5cb0666a4e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:20:55 +0200 Subject: [PATCH 15/16] Feature/optimize info endpoint using promise.all (#3742) * Optimize by using Promise.all() * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/info/info.service.ts | 33 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 510ccddf6..afc0dcc58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Optimized the asynchronous operations using `Promise.all()` in the info service - Extracted the users from the admin control panel endpoint to a dedicated endpoint - Improved the language localization for Italian (`it`) diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index de4a870d2..acd7b315b 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -54,9 +54,6 @@ export class InfoService { public async get(): Promise { const info: Partial = {}; let isReadOnlyMode: boolean; - const platforms = await this.platformService.getPlatforms({ - orderBy: { name: 'asc' } - }); const globalPermissions: string[] = []; @@ -100,22 +97,30 @@ export class InfoService { globalPermissions.push(permissions.enableSystemMessage); } - const isUserSignupEnabled = - await this.propertyService.isUserSignupEnabled(); + const [ + benchmarks, + demoAuthToken, + isUserSignupEnabled, + platforms, + statistics, + subscriptions, + tags + ] = await Promise.all([ + this.benchmarkService.getBenchmarkAssetProfiles(), + this.getDemoAuthToken(), + this.propertyService.isUserSignupEnabled(), + this.platformService.getPlatforms({ + orderBy: { name: 'asc' } + }), + this.getStatistics(), + this.getSubscriptions(), + this.tagService.get() + ]); if (isUserSignupEnabled) { globalPermissions.push(permissions.createUserAccount); } - const [benchmarks, demoAuthToken, statistics, subscriptions, tags] = - await Promise.all([ - this.benchmarkService.getBenchmarkAssetProfiles(), - this.getDemoAuthToken(), - this.getStatistics(), - this.getSubscriptions(), - this.tagService.get() - ]); - return { ...info, benchmarks, From 6db881b08f49aa323b5940c1dde082b7aef974c6 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:39:52 +0200 Subject: [PATCH 16/16] Feature/optimize admin control panel endpoint using promise.all (#3741) * Optimize by using Promise.all() * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/admin/admin.service.ts | 62 ++++++++++++++----------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc0dcc58..e4d92ad12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Optimized the asynchronous operations using `Promise.all()` in the info service +- Optimized the asynchronous operations using `Promise.all()` in the admin control panel endpoint - Extracted the users from the admin control panel endpoint to a dedicated endpoint - Improved the language localization for Italian (`it`) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 3f5274285..143d5c1ca 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -108,34 +108,42 @@ export class AdminService { } public async get(): Promise { - return { - exchangeRates: this.exchangeRateDataService - .getCurrencies() - .filter((currency) => { - return currency !== DEFAULT_CURRENCY; - }) - .map((currency) => { - const label1 = DEFAULT_CURRENCY; - const label2 = currency; + const exchangeRates = this.exchangeRateDataService + .getCurrencies() + .filter((currency) => { + return currency !== DEFAULT_CURRENCY; + }) + .map((currency) => { + const label1 = DEFAULT_CURRENCY; + const label2 = currency; - return { - label1, - label2, - dataSource: - DataSource[ - this.configurationService.get('DATA_SOURCE_EXCHANGE_RATES') - ], - symbol: `${label1}${label2}`, - value: this.exchangeRateDataService.toCurrency( - 1, - DEFAULT_CURRENCY, - currency - ) - }; - }), - settings: await this.propertyService.get(), - transactionCount: await this.prismaService.order.count(), - userCount: await this.prismaService.user.count(), + return { + label1, + label2, + dataSource: + DataSource[ + this.configurationService.get('DATA_SOURCE_EXCHANGE_RATES') + ], + symbol: `${label1}${label2}`, + value: this.exchangeRateDataService.toCurrency( + 1, + DEFAULT_CURRENCY, + currency + ) + }; + }); + + const [settings, transactionCount, userCount] = await Promise.all([ + this.propertyService.get(), + this.prismaService.order.count(), + this.prismaService.user.count() + ]); + + return { + exchangeRates, + settings, + transactionCount, + userCount, version: environment.version }; }