From 5c07992791deb5b29b0cea4fed2678b72693a7e3 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:33:45 +0200 Subject: [PATCH 01/12] Task/improve changelog entry (#5790) * Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd841de15..e783ae690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - 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) - Improved the language localization for German (`de`) ## 2.209.0 - 2025-10-18 From e6ebe7e50105ec7f29a844f504f134453bece71a Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:34:38 +0200 Subject: [PATCH 02/12] Task/harmonize wording in glossary (#5781) * Harmonize wording --- .../resources/glossary/resources-glossary.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html b/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html index b028734a7..b65054bba 100644 --- a/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html +++ b/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html @@ -105,8 +105,8 @@

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.
Date: Mon, 20 Oct 2025 19:13:31 +0200 Subject: [PATCH 03/12] Task/improve typings of getInfo() functionality (#5803) * Improve typings --- apps/api/src/app/info/info.controller.ts | 4 ++-- apps/client/src/main.ts | 4 ++-- libs/common/src/lib/interfaces/index.ts | 2 ++ .../src/lib/interfaces/responses/info-response.interface.ts | 3 +++ 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 libs/common/src/lib/interfaces/responses/info-response.interface.ts 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/client/src/main.ts b/apps/client/src/main.ts index 2f656ddf2..96d6c0582 100644 --- a/apps/client/src/main.ts +++ b/apps/client/src/main.ts @@ -1,5 +1,5 @@ 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'; @@ -11,7 +11,7 @@ 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'; diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 578d9cec8..b69891e31 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -50,6 +50,7 @@ import type { DividendsResponse } from './responses/dividends-response.interface import type { ResponseError } from './responses/errors.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'; @@ -112,6 +113,7 @@ export { HoldingWithParents, ImportResponse, InfoItem, + InfoResponse, InvestmentItem, LineChartItem, LookupItem, 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 {} From be0ddd6298f9993f6a3ea0486f374a1b84b0c2f5 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:17:47 +0200 Subject: [PATCH 04/12] Task/harmonize interfaces naming (#5796) * Harmonize interfaces naming --- .../ghostfolio/ghostfolio.service.ts | 6 +-- .../exchange-rate/exchange-rate.controller.ts | 4 +- .../calculator/portfolio-calculator.ts | 4 +- .../interfaces/get-values-params.interface.ts | 4 +- apps/api/src/app/symbol/symbol.controller.ts | 4 +- apps/api/src/app/symbol/symbol.service.ts | 10 ++--- .../alpha-vantage/alpha-vantage.service.ts | 14 +++---- .../alpha-vantage/interfaces/interfaces.ts | 2 +- .../coingecko/coingecko.service.ts | 12 +++--- .../data-provider/data-provider.service.ts | 24 +++++------ .../eod-historical-data.service.ts | 14 +++---- .../financial-modeling-prep.service.ts | 14 +++---- .../ghostfolio/ghostfolio.service.ts | 14 +++---- .../google-sheets/google-sheets.service.ts | 12 +++--- .../interfaces/data-provider.interface.ts | 10 ++--- .../data-provider/manual/manual.service.ts | 12 +++--- .../rapid-api/interfaces/interfaces.ts | 2 +- .../rapid-api/rapid-api.service.ts | 8 ++-- .../yahoo-finance/yahoo-finance.service.ts | 14 +++---- .../exchange-rate-data.service.ts | 4 +- .../api/src/services/interfaces/interfaces.ts | 6 +-- .../market-data/market-data.service.ts | 4 +- .../data-gathering.processor.ts | 4 +- .../data-gathering/data-gathering.service.ts | 12 +++--- .../portfolio-snapshot-queue-job.interface.ts | 2 +- .../portfolio-snapshot.processor.ts | 6 +-- .../portfolio-snapshot.service.mock.ts | 4 +- .../portfolio-snapshot.service.ts | 4 +- .../interfaces/interfaces.ts | 2 +- .../rule-settings-dialog.component.ts | 4 +- .../src/app/components/rule/rule.component.ts | 4 +- .../alert-dialog/alert-dialog.component.ts | 4 +- .../alert-dialog/interfaces/interfaces.ts | 2 +- .../confirmation-dialog.component.ts | 4 +- .../interfaces/interfaces.ts | 2 +- .../notification/interfaces/interfaces.ts | 6 +-- .../core/notification/notification.service.ts | 12 +++--- apps/client/src/app/services/admin.service.ts | 4 +- apps/client/src/app/services/data.service.ts | 4 +- .../responses/dividends-response.interface.ts | 4 +- .../historical-response.interface.ts | 4 +- .../responses/quotes-response.interface.ts | 4 +- .../assistant-list-item.component.ts | 8 ++-- .../src/lib/assistant/assistant.component.ts | 40 +++++++++---------- .../lib/assistant/interfaces/interfaces.ts | 26 ++++++------ 45 files changed, 181 insertions(+), 183 deletions(-) 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..1094858cb 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,7 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { DataProviderHistoricalResponse } 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 { @@ -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/exchange-rate/exchange-rate.controller.ts b/apps/api/src/app/exchange-rate/exchange-rate.controller.ts index a5b2823d5..fc9e61d61 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/api/services/interfaces/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/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 8a8606003..3218d01f4 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -9,7 +9,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 { @@ -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); 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/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index 5d9a49a29..b374a914b 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/apps/api/src/app/symbol/symbol.controller.ts @@ -1,7 +1,7 @@ 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 { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { LookupResponse } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -97,7 +97,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..9eac234c9 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -1,7 +1,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { - IDataGatheringItem, - IDataProviderHistoricalResponse + DataGatheringItem, + DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; @@ -27,7 +27,7 @@ export class SymbolService { dataGatheringItem, includeHistoricalData }: { - dataGatheringItem: IDataGatheringItem; + dataGatheringItem: DataGatheringItem; includeHistoricalData?: number; }): Promise { const quotes = await this.dataProviderService.getQuotes({ @@ -75,10 +75,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/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 1e8f7eefa..1e631f8c8 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 @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; @@ -23,7 +23,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 +68,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 +81,7 @@ export class AlphaVantageService implements DataProviderInterface { ); const response: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; response[symbol] = {}; @@ -115,7 +115,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..e06cb6ab3 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; @@ -109,7 +109,7 @@ export class CoinGeckoService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { const { error, prices, status } = await fetch( @@ -133,7 +133,7 @@ export class CoinGeckoService implements DataProviderInterface { } const result: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = { [symbol]: {} }; @@ -166,8 +166,8 @@ 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; 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..53ef5c5e4 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -2,8 +2,8 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.s import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } 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'; @@ -215,10 +215,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 +284,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 +317,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 +329,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 +399,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 +716,7 @@ export class DataProviderService implements OnModuleInit { }: { allData: { data: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; symbol: string; }[]; @@ -728,7 +728,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..b837b2e6f 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 @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { @@ -89,7 +89,7 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }> { symbol = this.convertToEodSymbol(symbol); @@ -99,7 +99,7 @@ export class EodHistoricalDataService implements DataProviderInterface { try { const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; const historicalResult = await fetch( @@ -141,7 +141,7 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { symbol = this.convertToEodSymbol(symbol); @@ -198,8 +198,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; 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..0caad99ca 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 @@ -9,8 +9,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { @@ -245,7 +245,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { try { const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; const dividends = await fetch( @@ -289,11 +289,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]: {} }; @@ -353,8 +353,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; 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..9928af8eb 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -9,8 +9,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { @@ -111,10 +111,10 @@ export class GhostfolioService implements DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }> { let dividends: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; try { @@ -164,7 +164,7 @@ export class GhostfolioService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { const response = await fetch( @@ -228,9 +228,9 @@ 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; 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..fc188c345 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 @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } 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'; @@ -60,7 +60,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 +71,7 @@ export class GoogleSheetsService implements DataProviderInterface { const rows = await sheet.getRows(); const historicalData: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; rows @@ -104,8 +104,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..38eb62a2b 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,6 +1,6 @@ import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DataProviderInfo, @@ -26,7 +26,7 @@ export interface DataProviderInterface { symbol, to }: GetDividendsParams): Promise<{ - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }>; getHistorical({ @@ -36,7 +36,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 +46,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..00c28d9d2 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } 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'; @@ -77,7 +77,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 +88,7 @@ export class ManualService implements DataProviderInterface { if (defaultMarketPrice) { const historical: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = { [symbol]: {} }; @@ -132,8 +132,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..4d22e0feb 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 @@ -8,8 +8,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { ghostfolioFearAndGreedIndexSymbol, @@ -59,7 +59,7 @@ export class RapidApiService implements DataProviderInterface { symbol, to }: GetHistoricalParams): Promise<{ - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { if ( @@ -96,7 +96,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 390449d78..b36b0f215 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 @@ -10,8 +10,8 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { - IDataProviderHistoricalResponse, - IDataProviderResponse + DataProviderHistoricalResponse, + DataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; @@ -96,7 +96,7 @@ export class YahooFinanceService implements DataProviderInterface { ) ); const response: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; } = {}; for (const historicalItem of historicalResult) { @@ -124,7 +124,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); @@ -145,7 +145,7 @@ export class YahooFinanceService implements DataProviderInterface { ); const response: { - [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; } = {}; response[symbol] = {}; @@ -183,8 +183,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/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index 433547c94..47c67c3de 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,7 @@ import ms from 'ms'; @Injectable() export class ExchangeRateDataService { private currencies: string[] = []; - private currencyPairs: IDataGatheringItem[] = []; + private currencyPairs: DataGatheringItem[] = []; private exchangeRates: { [currencyPair: string]: number } = {}; public constructor( diff --git a/apps/api/src/services/interfaces/interfaces.ts b/apps/api/src/services/interfaces/interfaces.ts index 0eaa149a3..7469754b5 100644 --- a/apps/api/src/services/interfaces/interfaces.ts +++ b/apps/api/src/services/interfaces/interfaces.ts @@ -6,11 +6,11 @@ import { MarketState } from '@ghostfolio/common/types'; import { DataSource } from '@prisma/client'; -export interface IDataProviderHistoricalResponse { +export interface DataProviderHistoricalResponse { marketPrice: number; } -export interface IDataProviderResponse { +export interface DataProviderResponse { currency: string; dataProviderInfo?: DataProviderInfo; dataSource: DataSource; @@ -18,6 +18,6 @@ export interface IDataProviderResponse { marketState: MarketState; } -export interface IDataGatheringItem extends AssetProfileIdentifier { +export interface DataGatheringItem extends AssetProfileIdentifier { date?: Date; } 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..38ad61663 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -1,6 +1,6 @@ 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 { 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, 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..1a172f3ea 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,7 +99,7 @@ export class DataGatheringProcessor { ), name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME }) - public async gatherHistoricalMarketData(job: Job) { + public async gatherHistoricalMarketData(job: Job) { const { dataSource, date, symbol } = job.data; try { 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..2d3ec45ad 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,7 +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 { 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'; @@ -94,7 +94,7 @@ export class DataGatheringService { }); } - public async gatherSymbol({ dataSource, date, symbol }: IDataGatheringItem) { + public async gatherSymbol({ dataSource, date, symbol }: DataGatheringItem) { await this.marketDataService.deleteMany({ dataSource, symbol }); const dataGatheringItems = (await this.getSymbolsMax()) @@ -276,7 +276,7 @@ export class DataGatheringService { dataGatheringItems, priority }: { - dataGatheringItems: IDataGatheringItem[]; + dataGatheringItems: DataGatheringItem[]; priority: number; }) { await this.addJobsToQueue( @@ -348,7 +348,7 @@ export class DataGatheringService { }); } - private async getCurrencies7D(): Promise { + private async getCurrencies7D(): Promise { const assetProfileIdentifiersWithCompleteMarketData = await this.getAssetProfileIdentifiersWithCompleteMarketData(); @@ -376,7 +376,7 @@ export class DataGatheringService { withUserSubscription = false }: { withUserSubscription?: boolean; - }): Promise { + }): Promise { const symbolProfiles = await this.symbolProfileService.getActiveSymbolProfilesByUserSubscription( { @@ -407,7 +407,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/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..ba77ce162 100644 --- a/apps/client/src/app/components/rule/rule.component.ts +++ b/apps/client/src/app/components/rule/rule.component.ts @@ -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({ @@ -83,7 +83,7 @@ export class GfRuleComponent implements OnInit { rule, categoryName: this.categoryName, settings: this.settings - } as IRuleSettingsDialogParams, + } as RuleSettingsDialogParams, width: this.deviceType === 'mobile' ? '100vw' : '50rem' }); 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..49c6dc5a3 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 @@ -3,7 +3,7 @@ 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 +29,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..8788e54fe 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'; -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..c58c7fa28 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'; -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..9c31aa7bd 100644 --- a/apps/client/src/app/core/notification/notification.service.ts +++ b/apps/client/src/app/core/notification/notification.service.ts @@ -8,9 +8,9 @@ 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/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 6a90bc4df..a04ad8d56 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -1,7 +1,7 @@ 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 { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { HEADER_KEY_SKIP_INTERCEPTOR, HEADER_KEY_TOKEN @@ -208,7 +208,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..74d21bf41 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -19,7 +19,7 @@ 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 { DataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { PropertyDto } from '@ghostfolio/api/services/property/property.dto'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { @@ -291,7 +291,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 })}` ); } 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..15afc54c9 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/api/services/interfaces/interfaces'; export interface DividendsResponse { dividends: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; } 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..24383ab07 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/api/services/interfaces/interfaces'; export interface HistoricalResponse { historicalData: { - [date: string]: IDataProviderHistoricalResponse; + [date: string]: DataProviderHistoricalResponse; }; } 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..8b9b09cb8 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/api/services/interfaces/interfaces'; export interface QuotesResponse { - quotes: { [symbol: string]: IDataProviderResponse }; + quotes: { [symbol: string]: DataProviderResponse }; } diff --git a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts index f75aaea01..f9034df71 100644 --- a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts +++ b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts @@ -18,8 +18,8 @@ import { Params, RouterModule } from '@angular/router'; import { SearchMode } from '../enums/search-mode'; import { - IAssetSearchResultItem, - ISearchResultItem + AssetSearchResultItem, + SearchResultItem } from '../interfaces/interfaces'; @Component({ @@ -37,7 +37,7 @@ export class GfAssistantListItemComponent return this.hasFocus; } - @Input() item: ISearchResultItem; + @Input() item: SearchResultItem; @Output() clicked = new EventEmitter(); @@ -86,7 +86,7 @@ export class GfAssistantListItemComponent this.changeDetectorRef.markForCheck(); } - public isAsset(item: ISearchResultItem): item is IAssetSearchResultItem { + public isAsset(item: SearchResultItem): item is AssetSearchResultItem { return ( (item.mode === SearchMode.ASSET_PROFILE || item.mode === SearchMode.HOLDING) && diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 3fc1cc232..8c04c306f 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -65,9 +65,9 @@ import { translate } from '../i18n'; import { GfAssistantListItemComponent } from './assistant-list-item/assistant-list-item.component'; import { SearchMode } from './enums/search-mode'; import { - IDateRangeOption, - ISearchResultItem, - ISearchResults + DateRangeOption, + SearchResultItem, + SearchResults } from './interfaces/interfaces'; @Component({ @@ -144,7 +144,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public accounts: Account[] = []; public assetClasses: Filter[] = []; public dateRangeFormControl = new FormControl(undefined); - public dateRangeOptions: IDateRangeOption[] = []; + public dateRangeOptions: DateRangeOption[] = []; public filterForm = this.formBuilder.group({ account: new FormControl(undefined), assetClass: new FormControl(undefined), @@ -161,7 +161,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public isOpen = false; public placeholder = $localize`Find account, holding or page...`; public searchFormControl = new FormControl(''); - public searchResults: ISearchResults = { + public searchResults: SearchResults = { accounts: [], assetProfiles: [], holdings: [], @@ -229,7 +229,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { assetProfiles: [], holdings: [], quickLinks: [] - } as ISearchResults; + } as SearchResults; if (!searchTerm) { return of(results).pipe( @@ -245,7 +245,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { } // Accounts - const accounts$: Observable> = + const accounts$: Observable> = this.searchAccounts(searchTerm).pipe( map((accounts) => ({ accounts: accounts.slice( @@ -255,7 +255,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { })), catchError((error) => { console.error('Error fetching accounts for assistant:', error); - return of({ accounts: [] as ISearchResultItem[] }); + return of({ accounts: [] as SearchResultItem[] }); }), tap(() => { this.isLoading.accounts = false; @@ -264,7 +264,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); // Asset profiles - const assetProfiles$: Observable> = this + const assetProfiles$: Observable> = this .hasPermissionToAccessAdminControl ? this.searchAssetProfiles(searchTerm).pipe( map((assetProfiles) => ({ @@ -278,14 +278,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { 'Error fetching asset profiles for assistant:', error ); - return of({ assetProfiles: [] as ISearchResultItem[] }); + return of({ assetProfiles: [] as SearchResultItem[] }); }), tap(() => { this.isLoading.assetProfiles = false; this.changeDetectorRef.markForCheck(); }) ) - : of({ assetProfiles: [] as ISearchResultItem[] }).pipe( + : of({ assetProfiles: [] as SearchResultItem[] }).pipe( tap(() => { this.isLoading.assetProfiles = false; this.changeDetectorRef.markForCheck(); @@ -293,7 +293,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); // Holdings - const holdings$: Observable> = + const holdings$: Observable> = this.searchHoldings(searchTerm).pipe( map((holdings) => ({ holdings: holdings.slice( @@ -303,7 +303,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { })), catchError((error) => { console.error('Error fetching holdings for assistant:', error); - return of({ holdings: [] as ISearchResultItem[] }); + return of({ holdings: [] as SearchResultItem[] }); }), tap(() => { this.isLoading.holdings = false; @@ -312,7 +312,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); // Quick links - const quickLinks$: Observable> = of( + const quickLinks$: Observable> = of( this.searchQuickLinks(searchTerm) ).pipe( map((quickLinks) => ({ @@ -330,7 +330,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { // Merge all results return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe( scan( - (acc: ISearchResults, curr: Partial) => ({ + (acc: SearchResults, curr: Partial) => ({ ...acc, ...curr }), @@ -339,7 +339,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { assetProfiles: [], holdings: [], quickLinks: [] - } as ISearchResults + } as SearchResults ) ); }), @@ -658,7 +658,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }, this.PRESELECTION_DELAY); } - private searchAccounts(aSearchTerm: string): Observable { + private searchAccounts(aSearchTerm: string): Observable { return this.dataService .fetchAccounts({ filters: [ @@ -688,7 +688,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { private searchAssetProfiles( aSearchTerm: string - ): Observable { + ): Observable { return this.adminService .fetchAdminMarketData({ filters: [ @@ -721,7 +721,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); } - private searchHoldings(aSearchTerm: string): Observable { + private searchHoldings(aSearchTerm: string): Observable { return this.dataService .fetchPortfolioHoldings({ filters: [ @@ -753,7 +753,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); } - private searchQuickLinks(aSearchTerm: string): ISearchResultItem[] { + private searchQuickLinks(aSearchTerm: string): SearchResultItem[] { const searchTerm = aSearchTerm.toLowerCase(); const allRoutes = Object.values(internalRoutes) 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[]; } From 3b4705405bd166de92c67597fa0b0d3c55cb6e3b Mon Sep 17 00:00:00 2001 From: Vansh <140736931+Vansh-Parate@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:09:09 +0530 Subject: [PATCH 05/12] Task/improve typings of getAsset() functionality (#5804) * Improve typings of getAsset() functionality --- apps/api/src/app/asset/asset.controller.ts | 4 ++-- apps/client/src/app/services/data.service.ts | 3 ++- libs/common/src/lib/interfaces/index.ts | 2 ++ .../src/lib/interfaces/responses/asset-response.interface.ts | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 libs/common/src/lib/interfaces/responses/asset-response.interface.ts 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/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 74d21bf41..66d84c09d 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -30,6 +30,7 @@ import { AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, + AssetResponse, BenchmarkMarketDataDetailsResponse, BenchmarkResponse, DataProviderHealthResponse, @@ -345,7 +346,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) { diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index b69891e31..854c53df0 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -40,6 +40,7 @@ import type { AccountBalancesResponse } from './responses/account-balances-respo import type { AccountsResponse } from './responses/accounts-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 { DataEnhancerHealthResponse } from './responses/data-enhancer-health-response.interface'; @@ -91,6 +92,7 @@ export { ApiKeyResponse, AssetClassSelectorOption, AssetProfileIdentifier, + AssetResponse, Benchmark, BenchmarkMarketDataDetailsResponse, BenchmarkProperty, 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 {} From 32152806367b0cfa9cbe77e394507200cff7533a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADn?= Date: Mon, 20 Oct 2025 20:17:05 +0200 Subject: [PATCH 06/12] Task/extract portfolio filter sub form of assistant to reusable component (#5618) * Extract portfolio filter sub form of assistant to reusable component * Update changelog --- CHANGELOG.md | 1 + .../src/lib/interfaces/user.interface.ts | 9 +- .../src/lib/assistant/assistant.component.ts | 123 ++++-------- libs/ui/src/lib/assistant/assistant.html | 170 ++++++----------- .../ui/src/lib/portfolio-filter-form/index.ts | 2 + .../portfolio-filter-form/interfaces/index.ts | 1 + .../portfolio-filter-form-value.interface.ts | 8 + .../portfolio-filter-form.component.html | 75 ++++++++ .../portfolio-filter-form.component.scss | 3 + ...portfolio-filter-form.component.stories.ts | 79 ++++++++ .../portfolio-filter-form.component.ts | 177 ++++++++++++++++++ 11 files changed, 449 insertions(+), 199 deletions(-) create mode 100644 libs/ui/src/lib/portfolio-filter-form/index.ts create mode 100644 libs/ui/src/lib/portfolio-filter-form/interfaces/index.ts create mode 100644 libs/ui/src/lib/portfolio-filter-form/interfaces/portfolio-filter-form-value.interface.ts create mode 100644 libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html create mode 100644 libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.scss create mode 100644 libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts create mode 100644 libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e783ae690..08201a175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 on the analysis page (experimental) - Improved the language localization for German (`de`) diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index a48317fad..2e0906895 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -1,6 +1,9 @@ -import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; +import { + AccountWithPlatform, + SubscriptionType +} 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 +12,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/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 8c04c306f..eaf96f496 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -1,11 +1,10 @@ -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces'; import { InternalRoute } from '@ghostfolio/common/routes/interfaces/internal-route.interface'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; -import { DateRange } from '@ghostfolio/common/types'; +import { AccountWithPlatform, DateRange } from '@ghostfolio/common/types'; import { FocusKeyManager } from '@angular/cdk/a11y'; import { @@ -25,19 +24,14 @@ import { ViewChild, ViewChildren } from '@angular/core'; -import { - FormBuilder, - FormControl, - FormsModule, - ReactiveFormsModule -} from '@angular/forms'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatMenuTrigger } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; -import { Account, AssetClass, DataSource } from '@prisma/client'; +import { AssetClass, DataSource } from '@prisma/client'; import { differenceInYears } from 'date-fns'; import Fuse from 'fuse.js'; import { addIcons } from 'ionicons'; @@ -60,8 +54,11 @@ import { tap } from 'rxjs/operators'; -import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component'; import { translate } from '../i18n'; +import { + GfPortfolioFilterFormComponent, + PortfolioFilterFormValue +} from '../portfolio-filter-form'; import { GfAssistantListItemComponent } from './assistant-list-item/assistant-list-item.component'; import { SearchMode } from './enums/search-mode'; import { @@ -75,8 +72,7 @@ import { imports: [ FormsModule, GfAssistantListItemComponent, - GfEntityLogoComponent, - GfSymbolPipe, + GfPortfolioFilterFormComponent, IonIcon, MatButtonModule, MatFormFieldModule, @@ -141,16 +137,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public static readonly SEARCH_RESULTS_DEFAULT_LIMIT = 5; - public accounts: Account[] = []; + public accounts: AccountWithPlatform[] = []; public assetClasses: Filter[] = []; public dateRangeFormControl = new FormControl(undefined); public dateRangeOptions: DateRangeOption[] = []; - public filterForm = this.formBuilder.group({ - account: new FormControl(undefined), - assetClass: new FormControl(undefined), - holding: new FormControl(undefined), - tag: new FormControl(undefined) - }); public holdings: PortfolioPosition[] = []; public isLoading = { accounts: false, @@ -160,6 +150,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }; public isOpen = false; public placeholder = $localize`Find account, holding or page...`; + public portfolioFilterFormControl = new FormControl( + { + account: null, + assetClass: null, + holding: null, + tag: null + } + ); public searchFormControl = new FormControl(''); public searchResults: SearchResults = { accounts: [], @@ -186,8 +184,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public constructor( private adminService: AdminService, private changeDetectorRef: ChangeDetectorRef, - private dataService: DataService, - private formBuilder: FormBuilder + private dataService: DataService ) { addIcons({ closeCircleOutline, closeOutline, searchOutline }); } @@ -244,7 +241,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); } - // Accounts const accounts$: Observable> = this.searchAccounts(searchTerm).pipe( map((accounts) => ({ @@ -263,7 +259,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }) ); - // Asset profiles const assetProfiles$: Observable> = this .hasPermissionToAccessAdminControl ? this.searchAssetProfiles(searchTerm).pipe( @@ -292,7 +287,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }) ); - // Holdings const holdings$: Observable> = this.searchHoldings(searchTerm).pipe( map((holdings) => ({ @@ -311,7 +305,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }) ); - // Quick links const quickLinks$: Observable> = of( this.searchQuickLinks(searchTerm) ).pipe( @@ -327,7 +320,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }) ); - // Merge all results return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe( scan( (acc: SearchResults, curr: Partial) => ({ @@ -362,22 +354,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { quickLinks: [] }; this.changeDetectorRef.markForCheck(); - }, - complete: () => { - this.isLoading = { - accounts: false, - assetProfiles: false, - holdings: false, - quickLinks: false - }; - this.changeDetectorRef.markForCheck(); } }); } public ngOnChanges() { - this.accounts = this.user?.accounts ?? []; - this.dateRangeOptions = [ { label: $localize`Today`, @@ -445,7 +426,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { this.dateRangeFormControl.setValue(this.user?.settings?.dateRange ?? null); - this.filterForm.disable({ emitEvent: false }); + if (this.hasPermissionToChangeFilters) { + this.portfolioFilterFormControl.enable({ emitEvent: false }); + } else { + this.portfolioFilterFormControl.disable({ emitEvent: false }); + } this.tags = this.user?.tags @@ -459,29 +444,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { type: 'TAG' }; }) ?? []; - - if (this.tags.length === 0) { - this.filterForm.get('tag').disable({ emitEvent: false }); - } - } - - public hasFilter(aFormValue: { [key: string]: string }) { - return Object.values(aFormValue).some((value) => { - return !!value; - }); - } - - public holdingComparisonFunction( - option: PortfolioPosition, - value: PortfolioPosition - ): boolean { - if (value === null) { - return false; - } - - return ( - getAssetProfileIdentifier(option) === getAssetProfileIdentifier(value) - ); } public initialize() { @@ -527,36 +489,35 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { .sort((a, b) => { return a.name?.localeCompare(b.name); }); - this.setFilterFormValues(); - if (this.hasPermissionToChangeFilters) { - this.filterForm.enable({ emitEvent: false }); - } + this.setPortfolioFilterFormValues(); this.changeDetectorRef.markForCheck(); }); } public onApplyFilters() { + const filterValue = this.portfolioFilterFormControl.value; + this.filtersChanged.emit([ { - id: this.filterForm.get('account').value, + id: filterValue?.account, type: 'ACCOUNT' }, { - id: this.filterForm.get('assetClass').value, + id: filterValue?.assetClass, type: 'ASSET_CLASS' }, { - id: this.filterForm.get('holding').value?.dataSource, + id: filterValue?.holding?.dataSource, type: 'DATA_SOURCE' }, { - id: this.filterForm.get('holding').value?.symbol, + id: filterValue?.holding?.symbol, type: 'SYMBOL' }, { - id: this.filterForm.get('tag').value, + id: filterValue?.tag, type: 'TAG' } ]); @@ -569,12 +530,15 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { } public onCloseAssistant() { + this.portfolioFilterFormControl.reset(); this.setIsOpen(false); this.closed.emit(); } public onResetFilters() { + this.portfolioFilterFormControl.reset(); + this.filtersChanged.emit( this.filterTypes.map((type) => { return { @@ -786,7 +750,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); } - private setFilterFormValues() { + private setPortfolioFilterFormValues() { const dataSource = this.user?.settings?.[ 'filters.dataSource' ] as DataSource; @@ -800,16 +764,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); }); - this.filterForm.setValue( - { - account: this.user?.settings?.['filters.accounts']?.[0] ?? null, - assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null, - holding: selectedHolding ?? null, - tag: this.user?.settings?.['filters.tags']?.[0] ?? null - }, - { - emitEvent: false - } - ); + this.portfolioFilterFormControl.setValue({ + account: this.user?.settings?.['filters.accounts']?.[0] ?? null, + assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null, + holding: selectedHolding ?? null, + tag: this.user?.settings?.['filters.tags']?.[0] ?? null + }); } } diff --git a/libs/ui/src/lib/assistant/assistant.html b/libs/ui/src/lib/assistant/assistant.html index 5954ce369..e0a8f2fc9 100644 --- a/libs/ui/src/lib/assistant/assistant.html +++ b/libs/ui/src/lib/assistant/assistant.html @@ -164,119 +164,61 @@
} -
- @if (!searchFormControl.value) { -
- - Date Range - - @for (range of dateRangeOptions; track range) { - {{ range.label }} - } - - -
-
-
- - 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 - }} - } - - -
-
- - - -
+ @if (!searchFormControl.value) { +
+ + Date Range + + @for ( + dateRangeOption of dateRangeOptions; + track dateRangeOption.value + ) { + {{ + dateRangeOption.label + }} + } + + +
+
+ +
+ + +
- } - +
+ }
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..794f43d4d --- /dev/null +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts @@ -0,0 +1,177 @@ +import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; +import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; +import { Filter, PortfolioPosition } from '@ghostfolio/common/interfaces'; +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 + }; +} From b915c9554e6bec9ee2cb690a8ffaf4a8fd857e6f Mon Sep 17 00:00:00 2001 From: Dibyendu Sahoo Date: Tue, 21 Oct 2025 00:07:35 +0530 Subject: [PATCH 07/12] Task/extend rule settings interface by locale (#5802) * Extend rule settings interface by locale --- apps/api/src/models/interfaces/rule-settings.interface.ts | 1 + .../rules/account-cluster-risk/current-investment.ts | 7 ++++++- .../models/rules/account-cluster-risk/single-account.ts | 3 ++- .../src/models/rules/asset-class-cluster-risk/equity.ts | 7 ++++++- .../models/rules/asset-class-cluster-risk/fixed-income.ts | 7 ++++++- .../base-currency-current-investment.ts | 7 ++++++- .../rules/currency-cluster-risk/current-investment.ts | 7 ++++++- .../economic-market-cluster-risk/developed-markets.ts | 7 ++++++- .../rules/economic-market-cluster-risk/emerging-markets.ts | 7 ++++++- .../models/rules/emergency-fund/emergency-fund-setup.ts | 7 ++++++- .../src/models/rules/fees/fee-ratio-initial-investment.ts | 7 ++++++- apps/api/src/models/rules/liquidity/buying-power.ts | 7 ++++++- .../rules/regional-market-cluster-risk/asia-pacific.ts | 7 ++++++- .../rules/regional-market-cluster-risk/emerging-markets.ts | 7 ++++++- .../models/rules/regional-market-cluster-risk/europe.ts | 7 ++++++- .../src/models/rules/regional-market-cluster-risk/japan.ts | 7 ++++++- .../rules/regional-market-cluster-risk/north-america.ts | 7 ++++++- 17 files changed, 93 insertions(+), 16 deletions(-) diff --git a/apps/api/src/models/interfaces/rule-settings.interface.ts b/apps/api/src/models/interfaces/rule-settings.interface.ts index 377bab52b..ff22650ca 100644 --- a/apps/api/src/models/interfaces/rule-settings.interface.ts +++ b/apps/api/src/models/interfaces/rule-settings.interface.ts @@ -1,3 +1,4 @@ export interface RuleSettings { isActive: boolean; + locale: string; } 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..51c808b25 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 @@ -121,9 +121,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..0e07a9dc6 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 @@ -72,8 +72,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..9a6f9dacb 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 @@ -109,9 +109,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..70cdb63c8 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 @@ -109,9 +109,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..273c98e35 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 @@ -97,9 +97,14 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment 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..fa4f80d40 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 @@ -104,9 +104,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..1414b53ed 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 @@ -104,9 +104,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..2129f438b 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 @@ -59,9 +59,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..c5448a277 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 @@ -82,9 +82,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..70393278d 100644 --- a/apps/api/src/models/rules/liquidity/buying-power.ts +++ b/apps/api/src/models/rules/liquidity/buying-power.ts @@ -86,9 +86,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/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 From 7ee38d0067e79aef81d14facf4e810875a78d28e Mon Sep 17 00:00:00 2001 From: Ani07-05 <86768646+Ani07-05@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:10:55 +0530 Subject: [PATCH 08/12] Task/refactor Export interface to ExportResponse interface (#5805) * Refactor Export interface to ExportResponse interface --- apps/api/src/app/export/export.controller.ts | 4 ++-- apps/api/src/app/export/export.service.ts | 4 ++-- .../portfolio/calculator/portfolio-calculator-test-utils.ts | 4 ++-- .../calculator/roai/portfolio-calculator-btceur.spec.ts | 4 ++-- .../roai/portfolio-calculator-btcusd-short.spec.ts | 4 ++-- .../calculator/roai/portfolio-calculator-btcusd.spec.ts | 4 ++-- ...portfolio-calculator-novn-buy-and-sell-partially.spec.ts | 4 ++-- .../roai/portfolio-calculator-novn-buy-and-sell.spec.ts | 4 ++-- apps/client/src/app/services/data.service.ts | 4 ++-- apps/client/src/app/services/ics/ics.service.ts | 4 ++-- libs/common/src/lib/interfaces/index.ts | 4 ++-- .../export-response.interface.ts} | 6 +++--- 12 files changed, 25 insertions(+), 25 deletions(-) rename libs/common/src/lib/interfaces/{export.interface.ts => responses/export-response.interface.ts} (82%) diff --git a/apps/api/src/app/export/export.controller.ts b/apps/api/src/app/export/export.controller.ts index 8fa2baa43..0b4a2c6e0 100644 --- a/apps/api/src/app/export/export.controller.ts +++ b/apps/api/src/app/export/export.controller.ts @@ -1,7 +1,7 @@ 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 { 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 { @@ -35,7 +35,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, diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 7d78bdf22..2001fd3e2 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -3,7 +3,7 @@ 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 } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { Platform, Prisma } from '@prisma/client'; @@ -28,7 +28,7 @@ export class ExportService { filters?: Filter[]; userCurrency: string; userId: string; - }): Promise { + }): Promise { const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => { return type; }); 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/roai/portfolio-calculator-btceur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts index 1ac0dcd16..ca9e5b0d5 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 @@ -15,7 +15,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 { ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -52,7 +52,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-short.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts index 29413c6ad..3e67389dd 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 @@ -15,7 +15,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 { ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -52,7 +52,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..f08083554 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 @@ -15,7 +15,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 { ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -52,7 +52,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-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 0f1cdfff7..4678dbd5e 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 @@ -15,7 +15,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 { ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -52,7 +52,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..c4ccab7ad 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 @@ -15,7 +15,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 { ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; @@ -52,7 +52,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/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 66d84c09d..d6d582c56 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -34,7 +34,7 @@ import { BenchmarkMarketDataDetailsResponse, BenchmarkResponse, DataProviderHealthResponse, - Export, + ExportResponse, Filter, ImportResponse, InfoItem, @@ -407,7 +407,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 }); } 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/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 854c53df0..6c8754ea9 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -15,7 +15,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'; @@ -49,6 +48,7 @@ import type { DataProviderGhostfolioStatusResponse } from './responses/data-prov import type { DataProviderHealthResponse } from './responses/data-provider-health-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'; @@ -105,7 +105,7 @@ export { DataProviderInfo, DividendsResponse, EnhancedSymbolProfile, - Export, + ExportResponse, Filter, FilterGroup, FireWealth, diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/responses/export-response.interface.ts similarity index 82% rename from libs/common/src/lib/interfaces/export.interface.ts rename to libs/common/src/lib/interfaces/responses/export-response.interface.ts index 16a49b0ef..a5416e886 100644 --- a/libs/common/src/lib/interfaces/export.interface.ts +++ b/libs/common/src/lib/interfaces/responses/export-response.interface.ts @@ -7,10 +7,10 @@ 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'; -export interface Export { +export interface ExportResponse { accounts: (Omit & { balances: AccountBalance[]; })[]; From 9b51c2da5d7c0ee9986aef6e2e3296a7ec4fafe7 Mon Sep 17 00:00:00 2001 From: Harsh Santwani <96873014+HydrallHarsh@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:12:30 +0530 Subject: [PATCH 09/12] Task/improve typings of getOrderById() functionality (#5810) * Improve typings of getOrderById() functionality --- apps/api/src/app/order/order.controller.ts | 5 +++-- apps/client/src/app/services/data.service.ts | 8 +++----- libs/common/src/lib/interfaces/index.ts | 2 ++ .../interfaces/responses/activity-response.interface.ts | 3 +++ 4 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 libs/common/src/lib/interfaces/responses/activity-response.interface.ts diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index ffed8ac2e..86228cf2e 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -11,6 +11,7 @@ import { DATA_GATHERING_QUEUE_PRIORITY_HIGH, HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; +import { ActivityResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { DateRange, RequestWithUser } from '@ghostfolio/common/types'; @@ -36,7 +37,7 @@ 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 { Activities } from './interfaces/activities.interface'; import { OrderService } from './order.service'; import { UpdateOrderDto } from './update-order.dto'; @@ -157,7 +158,7 @@ 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; diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index d6d582c56..549675f7f 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -9,10 +9,7 @@ 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 { Activities } 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'; @@ -27,6 +24,7 @@ import { AccessTokenResponse, AccountBalancesResponse, AccountsResponse, + ActivityResponse, AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, @@ -248,7 +246,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); diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 6c8754ea9..e3c2c2038 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -37,6 +37,7 @@ 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 { AccountsResponse } from './responses/accounts-response.interface'; +import type { ActivityResponse } from './responses/activity-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'; @@ -82,6 +83,7 @@ export { AccountBalance, AccountBalancesResponse, AccountsResponse, + ActivityResponse, AdminData, AdminJobs, AdminMarketData, 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..5dd338627 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/activity-response.interface.ts @@ -0,0 +1,3 @@ +import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; + +export interface ActivityResponse extends Activity {} From ed364fc25f1973d1c7b5e361a41728cb12c63c6b Mon Sep 17 00:00:00 2001 From: Arshad Jamal Date: Tue, 21 Oct 2025 22:26:47 +0530 Subject: [PATCH 10/12] Bugfix/database seed (#5792) * Fix database seed * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 4 +++ package-lock.json | 66 ++++++++++++++++++++++++++++++++++++++++++----- package.json | 2 ++ prisma.config.ts | 5 +++- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08201a175..7690d90ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Formatted the holdings table in the _Copy portfolio data to clipboard for AI prompt_ action on the analysis page (experimental) - Improved the language localization for German (`de`) +### Fixed + +- Fixed an issue in the database seeding process caused by unresolved environment variables in `DATABASE_URL` + ## 2.209.0 - 2025-10-18 ### Added diff --git a/package-lock.json b/package-lock.json index 74b9936a9..16a8381b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,8 @@ "countries-list": "3.1.1", "countup.js": "2.9.0", "date-fns": "4.1.0", + "dotenv": "17.2.3", + "dotenv-expand": "12.0.3", "envalid": "8.1.0", "fuse.js": "7.1.0", "google-spreadsheet": "3.2.0", @@ -9081,6 +9083,33 @@ "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", @@ -21037,9 +21066,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" @@ -21049,9 +21078,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" @@ -21063,6 +21092,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", @@ -33459,6 +33500,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", diff --git a/package.json b/package.json index ff8adc51f..403ce7f7b 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,8 @@ "countries-list": "3.1.1", "countup.js": "2.9.0", "date-fns": "4.1.0", + "dotenv": "17.2.3", + "dotenv-expand": "12.0.3", "envalid": "8.1.0", "fuse.js": "7.1.0", "google-spreadsheet": "3.2.0", diff --git a/prisma.config.ts b/prisma.config.ts index 24da6d886..60597cbf1 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -1,7 +1,10 @@ -import 'dotenv/config'; +import { config } from 'dotenv'; +import { expand } from 'dotenv-expand'; import { join } from 'node:path'; import { defineConfig } from 'prisma/config'; +expand(config({ quiet: true })); + export default defineConfig({ migrations: { path: join('prisma', 'migrations'), From ceace870a875f1e69c4e11e9b0db482ff84e82c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20=C5=81=C4=85giewka?= Date: Tue, 21 Oct 2025 20:46:27 +0200 Subject: [PATCH 11/12] Task/upgrade ioredis to forfeit overriding defaults (#5813) * Upgrade ioredis to forfeit overriding defaults * Update changelog --- CHANGELOG.md | 2 ++ apps/api/src/app/app.module.ts | 1 - package-lock.json | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7690d90ed..cbd306f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 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 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/package-lock.json b/package-lock.json index 16a8381b4..cc58d01ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6115,9 +6115,9 @@ } }, "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": { @@ -25108,12 +25108,12 @@ } }, "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", From 167cbcd5c7201106638970d57e75b286ce947c23 Mon Sep 17 00:00:00 2001 From: jjs2099 <140512982+jjs2099@users.noreply.github.com> Date: Wed, 22 Oct 2025 00:29:12 +0530 Subject: [PATCH 12/12] Bugfix/submit form of login with access token dialog with enter key press (#5751) * Fix form submit with enter key press * Update changelog --- CHANGELOG.md | 1 + .../login-with-access-token-dialog.html | 57 ++++++++++--------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd306f9c..63cbf5898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 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..b51802caf 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,13 +2,14 @@
-
+ Security Token + + @if (data.hasPermissionToUseSocialLogin) { +
or
+
+ }
- @if (data.hasPermissionToUseSocialLogin) { -
or
-
- - Sign in with Google -
- }
+