diff --git a/CHANGELOG.md b/CHANGELOG.md index abaa9037e..ce859cb5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,15 +11,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactored various `lodash` functions with native JavaScript equivalents -## Unreleased +### Fixed + +- Fixed an issue with the renaming of activities with type `FEE`, `INTEREST`, `ITEM` or `LIABILITY` + +## 2.133.1 - 2025-01-09 + +### Added + +- Added a _Copy AI prompt to clipboard_ action to the analysis page (experimental) ### Changed +- Improved the usability of the _Copy link to clipboard_ action by adding a confirmation on success in the access table to share the portfolio +- Improved the endpoint to fetch the logo of an asset or a platform by sending the original MIME type +- Eliminated `got` in favor of using `fetch` - Changed the `REDIS_HOST` from `localhost` to `redis` in `.env.example` - Changed the _Postgres_ host from `localhost` to `postgres` in `.env.example` - Changed the _Postgres_ image from `postgres:15` to `postgres:15-alpine` in the `docker-compose` files - Introduced `extends` in the `docker-compose` files - Improved the language localization for German (`de`) +- Refreshed the cryptocurrencies list +- Upgraded `envalid` from version `7.3.1` to `8.0.0` +- Upgraded `replace-in-file` from version `7.0.1` to `8.3.0` + +### Fixed + +- Improved the handling of a missing url in the endpoint to fetch the logo of an asset or a platform +- Fixed the _Storybook_ setup ## 2.132.0 - 2024-12-30 @@ -43,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `ngx-markdown` from version `18.0.0` to `19.0.0` - Upgraded `Nx` from version `20.1.2` to `20.3.0` - Upgraded `prisma` from version `6.0.1` to `6.1.0` +- Upgraded `storybook` from version `8.2.5` to `8.4.7` - Upgraded `zone.js` from version `0.14.10` to `0.15.0` ### Fixed diff --git a/Dockerfile b/Dockerfile index e6c38f273..103dc3b9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,13 +25,13 @@ RUN npm install COPY ./decorate-angular-cli.js decorate-angular-cli.js RUN node decorate-angular-cli.js -COPY ./nx.json nx.json -COPY ./replace.build.js replace.build.js -COPY ./jest.preset.js jest.preset.js +COPY ./apps apps +COPY ./libs libs COPY ./jest.config.ts jest.config.ts +COPY ./jest.preset.js jest.preset.js +COPY ./nx.json nx.json +COPY ./replace.build.mjs replace.build.mjs COPY ./tsconfig.base.json tsconfig.base.json -COPY ./libs libs -COPY ./apps apps RUN npm run build:production diff --git a/README.md b/README.md index 4151617c8..14477ea9a 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,6 @@ If you like to support this project, get [**Ghostfolio Premium**](https://ghostf ## License -© 2021 - 2024 [Ghostfolio](https://ghostfol.io) +© 2021 - 2025 [Ghostfolio](https://ghostfol.io) Licensed under the [AGPLv3 License](https://www.gnu.org/licenses/agpl-3.0.html). diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 7ac2c5915..6d097aeff 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -31,6 +31,7 @@ import { AuthDeviceModule } from './auth-device/auth-device.module'; import { AuthModule } from './auth/auth.module'; import { BenchmarkModule } from './benchmark/benchmark.module'; import { CacheModule } from './cache/cache.module'; +import { AiModule } from './endpoints/ai/ai.module'; import { ApiKeysModule } from './endpoints/api-keys/api-keys.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; import { MarketDataModule } from './endpoints/market-data/market-data.module'; @@ -57,6 +58,7 @@ import { UserModule } from './user/user.module'; AdminModule, AccessModule, AccountModule, + AiModule, ApiKeysModule, AssetModule, AuthDeviceModule, diff --git a/apps/api/src/app/endpoints/ai/ai.controller.ts b/apps/api/src/app/endpoints/ai/ai.controller.ts new file mode 100644 index 000000000..981b26aa2 --- /dev/null +++ b/apps/api/src/app/endpoints/ai/ai.controller.ts @@ -0,0 +1,39 @@ +import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; +import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { + DEFAULT_CURRENCY, + DEFAULT_LANGUAGE_CODE +} from '@ghostfolio/common/config'; +import { AiPromptResponse } from '@ghostfolio/common/interfaces'; +import { permissions } from '@ghostfolio/common/permissions'; +import type { RequestWithUser } from '@ghostfolio/common/types'; + +import { Controller, Get, Inject, UseGuards } from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; + +import { AiService } from './ai.service'; + +@Controller('ai') +export class AiController { + public constructor( + private readonly aiService: AiService, + @Inject(REQUEST) private readonly request: RequestWithUser + ) {} + + @Get('prompt') + @HasPermission(permissions.readAiPrompt) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getPrompt(): Promise { + const prompt = await this.aiService.getPrompt({ + impersonationId: undefined, + languageCode: + this.request.user.Settings.settings.language ?? DEFAULT_LANGUAGE_CODE, + userCurrency: + this.request.user.Settings.settings.baseCurrency ?? DEFAULT_CURRENCY, + userId: this.request.user.id + }); + + return { prompt }; + } +} diff --git a/apps/api/src/app/endpoints/ai/ai.module.ts b/apps/api/src/app/endpoints/ai/ai.module.ts new file mode 100644 index 000000000..5a30f3264 --- /dev/null +++ b/apps/api/src/app/endpoints/ai/ai.module.ts @@ -0,0 +1,51 @@ +import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; +import { AccountService } from '@ghostfolio/api/app/account/account.service'; +import { OrderModule } from '@ghostfolio/api/app/order/order.module'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; +import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; +import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; +import { UserModule } from '@ghostfolio/api/app/user/user.module'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; +import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; +import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; +import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; + +import { Module } from '@nestjs/common'; + +import { AiController } from './ai.controller'; +import { AiService } from './ai.service'; + +@Module({ + controllers: [AiController], + imports: [ + ConfigurationModule, + DataProviderModule, + ExchangeRateDataModule, + ImpersonationModule, + MarketDataModule, + OrderModule, + PortfolioSnapshotQueueModule, + PrismaModule, + RedisCacheModule, + SymbolProfileModule, + UserModule + ], + providers: [ + AccountBalanceService, + AccountService, + AiService, + CurrentRateService, + MarketDataService, + PortfolioCalculatorFactory, + PortfolioService, + RulesService + ] +}) +export class AiModule {} diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts new file mode 100644 index 000000000..59dec6add --- /dev/null +++ b/apps/api/src/app/endpoints/ai/ai.service.ts @@ -0,0 +1,60 @@ +import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; + +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AiService { + public constructor(private readonly portfolioService: PortfolioService) {} + + public async getPrompt({ + impersonationId, + languageCode, + userCurrency, + userId + }: { + impersonationId: string; + languageCode: string; + userCurrency: string; + userId: string; + }) { + const { holdings } = await this.portfolioService.getDetails({ + impersonationId, + userId + }); + + const holdingsTable = [ + '| Name | Symbol | Currency | Asset Class | Asset Sub Class | Allocation in Percentage |', + '| --- | --- | --- | --- | --- | --- |', + ...Object.values(holdings) + .sort((a, b) => { + return b.allocationInPercentage - a.allocationInPercentage; + }) + .map( + ({ + allocationInPercentage, + assetClass, + assetSubClass, + currency, + name, + symbol + }) => { + return `| ${name} | ${symbol} | ${currency} | ${assetClass} | ${assetSubClass} | ${(allocationInPercentage * 100).toFixed(3)}% |`; + } + ) + ]; + + return [ + `You are a neutral financial assistant. Please analyze the following investment portfolio (base currency being ${userCurrency}) in simple words.`, + ...holdingsTable, + 'Structure your answer with these sections:', + 'Overview: Briefly summarize the portfolio’s composition and allocation rationale.', + 'Risk Assessment: Identify potential risks, including market volatility, concentration, and sectoral imbalances.', + 'Advantages: Highlight strengths, focusing on growth potential, diversification, or other benefits.', + 'Disadvantages: Point out weaknesses, such as overexposure or lack of defensive assets.', + 'Target Group: Discuss who this portfolio might suit (e.g., risk tolerance, investment goals, life stages, and experience levels).', + 'Optimization Ideas: Offer ideas to complement the portfolio, ensuring they are constructive and neutral in tone.', + 'Conclusion: Provide a concise summary highlighting key insights.', + `Provide your answer in the following language: ${languageCode}.` + ].join('\n'); + } +} diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index cd4035164..1af41520d 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -33,7 +33,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as cheerio from 'cheerio'; import { format, subDays } from 'date-fns'; -import got from 'got'; @Injectable() export class InfoService { @@ -155,16 +154,15 @@ export class InfoService { private async countDockerHubPulls(): Promise { try { - const { pull_count } = await got( - `https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`, + const { pull_count } = (await fetch( + 'https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio', { headers: { 'User-Agent': 'request' }, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json())) as { pull_count: number }; return pull_count; } catch (error) { @@ -176,18 +174,17 @@ export class InfoService { private async countGitHubContributors(): Promise { try { - const { body } = await got('https://github.com/ghostfolio/ghostfolio', { - // @ts-ignore + const body = await fetch('https://github.com/ghostfolio/ghostfolio', { signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) - }); + }).then((res) => res.text()); const $ = cheerio.load(body); return extractNumberFromString({ value: $( - `a[href="/ghostfolio/ghostfolio/graphs/contributors"] .Counter` + 'a[href="/ghostfolio/ghostfolio/graphs/contributors"] .Counter' ).text() }); } catch (error) { @@ -199,16 +196,15 @@ export class InfoService { private async countGitHubStargazers(): Promise { try { - const { stargazers_count } = await got( - `https://api.github.com/repos/ghostfolio/ghostfolio`, + const { stargazers_count } = (await fetch( + 'https://api.github.com/repos/ghostfolio/ghostfolio', { headers: { 'User-Agent': 'request' }, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json())) as { stargazers_count: number }; return stargazers_count; } catch (error) { @@ -323,7 +319,7 @@ export class InfoService { PROPERTY_BETTER_UPTIME_MONITOR_ID )) as string; - const { data } = await got( + const { data } = await fetch( `https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format( subDays(new Date(), 90), DATE_FORMAT @@ -334,12 +330,11 @@ export class InfoService { 'API_KEY_BETTER_UPTIME' )}` }, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json()); return data.attributes.availability / 100; } catch (error) { diff --git a/apps/api/src/app/logo/logo.controller.ts b/apps/api/src/app/logo/logo.controller.ts index 0982a793f..fdbe430c9 100644 --- a/apps/api/src/app/logo/logo.controller.ts +++ b/apps/api/src/app/logo/logo.controller.ts @@ -26,12 +26,13 @@ export class LogoController { @Res() response: Response ) { try { - const buffer = await this.logoService.getLogoByDataSourceAndSymbol({ - dataSource, - symbol - }); + const { buffer, type } = + await this.logoService.getLogoByDataSourceAndSymbol({ + dataSource, + symbol + }); - response.contentType('image/png'); + response.contentType(type); response.send(buffer); } catch { response.status(HttpStatus.NOT_FOUND).send(); @@ -44,9 +45,9 @@ export class LogoController { @Res() response: Response ) { try { - const buffer = await this.logoService.getLogoByUrl(url); + const { buffer, type } = await this.logoService.getLogoByUrl(url); - response.contentType('image/png'); + response.contentType(type); response.send(buffer); } catch { response.status(HttpStatus.NOT_FOUND).send(); diff --git a/apps/api/src/app/logo/logo.service.ts b/apps/api/src/app/logo/logo.service.ts index 759db2afb..ba1acdd29 100644 --- a/apps/api/src/app/logo/logo.service.ts +++ b/apps/api/src/app/logo/logo.service.ts @@ -4,7 +4,6 @@ import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { HttpException, Injectable } from '@nestjs/common'; import { DataSource } from '@prisma/client'; -import got from 'got'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; @Injectable() @@ -29,7 +28,7 @@ export class LogoService { { dataSource, symbol } ]); - if (!assetProfile) { + if (!assetProfile?.url) { throw new HttpException( getReasonPhrase(StatusCodes.NOT_FOUND), StatusCodes.NOT_FOUND @@ -39,20 +38,26 @@ export class LogoService { return this.getBuffer(assetProfile.url); } - public async getLogoByUrl(aUrl: string) { + public getLogoByUrl(aUrl: string) { return this.getBuffer(aUrl); } - private getBuffer(aUrl: string) { - return got( + private async getBuffer(aUrl: string) { + const blob = await fetch( `https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`, { headers: { 'User-Agent': 'request' }, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).buffer(); + ).then((res) => res.blob()); + + return { + buffer: await blob.arrayBuffer().then((arrayBuffer) => { + return Buffer.from(arrayBuffer); + }), + type: blob.type + }; } } diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 33e9a67da..b5c71179f 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -312,7 +312,8 @@ export class UserService { currentPermissions = without( currentPermissions, permissions.accessHoldingsChart, - permissions.createAccess + permissions.createAccess, + permissions.readAiPrompt ); // Reset benchmark diff --git a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json index ac2157389..f77297d07 100644 --- a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json +++ b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json @@ -11,7 +11,7 @@ "611": "SixEleven", "777": "Jackpot", "808": "808", - "888": "Octocoin", + "888": "888", "1337": "EliteCoin", "1717": "1717 Masonic Commemorative Token", "2015": "2015 coin", @@ -26,6 +26,7 @@ "0DOG": "Bitcoin Dogs", "0KN": "0 Knowledge Network", "0NE": "Stone", + "0X0": "0x0.ai", "0X1": "0x1.tools: AI Multi-tool Plaform", "0XBTC": "0xBitcoin", "0XCOCO": "0xCoco", @@ -36,7 +37,6 @@ "0XOS": "0xOS AI", "0XSEARCH": "Search", "0XVOX": "HashVox AI", - "0x0": "0x0.ai", "0xDIARY": "The 0xDiary Token", "0xVPN": "0xVPN.org", "1-UP": "1-UP", @@ -50,6 +50,7 @@ "1EX": "1ex Trading Board", "1FLR": "Flare Token", "1GOLD": "1irstGold", + "1GUY": "1GUY", "1INCH": "1inch", "1IRST": "1irstcoin", "1MCT": "MicroCreditToken", @@ -136,6 +137,7 @@ "A8": "Ancient8", "AA": "Alva", "AAA": "Moon Rabbit", + "AAAHHM": "Plankton in Pain", "AAB": "AAX Token", "AABL": "Abble", "AAC": "Double-A Chain", @@ -177,6 +179,7 @@ "ABONDV1": "ApeSwap", "ABR": "Allbridge", "ABT": "ArcBlock", + "ABTC": "aBTC", "ABUL": "Abulaba", "ABUNI": "Uniswap Protocol Token (Arbitrum Bridge)", "ABUSDC": "USD Coin (Arbitrum Bridge)", @@ -318,6 +321,7 @@ "AGB": "Apes Go Bananas", "AGC": "Argocoin", "AGENT": "AgentLayer", + "AGENTFUN": "AgentFun.AI", "AGET": "Agetron", "AGETH": "Kelp Gain", "AGEUR": "agEUR", @@ -454,6 +458,7 @@ "AKRO": "Akropolis", "AKT": "Akash Network", "AKTIO": "AKTIO Coin", + "AKUMA": "Akuma Inu", "AL": "ArchLoot", "ALA": "Alanyaspor Fan Token", "ALAN": "Alan the Alien", @@ -502,6 +507,7 @@ "ALLC": "All Crypto Mechanics", "ALLEY": "NFT Alley", "ALLIN": "All in", + "ALLMEE": "All.me", "ALM": "Alium Finance", "ALMAN": "Alman", "ALMC": "Awkward Look Monkey Club", @@ -577,6 +583,7 @@ "AMO": "AMO Coin", "AMOGUS": "Sussy Baka Impostor", "AMON": "AmonD", + "AMORE": "Amocucinare", "AMOS": "Amos", "AMP": "Amp", "AMPL": "Ampleforth", @@ -736,7 +743,7 @@ "ARBS": "Arbswap", "ARBT": "ARBITRAGE", "ARBUZ": "ARBUZ", - "ARC": "Neko Arc", + "ARC": "Arc", "ARCA": "Legend of Arcadia", "ARCAD": "Arcadeum", "ARCADE": "ARCADE", @@ -818,6 +825,7 @@ "ARTFI": "ARTFI", "ARTG": "Goya Giant Token", "ARTH": "ARTH", + "ARTHERA": "Arthera", "ARTI": "Arti Project", "ARTIF": "Artificial Intelligence", "ARTII": "ARTII Token", @@ -906,6 +914,7 @@ "ATHE": "Atheios", "ATHEN": "Athenas AI", "ATHENA": "Athena DexFi", + "ATHER": "Ather", "ATHVODKA": "All Time High Vodka", "ATID": "AstridDAO Token", "ATK": "Attack Wagon", @@ -932,11 +941,12 @@ "ATOZ": "Race Kingdom", "ATP": "Atlas Protocol", "ATPAY": "AtPay", - "ATR": "Ather", + "ATR": "Artrade", "ATRI": "Atari Token", "ATRNO": "AETERNUS", "ATROFA": "Atrofarm", "ATRS": "Attarius Network", + "ATRV1": "Artrade v1", "ATS": "Atlas DEX", "ATT": "Attila", "ATTR": "Attrace", @@ -991,6 +1001,7 @@ "AVATAR": "Avatar", "AVATLY": "Avatly", "AVAV": "AVAV", + "AVAV1": "AVA v1", "AVAX": "Avalanche", "AVAXIOU": "Avalanche IOU", "AVB": "Autonomous Virtual Beings", @@ -1210,10 +1221,11 @@ "BAMBIT": "BAMBIT", "BAMBOO": "BambooDeFi", "BAMITCOIN": "Bamit", - "BAN": "Banano", + "BAN": "Comedian", "BANANA": "Banana Gun", "BANANAF": "Banana For Scale", "BANANAS": "Monkey Peepo", + "BANANO": "Banano", "BANC": "Babes and Nerds", "BANCA": "BANCA", "BAND": "Band Protocol", @@ -1455,6 +1467,7 @@ "BEPR": "Blockchain Euro Project", "BEPRO": "BEPRO Network", "BERF": "BERF", + "BERG": "Bloxberg", "BERN": "BERNcash", "BERNIE": "BERNIE SENDERS", "BERRY": "Berry", @@ -1873,6 +1886,7 @@ "BOBE": "BOOK OF BILLIONAIRES", "BOBER": "BOBER", "BOBFUN": "BOB", + "BOBLS": "Boblles", "BOBO": "BOBO", "BOBOT": "Bobo The Bear", "BOBS": "Bob's Repair", @@ -2217,6 +2231,7 @@ "BTCAS": "BitcoinAsia", "BTCAT": "Bitcoin Cat", "BTCB": "Bitcoin BEP2", + "BTCBASE": "Bitcoin on Base", "BTCBR": "Bitcoin BR", "BTCBRV1": "Bitcoin BR v1", "BTCC": "Bitcoin Core", @@ -2308,8 +2323,10 @@ "BUB": "BUBCAT", "BUBBA": "Bubba", "BUBBLE": "Bubble", + "BUBBLES": "BUBBLES", "BUBO": "Budbo", "BUBU": "BUBU", + "BUBV1": "BUBCAT v1", "BUC": "Beau Cat", "BUCK": "Coinbuck", "BUCKS": "SwagBucks", @@ -2784,7 +2801,9 @@ "CHEDDA": "Chedda", "CHEEKS": "CHEEKS", "CHEEL": "Cheelee", - "CHEEMS": "Cheems", + "CHEEMS": "Cheems (cheems.pet)", + "CHEEMSCO": "Cheems", + "CHEEMSV1": "Cheems (cheems.pet) v1", "CHEEPEPE": "CHEEPEPE", "CHEERS": "DICAPRIO CHEERS", "CHEESE": "Cheese", @@ -3107,7 +3126,7 @@ "COMFI": "CompliFi", "COMM": "Community Coin", "COMMUNITYCOIN": "Community Coin", - "COMP": "Compound Governance Token", + "COMP": "Compound", "COMPCOIN": "Compcoin", "COMPD": "Compound Coin", "COMPU": "Compute Network", @@ -3125,6 +3144,7 @@ "CONSENTIUM": "Consentium", "CONTROL": "Control Token", "CONV": "Convergence", + "CONVO": "Prefrontal Cortex Convo Agent by Virtuals", "CONX": "Connex", "COO": "Cool Cats MILK", "COOCHIE": "Cucci", @@ -3708,6 +3728,7 @@ "DCCT": "DocuChain", "DCD": "DecideAI", "DCE": "Decentra Ecosystem", + "DCF": "Decentralized Finance", "DCHEFSOL": "Degen Chef", "DCHEWY": "Drop Chewy", "DCHF": "DeFi Franc", @@ -3792,7 +3813,9 @@ "DEFY": "DEFY", "DEG": "Degis", "DEGA": "Dega", + "DEGATE": "DeGate", "DEGEN": "Degen", + "DEGENAI": "Degen Spartan AI", "DEGENR": "DegenReborn", "DEGO": "Dego Finance", "DEGOD": "degod", @@ -3802,6 +3825,7 @@ "DEI": "Deimos", "DEK": "DekBox", "DEL": "Decimal", + "DELAY": "DegenLayer", "DELCHAIN": "DelChain", "DELFI": "DeltaFi", "DELI": "NFTDeli", @@ -3914,6 +3938,7 @@ "DGPT": "DigiPulse", "DGTA": "Digitra.com Token", "DGTX": "Digitex Token", + "DGV1": "Decentral Games v1", "DGVC": "DegenVC", "DGX": "Digix Gold token", "DHLT": "DeHealth", @@ -4008,6 +4033,7 @@ "DKEY": "DKEY Bank", "DKKT": "DKK Token", "DKNIGHT": "Dark Knight", + "DKP": "Dragginz", "DKS": "DarkShield", "DKT": "Duelist King", "DKUMA": "KumaDex Token", @@ -4074,6 +4100,7 @@ "DOBBY": "Dobby", "DOBEN": "dark boden", "DOBO": "DogeBonk", + "DOBUY": "Just do buy", "DOC": "Dochain", "DOCAINEURON": "Doc.ai Neuron", "DOCC": "Doc Coin", @@ -4291,13 +4318,15 @@ "DRM8": "Dream8Coin", "DROGGY": "Droggy", "DRONE": "Drone Coin", - "DROP": "Dropil", + "DROP": "DROP", + "DROPIL": "Dropil", "DROPS": "Drops", "DROVERS": "Drover Inu", "DRP": "DCORP", "DRPU": "DRP Utility", "DRS": "Digital Rupees", "DRT": "DomRaider", + "DRUGS": "Big Pharmai", "DRXNE": "Droxne", "DRZ": "Droidz", "DS": "DeStorage", @@ -4486,6 +4515,7 @@ "EBEN": "Green Ben", "EBET": "EthBet", "EBIT": "eBit", + "EBITCOIN": "eBitcoin", "EBK": "Ebakus", "EBOX": "Ethbox Token", "EBS": "EbolaShare", @@ -4494,7 +4524,7 @@ "EBSO": "eBlockStock", "EBST": "eBoost", "EBT": "ELON BUYS TWITTER", - "EBTC": "eBitcoin", + "EBTC": "Ether.fi Staked BTC", "EBULL": "ETHEREUM IS GOOD", "EBYT": "EarthByt", "EBZ": "Ebitz", @@ -4680,6 +4710,7 @@ "ELONMU": "Elon Musk", "ELONONE": "AstroElon", "ELONPEPE": "Elon Pepe Robot", + "ELONRWA": "ElonRWA", "ELONTRUMP": "ELON TRUMP", "ELP": "Ellerium", "ELS": "Ethlas", @@ -4693,6 +4724,7 @@ "ELVN": "11Minutes", "ELX": "Energy Ledger", "ELY": "Elysian", + "ELYS": "Elys Network", "ELYSIUM": "Elysium", "EM": "Eminer", "EMAID": "MaidSafeCoin", @@ -4783,6 +4815,7 @@ "EOX": "EXTRA ORDINARY", "EPAN": "Paypolitan Token", "EPANUS": "Epanus", + "EPENDLE": "Equilibria Pendle", "EPEP": "Epep", "EPETS": "Etherpets", "EPIC": "Epic Cash", @@ -4891,7 +4924,7 @@ "ETHERDELTA": "EtherDelta", "ETHERE": "Ethereal", "ETHEREM": "Etherempires", - "ETHEREUM": "Solana Ethereum Meme", + "ETHEREUMMEME": "Solana Ethereum Meme", "ETHEREUMP": "ETHEREUMPLUS", "ETHERINC": "EtherInc", "ETHERKING": "Ether Kingdoms Token", @@ -4926,6 +4959,7 @@ "ETM": "En-Tan-Mo", "ETN": "Electroneum", "ETNA": "ETNA Network", + "ETNY": "Ethernity", "ETP": "Metaverse", "ETPOS": "EtherPOS", "ETR": "Electric Token", @@ -5056,6 +5090,7 @@ "EZT": "EZToken", "EZY": "EzyStayz", "ElvishMagic": "EMAGIC", + "F": "SynFutures", "F16": "F16Coin", "F1C": "Future1coin", "F2C": "Ftribe Fighters", @@ -5114,6 +5149,7 @@ "FAST": "Fastswap", "FASTAI": "Fast And Ai", "FASTMOON": "FastMoon", + "FASTUSD": "Sei fastUSD", "FASTV1": "Fastswap v1", "FAT": "Fatcoin", "FATCAKE": "FatCake", @@ -5153,6 +5189,7 @@ "FCS": "CryptoFocus", "FCT": "FirmaChain", "FCTC": "FaucetCoin", + "FCTR": "FactorDAO", "FDC": "Fidance", "FDLS": "FIDELIS", "FDM": "Fandom", @@ -5335,10 +5372,14 @@ "FLOKINY": "Floki New Year", "FLOKIPEPE": "FlokiPepe", "FLOKITA": "FLOKITA", + "FLOKIV1": "Floki v1", + "FLOKIV2": "Floki v2", + "FLOKIV3": "Floki v3", "FLOKIX": "FLOKI X", "FLOOF": "FLOOF", "FLOOR": "FloorDAO", "FLOP": "Big Floppa", + "FLOPPA": "Floppa Cat", "FLORK": "FLORK BNB", "FLORKY": "Florky", "FLOSHIDO": "FLOSHIDO INU", @@ -5491,8 +5532,9 @@ "FREAK": "Freakoff", "FREC": "Freyrchain", "FRECNX": "FreldoCoinX", - "FRED": "FREDEnergy", + "FRED": "First Convicted Raccon Fred", "FREDDY": "FREDDY", + "FREDE": "FREDEnergy", "FREE": "FREE coin", "FREED": "FreedomCoin", "FREEDO": "Freedom", @@ -5648,6 +5690,7 @@ "FXDX": "FXDX", "FXF": "Finxflo", "FXI": "FX1 Sports", + "FXN": "FXN", "FXP": "FXPay", "FXS": "Frax Share", "FXST": "FX Stock Token", @@ -5691,6 +5734,7 @@ "GALO": "Clube Atlético Mineiro Fan Token", "GALT": "Galtcoin", "GAM": "Gambit coin", + "GAMA": "GAMA Coin", "GAMB": "GAMB", "GAMBI": "Gambi Fi", "GAMBIT": "Gambit", @@ -5952,6 +5996,7 @@ "GKI": "GKi", "GL": "Lemmings", "GLA": "Gladius", + "GLASS": "Glass Chain", "GLAX": "BLOCK GALAXY NETWORK", "GLAZE": "Glaze", "GLB": "Golden Ball", @@ -5985,7 +6030,7 @@ "GLOWSHA": "GlowShares", "GLQ": "GraphLinq Protocol", "GLR": "Glory Finance", - "GLS": "Glass Chain", + "GLS": "Glacier", "GLT": "GlobalToken", "GLX": "GalaxyCoin", "GLYPH": "GlyphCoin", @@ -6196,6 +6241,8 @@ "GRID": "Grid+", "GRIDCOIN": "GridCoin", "GRIDZ": "GridZone.io", + "GRIFFAIN": "GRIFFAIN", + "GRIFT": "ORBIT", "GRIM": "GRIMREAPER", "GRIMACE": "Grimace", "GRIMEVO": "Grim EVO", @@ -6294,6 +6341,7 @@ "GTX": "GALLACTIC", "GUA": "GUA", "GUAC": "Guacamole", + "GUAN": "Guanciale by Virtuals", "GUAP": "Guapcoin", "GUAR": "Guarium", "GUARD": "Guardian", @@ -6316,6 +6364,7 @@ "GUP": "Guppy", "GURL": "Gently Used Girl", "GURU": "Guru Network", + "GUS": "Gus", "GUSD": "Gemini Dollar", "GUSDT": "Global Utility Smart Digital Token", "GUT": "Genesis Universe", @@ -6409,6 +6458,7 @@ "HARRIS": "KAMALA HARRIS", "HARRISV": "Harris V Trump", "HARRYP": "HarryPotterObamaSonic10Inu (ERC20)", + "HARRYPO": "HarryPotterObamaPacMan8Inu", "HART": "HARA", "HASBIK": "Hasbulla", "HASH": "Provenance Blockchain", @@ -6503,6 +6553,8 @@ "HERBE": "Herbee", "HERME": "Hermes DAO", "HERMES": "Hermes Protocol", + "HERMIONE": "Hermione", + "HERMY": "Hermy The Stallion", "HERO": "Metahero", "HEROC": "HEROcoin", "HEROES": "Dehero Community Token", @@ -6808,13 +6860,15 @@ "HYN": "Hyperion", "HYP": "HyperStake", "HYPC": "HyperCycle", - "HYPE": "Hype", + "HYPE": "Hyperliquid", "HYPER": "HyperChainX", "HYPERAI": "HyperHash AI", "HYPERCOIN": "HyperCoin", "HYPERD": "HyperDAO", + "HYPERIONX": "HyperionX", "HYPERS": "HyperSpace", "HYPES": "Supreme Finance", + "HYPEV1": "Hype v1", "HYPR": "Hypr Network", "HYS": "Heiss Shares", "HYT": "HoryouToken", @@ -6955,6 +7009,7 @@ "IMI": "Influencer", "IML": "IMMLA", "IMMO": "ImmortalDAO Finance", + "IMMORTAL": "IMMORTAL.COM", "IMO": "IMO", "IMP": "CoinIMP", "IMPACT": "Impact", @@ -6980,6 +7035,7 @@ "INCAKE": "InfinityCAKE", "INCEPT": "Incept", "INCNT": "Incent", + "INCO": "InfinitiCoin", "INCORGNITO": "Incorgnito", "INCP": "InceptionCoin", "IND": "Indorse", @@ -7208,6 +7264,7 @@ "JANE": "JaneCoin", "JANET": "Janet", "JANI": "JANI", + "JANRO": "Janro The Rat", "JAR": "Jarvis+", "JARED": "Jared From Subway", "JARY": "JeromeAndGary", @@ -7261,6 +7318,7 @@ "JFI": "JackPool.finance", "JFIN": "JFIN Coin", "JFIVE": "Jonny Five", + "JFP": "JUSTICE FOR PEANUT", "JGLP": "Jones GLP", "JGN": "Juggernaut", "JHH": "Jen-Hsun Huang", @@ -7430,6 +7488,7 @@ "KANGAL": "Kangal", "KANGO": "KANGO", "KAP": "KAP Games", + "KAPPY": "Kappy", "KAPU": "Kapu", "KAR": "Karura", "KARA": "KarateCat", @@ -7540,6 +7599,7 @@ "KI": "Genopets KI", "KIAN": "Porta", "KIBA": "Kiba Inu", + "KIBAV1": "Kiba Inu v1", "KIBSHI": "KiboShib", "KICK": "Kick", "KICKS": "GetKicks", @@ -7787,6 +7847,7 @@ "KWAI": "KWAI", "KWATT": "4New", "KWD": "KIWI DEFI", + "KWEEN": "KWEEN", "KWENTA": "Kwenta", "KWH": "KWHCoin", "KWIK": "KwikSwap", @@ -7799,6 +7860,7 @@ "KYL": "Kylin Network", "KYOKO": "Kyoko", "KYRA": "KYRA", + "KYSOL": "Kyros Restaked SOL", "KYTE": "Kambria Yield Tuning Engine", "KYUB": "Kyuubi", "KYVE": "KYVE Network", @@ -7843,6 +7905,7 @@ "LANDLORD": "LANDLORD RONALD", "LANDS": "Two Lands", "LANDV1": "Landshare v1", + "LANDW": "LandWolf", "LANDWOLF": "LANDWOLF", "LANDWOLFETH": "Landwolf", "LANDWU": "LandWu", @@ -7892,6 +7955,7 @@ "LC": "Lotus Capital", "LC4": "LEOcoin", "LCASH": "LitecoinCash", + "LCAT": "Lion Cat", "LCC": "LitecoinCash", "LCD": "Lucidao", "LCG": "LCG", @@ -7955,6 +8019,7 @@ "LEPEN": "LePenCoin", "LEPER": "Leper", "LESBIAN": "Lesbian Inu", + "LESLIE": "Leslie", "LESS": "Less Network", "LESSF": "LessFnGas", "LESTER": "Litecoin Mascot", @@ -7976,6 +8041,7 @@ "LEZGI": "LEZGI Token", "LF": "Linkflow", "LFC": "BigLifeCoin", + "LFDOG": "lifedog", "LFG": "Gamerse", "LFGO": "Lets Fuckin Go", "LFI": "LunaFi", @@ -8024,6 +8090,7 @@ "LIFT": "Uplift", "LIGER": "Ligercoin", "LIGHT": "LightChain", + "LIGHTSPEED": "LightSpeedCoin", "LIGMA": "Ligma Node", "LIGO": "Ligo", "LIKE": "Only1", @@ -8044,6 +8111,7 @@ "LINDACEO": "LindaYacc Ceo", "LINEA": "Linea", "LING": "Lingose", + "LINGO": "Lingo", "LINK": "Chainlink", "LINKA": "LINKA", "LINKC": "LINKCHAIN", @@ -8229,7 +8297,7 @@ "LRT": "LandRocker", "LRT2": "LRT Squared", "LSC": "LS Coin", - "LSD": "LightSpeedCoin", + "LSD": "Pontem Liquidswap", "LSDOGE": "LSDoge", "LSETH": "Liquid Staked ETH", "LSHARE": "LSHARE", @@ -8246,6 +8314,7 @@ "LSWAP": "LoopSwap", "LT": "Loctite Assets Token", "LTA": "Litra", + "LTAI": "LibertAI", "LTB": "Litebar", "LTBC": "LTBCoin", "LTBTC": "Lightning Bitcoin", @@ -8370,6 +8439,7 @@ "M": "MetaVerse-M", "M1": "SupplyShock", "M2O": "M2O Token", + "M3M3": "M3M3", "M87": "MESSIER", "MAAL": "Maal Chain", "MABA": "Make America Based Again", @@ -8467,6 +8537,7 @@ "MARIO": "MARIO CEO", "MARK": "Benchmark Protocol", "MARKE": "Market Ledger", + "MARKETMOVE": "MarketMove", "MARLEY": "Marley Token", "MARMAJ": "marmaj", "MARO": "Maro", @@ -8636,7 +8707,7 @@ "MDUS": "MEDIEUS", "MDX": "Mdex (BSC)", "MDXH": "Mdex (HECO)", - "ME": "All.me", + "ME": "Magic Eden", "MEAN": "Meanfi", "MEB": "Meblox Protocol", "MEC": "MegaCoin", @@ -8743,6 +8814,7 @@ "METAD": "MetaDoge", "METADIUM": "Metadium", "METADOGE": "MetaDoge", + "METADOGEV1": "MetaDoge V1", "METADOGEV2": "MetaDoge V2", "METAF": "MetaFastest", "METAG": "MetagamZ", @@ -8845,6 +8917,7 @@ "MIDAS": "Midas", "MIDASDOLLAR": "Midas Dollar Share", "MIDN": "Midnight", + "MIDNIGHT": "Midnight", "MIE": "MIE Network", "MIF": "monkeywifhat", "MIG": "Migranet", @@ -9111,6 +9184,7 @@ "MONAV": "Monavale", "MONB": "MonbaseCoin", "MONDO": "mondo", + "MONET": "Claude Monet Memeory Coin", "MONETA": "Moneta", "MONEY": "MoneyCoin", "MONEYBEE": "MONEYBEE", @@ -9130,6 +9204,7 @@ "MONKEY": "Monkey", "MONKEYS": "Monkeys Token", "MONKU": "Monku", + "MONKY": "Wise Monkey", "MONO": "MonoX", "MONOLITH": "Monolith", "MONONOKEINU": "Mononoke Inu", @@ -9144,7 +9219,10 @@ "MOOBIFI": "Staked BIFI", "MOOCAT": "MooCat", "MOODENG": "Moo Deng (moodengsol.com)", + "MOODENGBNB": "MOODENG (moodengbnb.com)", + "MOODENGSBS": "Moo Deng (moodeng.sbs)", "MOODENGSPACE": "MOO DENG", + "MOODENGVIP": "MOO DENG (moodeng.vip)", "MOODENGWIF": "MOODENGWIF", "MOOI": "Moonai", "MOOLA": "Degen Forest", @@ -9179,6 +9257,7 @@ "MORE": "More Coin", "MOREGEN": "MoreGen FreeMoon", "MORFEY": "Morfey", + "MORI": "MEMENTO•MORI (Runes)", "MOROS": "MOROS NET", "MORPH": "Morpheus Token", "MORPHO": "Morpho", @@ -9196,7 +9275,7 @@ "MOUTAI": "Moutai", "MOV": "MovieCoin", "MOVD": "MOVE Network", - "MOVE": "MarketMove", + "MOVE": "Movement", "MOVER": "Mover", "MOVEUSD": "MoveMoney USD", "MOVEY": "Movey", @@ -9345,6 +9424,7 @@ "MUN": "MUNcoin", "MUNCH": "Munch Token", "MUNCHY": "Boys Club Munchy", + "MUNDI": "Salvator Mundi", "MUNI": "Uniswap Protocol Token (Multichain)", "MUNITY": "Metahorse Unity", "MUNK": "Dramatic Chipmunk", @@ -9571,6 +9651,7 @@ "NEIROONB": "Neiro on Base", "NEKI": "Neki Token", "NEKO": "The Neko", + "NEKOARC": "Neko Arc", "NEKOIN": "Nekoin", "NEKOS": "Nekocoin", "NEMO": "NEMO", @@ -9730,6 +9811,7 @@ "NIPPY": "Cat On Catnip", "NIQAB": "NIQAB WORLD ORDER", "NIRV": "Nirvana NIRV", + "NIRVA": "Nirvana", "NIT": "Nesten", "NITEFEEDER": "Nitefeeder", "NITO": "Nitroken", @@ -9785,6 +9867,7 @@ "NOIZ": "NOIZ", "NOKA": "Noka Solana AI", "NOKU": "NOKU Master token", + "NOKUV1": "NOKU Master token v1", "NOLA": "Nola", "NOM": "Finom NOM Token", "NOMNOM": "nomnom", @@ -10008,7 +10091,7 @@ "OCTAX": "OctaX", "OCTI": "Oction", "OCTO": "OctoFi", - "OCTOC": "OctoCoin", + "OCTOCOIN": "Octocoin", "OCTOIN": "Octoin Coin", "OCW": "Online Cold Wallet", "OCX": "Original Crypto Coin", @@ -10189,6 +10272,7 @@ "OPS": "Octopus Protocol", "OPSC": "OpenSourceCoin", "OPSEC": "OpSec", + "OPSECV1": "OpSec v1", "OPSV1": "Octopus Protocol v1", "OPSV2": "Octopus Protocol v2", "OPT": "Opus", @@ -10202,6 +10286,7 @@ "OPTION": "OptionCoin", "OPU": "Opu Coin", "OPUL": "Opulous", + "OPUS": "Opus", "OPV": "OpenLive NFT", "OPXVEVELO": "OpenX Locked Velo", "ORA": "Oracolxor", @@ -10413,6 +10498,7 @@ "PARQ": "PARQ", "PARRY": "Parry Parrot", "PART": "Particl", + "PARTY": "Party", "PAS": "Passive Coin", "PASC": "Pascal Coin", "PASG": "Passage", @@ -10460,7 +10546,9 @@ "PBR": "PolkaBridge", "PBRV1": "PolkaBridge v1", "PBT": "Primalbase", + "PBTC": "pTokens BTC", "PBTC35A": "pBTC35A", + "PBTCV1": "pTokens BTC v1", "PBUX": "Playbux", "PBX": "Probinex", "PBXV1": "Probinex v1", @@ -10546,7 +10634,8 @@ "PENDY": "Pendy", "PENG": "Peng", "PENGCOIN": "PENG", - "PENGU": "Penguiana", + "PENGU": "Pudgy Penguins", + "PENGUI": "Penguiana", "PENGYX": "PengyX", "PENIS": "PenisGrow", "PENJ": "Penjamin Blinkerton", @@ -10622,6 +10711,7 @@ "PEPPA": "PEPPA", "PEPPER": "Pepper Token", "PEPS": "PEPS Coin", + "PEPU": "Pepe Unchained", "PEPURAI": "PEPURAI", "PEPVERS": "PepVerse", "PEPY": "Pepy", @@ -10681,7 +10771,6 @@ "PHAUNTEM": "Phauntem", "PHB": "Phoenix Global [v2]", "PHBD": "Polygon HBD", - "PHC": "Profit Hunters Coin", "PHCR": "PhotoChromic", "PHEN": "Phenx", "PHEX": "HEX (Polygon Portal)", @@ -10948,6 +11037,7 @@ "POLYX": "Polymesh", "POM": "Proof Of Memes", "PON": "Ponder", + "PONCH": "Ponchiqs", "PONCHO": "Poncho", "POND": "Marlin", "PONGO": "Pongo", @@ -11021,6 +11111,7 @@ "POWR": "Power Ledger", "POWSCHE": "Powsche", "POX": "Monkey Pox", + "POZO": "Pozo Coin", "PP": "ProducePay Chain", "PPAD": "PlayPad", "PPALPHA": "Phoenix Protocol", @@ -11087,6 +11178,7 @@ "PROB": "ProBit Token", "PROC": "ProCurrency", "PROD": "Productivist", + "PROFITHUNTERS": "Profit Hunters Coin", "PROGE": "Protector Roge", "PROJECT89": "Project89", "PROLIFIC": "Prolific Game Studio", @@ -11313,6 +11405,7 @@ "QLINDO": "QLINDO", "QLIX": "QLix", "QMALL": "QMALL TOKEN", + "QMV": "Qumva Network", "QNT": "Quant", "QNTR": "Quantor", "QNTU": "Quanta", @@ -11357,11 +11450,13 @@ "QUARK": "Quark", "QUARTZ": "Sandclock", "QUASA": "Quasacoin", + "QUASAR": "Quasar", "QUB": "Qubism", "QUBE": "Qube", "QUBIC": "Qubic", "QUBITICA": "Qubitica", "QUBY": "Quby", + "QUDEFI": "Qudefi", "QUE": "Queen Of Memes", "QUEEN": "Queen of Engrand", "QUICK": "Quickswap", @@ -11467,6 +11562,7 @@ "RBLZ": "RebelSatoshi", "RBN": "Ribbon Finance", "RBNB": "StaFi Staked BNB", + "RBNT": "Redbelly Network", "RBP": "Rare Ball Potion", "RBR": "Ribbit Rewards", "RBRETT": "ROARING BRETT", @@ -11483,6 +11579,7 @@ "RCC": "Reality Clash", "RCCC": "RCCC", "RCG": "Recharge", + "RCGE": "RCGE", "RCH": "Rich", "RCKT": "RocketSwap", "RCM": "READ2N", @@ -11707,6 +11804,7 @@ "RIZO": "HahaYes", "RIZOLOL": "Rizo", "RIZZ": "Rizz", + "RIZZMAS": "Rizzmas", "RJV": "Rejuve.AI", "RKC": "Royal Kingdom Coin", "RKI": "RAKHI", @@ -11717,6 +11815,7 @@ "RLC": "iExec", "RLM": "MarbleVerse", "RLOOP": "rLoop", + "RLP": "Resolv RLP", "RLT": "Runner Land", "RLUSD": "Ripple USD", "RLX": "Relex", @@ -11779,6 +11878,7 @@ "RONCOIN": "RON", "ROND": "ROND", "RONIN": "Ronin", + "RONNIE": "Ronnie", "ROO": "Lucky Roo", "ROOBEE": "ROOBEE", "ROOK": "KeeperDAO", @@ -11789,6 +11889,7 @@ "ROOT": "The Root Network", "ROOTCOIN": "RootCoin", "ROOTS": "RootProject", + "ROPIRITO": "Ropirito", "ROS": "ROS Coin", "ROSA": "Rosa Inu", "ROSE": "Oasis Labs", @@ -11854,6 +11955,7 @@ "RSWETH": "Restaked Swell Ethereum", "RT2": "RotoCoin", "RTB": "AB-CHAIN", + "RTBL": "Rolling T-bill", "RTC": "Reltime", "RTD": "Retard", "RTE": "Rate3", @@ -11885,6 +11987,7 @@ "RUNY": "Runy", "RUP": "Rupee", "RUPX": "Rupaya", + "RURI": "Ruri - Truth Terminal's Crush", "RUSD": "Reflecto USD", "RUSH": "RUSH COIN", "RUSHCMC": "RUSHCMC", @@ -11899,6 +12002,7 @@ "RVL": "Revolotto", "RVLNG": "RevolutionGames", "RVLT": "Revolt 2 Earn", + "RVLTV1": "Revolt 2 Earn v1", "RVM": "Realvirm", "RVN": "Ravencoin", "RVO": "AhrvoDEEX", @@ -12006,6 +12110,7 @@ "SANSHU": "Sanshu Inu", "SANTA": "SANTA CHRISTMAS INU", "SANTAGROK": "Santa Grok", + "SANTAHAT": "SANTA HAT", "SANTOS": "Santos FC Fan Token", "SAO": "Sator", "SAP": "SwapAll", @@ -12370,6 +12475,7 @@ "SHINT": "Shiba Interstellar", "SHIP": "ShipChain", "SHIR": "SHIRO", + "SHIRO": "Shiro Neko", "SHIRYOINU": "Shiryo-Inu", "SHISHA": "Shisha Coin", "SHIT": "I will poop it NFT", @@ -12494,10 +12600,12 @@ "SKET": "Sketch coin", "SKEY": "SmartKey", "SKG888": "Safu & Kek Gigafundz 888", - "SKI": "Skillchain", + "SKI": "Ski Mask Dog", "SKIBIDI": "Skibidi Toilet", + "SKICAT": "SKI MASK CAT", "SKID": "Success Kid", "SKILL": "CryptoBlades", + "SKILLC": "Skillchain", "SKIN": "Skincoin", "SKING": "Solo King", "SKINS": "Coins & Skins", @@ -12797,6 +12905,7 @@ "SOONCOIN": "SoonCoin", "SOOTCASE": "I like my sootcase", "SOP": "SoPay", + "SOPH": "Sophon", "SOPHON": "Sophon (Atomicals)", "SOR": "Sorcery", "SORA": "Sora Validator Token", @@ -12806,6 +12915,7 @@ "SORAI": "Sora AI", "SORAPORN": "Sora Porn", "SOSNOVKINO": "Sosnovkino", + "SOSWAP": "Solana Swap", "SOT": "Soccer Crypto", "SOTA": "SOTA Finance", "SOUL": "Phantasma", @@ -12817,6 +12927,7 @@ "SOV": "Sovryn", "SOVE": "Soverain", "SOVI": "Sovi Finance", + "SOVRN": "Sovrun", "SOWA": "Sowa AI", "SOX": "Nobby Game", "SOY": "Soy Finance", @@ -12971,6 +13082,7 @@ "SSB": "SatoshiStreetBets", "SSC": "SelfSell", "SSD": "Sonic Screw Driver Coin", + "SSDX": "SpunkySDX", "SSE": "Soroosh Smart Ecosystem", "SSG": "Surviving Soldiers", "SSGT": "Safeswap", @@ -12997,7 +13109,7 @@ "STABLZ": "Stablz", "STAC": "STAC", "STACK": "StackOS", - "STACKS": "STACKS", + "STACKS": " STACKS PAY", "STACS": "STACS Token", "STAK": "Jigstack", "STAKE": "xDai Chain", @@ -13011,6 +13123,7 @@ "STARAMBA": "Staramba", "STARBASE": "Starbase", "STARC": "StarChain", + "STARDOGE": "StarDOGE", "STARL": "StarLink", "STARLAUNCH": "StarLaunch", "STARLY": "Starly", @@ -13035,18 +13148,22 @@ "STATOKEN": "STA", "STATOM": "Stride Staked ATOM", "STATS": "Stats", + "STAU": "STAU", "STAX": "Staxcoin", "STAY": "NFsTay", "STBOT": "SolTradingBot", + "STBTC": "Lorenzo stBTC", "STBU": "Stobox Token", "STC": "Satoshi Island", "STCN": "Stakecoin", "STD": "STEED", "STDYDX": "Stride Staked DYDX", "STEAK": "SteakHut Finance", + "STEAKUSDC": "Steakhouse USDC Morpho Vault", "STEALTH": "StealthPad", "STEAMPUNK": "SteamPunk", "STEAMX": "Steam Exchange", + "STEEL": "SteelCoin", "STEEM": "Steem", "STEEMD": "Steem Dollars", "STEEP": "SteepCoin", @@ -13092,6 +13209,7 @@ "STMX": "StormX", "STND": "Standard Protocol", "STNEAR": "Staked NEAR", + "STNK": "Stonks", "STO": "Save The Ocean", "STOC": "STO Cash", "STOG": "Stooges", @@ -13176,6 +13294,7 @@ "SUGAR": "Sugar Exchange", "SUI": "Sui", "SUIA": "SUIA", + "SUIAI": "SUI Agents", "SUIB": "Suiba Inu", "SUIJAK": "Suijak", "SUILAMA": "Suilama", @@ -13226,6 +13345,8 @@ "SUPERT": "Super Trump", "SUPERTX": "SuperTX", "SUPR": "SuperDapp", + "SUPRA": "Supra", + "SUPREMEFINANCE": "Hype", "SUR": "Suretly", "SURE": "inSure", "SURF": "Surf.Finance", @@ -13233,6 +13354,7 @@ "SUSD": "sUSD", "SUSDA": "sUSDa", "SUSDE": "Ethena Staked USDe", + "SUSDS": "Savings USDS", "SUSDX": "Staked USDX", "SUSHI": "Sushi", "SUSX": "Savings USX", @@ -13333,6 +13455,8 @@ "SYNCG": "SyncGPT", "SYNCN": "Sync Network", "SYNCO": "Synco", + "SYNDOG": "Synthesizer Dog", + "SYNK": "Synk", "SYNLEV": "SynLev", "SYNO": "Synonym Finance", "SYNR": "MOBLAND", @@ -13395,6 +13519,7 @@ "TARA": "Taraxa", "TARAL": "TARALITY", "TARD": "Tard", + "TARDI": "Tardi", "TARI": "Tari World", "TAROT": "Tarot", "TAROTV1": "Tarot v1", @@ -13414,6 +13539,7 @@ "TAVA": "ALTAVA", "TAX": "MetaToll", "TAXI": "Robotaxi", + "TAXLESSTRUMP": "MAGA TAXLESS", "TBAC": "BlockAura", "TBANK": "TaoBank", "TBAR": "Titanium BAR", @@ -13457,6 +13583,7 @@ "TCY": "The Crypto You", "TD": "The Big Red", "TDAN": "TDAN", + "TDC": "Tidecoin", "TDE": "Trade Ecology Token", "TDEFI": "Token Teknoloji A.S. Token DeFi", "TDFB": "TDFB", @@ -13523,10 +13650,11 @@ "TEST": "Test", "TESTA": "Testa", "TET": "Tectum", - "TETHER": "Hermione", + "TETH": "Treehouse ETH", "TETHYS": "Tethys", "TETRA": "Tetra", "TETU": "TETU", + "TEVA": "Tevaera", "TEW": "Trump in a memes world", "TF47": "Trump Force 47", "TFBX": "Truefeedback Token", @@ -13591,6 +13719,7 @@ "THR": "Thorecoin", "THREE": "Three Protocol Token ", "THRT": "ThriveToken", + "THRUST": "Thruster", "THRY": "THEORY", "THS": "TechShares", "THT": "Thought", @@ -13669,6 +13798,7 @@ "TLP": "TulipCoin", "TLW": "TILWIKI", "TMAGA": "THE MAGA MOVEMENT", + "TMAI": "Token Metrics AI", "TMANIA": "Trump Mania", "TME": "Timereum", "TMED": "MDsquare", @@ -13721,6 +13851,7 @@ "TOMB": "Tomb", "TOMC": "TOM CAT", "TOMI": "tomiNet", + "TOMO": "Tomo Cat", "TOMOE": "TomoChain ERC20", "TOMS": "TomTomCoin", "TON": "Toncoin", @@ -13800,6 +13931,7 @@ "TRAC": "OriginTrail", "TRACE": "Trace Network Labs", "TRACEA": "Trace AI", + "TRACKEDBIO": "TrackedBio", "TRACN": "trac (Ordinals)", "TRADE": "Polytrade", "TRADEBOT": "TradeBot", @@ -13830,7 +13962,7 @@ "TRDT": "Trident", "TREAT": "Treat", "TRECENTO": "Trecento Blockchain Capital", - "TREE": "HyperionX", + "TREE": "Tree", "TREEB": "Retreeb", "TREMP": "Doland Tremp", "TRESTLE": "TRESTLE", @@ -14330,6 +14462,7 @@ "UTKV1": "Utrust", "UTMDOGE": "UltramanDoge", "UTNP": "Universa", + "UTON": "uTON", "UTT": "United Traders Token", "UTU": "UTU Protocol", "UTX": "UTIX", @@ -14363,7 +14496,7 @@ "VALUE": "Value Liquidity", "VAMPIRE": "Vampire Inu", "VAN": "Vanspor Token", - "VANA": "Nirvana", + "VANA": "Vana", "VANCAT": "Vancat", "VANCE": "JD Vance", "VANF": "Van Fwogh", @@ -14523,6 +14656,8 @@ "VITAFAST": "Molecules of Korolchuk IP-NFT", "VITAL": "Vital Network", "VITALI": "Vitalik's Casper", + "VITAMINS": "Vitamins", + "VITARNA": "VitaRNA", "VITE": "VITE", "VITRA": "Vitra Studios", "VITY": "Vitteey", @@ -14547,6 +14682,7 @@ "VLUNA": "Venus Luna", "VLX": "Velas", "VLXPAD": "VelasPad", + "VMANTA": "Bifrost Voucher MANTA", "VMATIC": "Venus MATIC", "VMC": "VirtualMining Coin", "VME": "TrueVett", @@ -14632,6 +14768,7 @@ "VSOL": "VSolidus", "VSP": "Vesper Finance", "VSTA": "Vesta Finance", + "VSTR": "Vestra DAO", "VSUI": "Volo Staked SUI", "VSX": "Vsync", "VSYS": "V Systems", @@ -14668,6 +14805,7 @@ "VXRP": "Venus XRP", "VXT": "Voxto Amplify", "VXV": "Vectorspace AI", + "VY": "Valinity", "VYBE": "Vybe", "VYFI": "VyFinance", "VYNC": "VYNK Chain", @@ -14730,6 +14868,7 @@ "WARPED": "Warped Games", "WARPIE": "Warpie", "WARS": "MetaWars", + "WART": "Warthog", "WAS": "Wasder", "WASABI": "WasabiX", "WASD": "WASD Studios", @@ -14750,6 +14889,7 @@ "WAXS": "Axie Infinity Shards (Wormhole)", "WAY": "WayCoin", "WAZ": "MikeAI", + "WBAN": "Wrapped Banano", "WBB": "Wild Beast Coin", "WBBC": "Wibcoin", "WBC": "WorldBrain Coin", @@ -14828,6 +14968,7 @@ "WELL": "Moonwell", "WELLTOKEN": "Well", "WELLV1": "Moonwell v1", + "WELON": "WrappedElon", "WELSH": "Welshcorgicoin", "WELT": "Fabwelt", "WELUPS": "Welups Blockchain", @@ -14839,6 +14980,7 @@ "WENLAMBO": "Wenlambo", "WEOS": "Wrapped EOS", "WEPC": "World Earn & Play Community", + "WERK": "Werk Family", "WEST": "Waves Enterprise", "WET": "WeShow Token", "WETH": "WETH", @@ -14985,7 +15127,8 @@ "WMN": "WebMind Network", "WMNT": "Wrapped Mantle", "WMOXY": "Moxy", - "WMT": "World Mobile Token", + "WMT": "World Mobile Token v1", + "WMTX": "World Mobile Token", "WMW": "WoopMoney", "WMX": "Wombex Finance", "WMXWOM": "Wombex WOM", @@ -15139,6 +15282,7 @@ "WXRP": "Wrapped XRP", "WXT": "WXT", "WXTZ": "Wrapped Tezos", + "WYAC": "Woman Yelling At Cat", "WYN": "Wynn", "WYNN": "Anita Max Wynn", "WYS": "Wysker", @@ -15168,6 +15312,7 @@ "XALGO": "Wrapped ALGO", "XALPHA": "XAlpha AI", "XAMP": "Antiample", + "XAND": "Xandeum", "XANK": "Xank", "XAP": "Apollon", "XAR": "Arcana Network", @@ -15305,6 +15450,7 @@ "XING": "Xing Xing", "XINU": "XINU", "XIO": "Blockzero Labs", + "XION": "XION", "XIOS": "Xios", "XIOT": "Xiotri", "XIV": "Project Inverse", @@ -15334,6 +15480,7 @@ "XMN": "Motion", "XMO": "Monero Original", "XMON": "XMON", + "XMOON": "r/CryptoCurrency Moons v1", "XMP": "Mapt.Coin", "XMR": "Monero", "XMRG": "Monero Gold", @@ -15418,7 +15565,6 @@ "XRP": "XRP", "XRP2": "XRP2.0", "XRP20": "XRP20", - "XRP8": "HarryPotterObamaPacMan8Inu", "XRPAYNET": "XRPayNet", "XRPC": "Xrp Classic", "XRPCHAIN": "Ripple Chain", @@ -15525,6 +15671,7 @@ "YAYCOIN": "YAYcoin", "YBC": "YbCoin", "YBO": "Young Boys Fan Token", + "YBR": "YieldBricks", "YCC": "Yuan Chain Coin", "YCE": "MYCE", "YCO": "Y Coin", @@ -15538,6 +15685,7 @@ "YEE": "Yeeco", "YEED": "Yggdrash", "YEEHAW": "YEEHAW", + "YEETI": "YEETI 液体", "YEFI": "YeFi", "YEL": "Yel.Finance", "YEON": "Yeon", @@ -15603,6 +15751,7 @@ "YOUNES": "YOUNES", "YOURAI": "YOUR AI", "YOURMOM": "YOUR MOM DOG", + "YOUSIM": "YouSim", "YOVI": "YobitVirtualCoin", "YOYOW": "Yoyow", "YPC": "YoungParrot", @@ -15625,6 +15774,7 @@ "YUKI": "YUKI", "YUKIE": "Yukie", "YUKKY": "YUKKY", + "YUKO": "YUKO", "YUM": "Yumerium", "YUMMI": "Yummi Universe", "YUMMY": "Yummy", @@ -15689,8 +15839,7 @@ "ZEBU": "ZEBU", "ZEC": "ZCash", "ZECD": "ZCashDarkCoin", - "ZED": "ZED Token", - "ZEDCOIN": "ZedCoin", + "ZED": "ZedCoins", "ZEDD": "ZedDex", "ZEDTOKEN": "Zed Token", "ZEDX": "ZEDXION", @@ -15729,9 +15878,11 @@ "ZEUM": "Colizeum", "ZEUS": "Zeus Network", "ZEUSPEPES": "Zeus", + "ZEX": "Zeta", "ZEXI": "ZEXICON", "ZEXY": "ZEXY", "ZF": "zkSwap Finance ", + "ZFI": "Zyfi", "ZFL": "Zuflo Coin", "ZFLOKI": "zkFloki", "ZFM": "ZFMCOIN", @@ -15791,7 +15942,8 @@ "ZKX": "ZKX", "ZKZ": "Zkzone", "ZLA": "Zilla", - "ZLDA": "Zelda Inu", + "ZLDA": "ZELDA 2.0", + "ZLDAV1": "ZELDA v1", "ZLK": "Zenlink Network", "ZLOT": "zLOT Finance", "ZLP": "ZilPay Wallet", @@ -15812,6 +15964,7 @@ "ZODI": "Zodium", "ZOE": "Zoe Cash", "ZOI": "Zoin", + "ZON": "Zon Token", "ZONE": "Zone", "ZONO": "Zono Swap", "ZONX": "METAZONX", 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 ddc04ffcd..30dbe0ae1 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -26,12 +26,11 @@ import { SymbolProfile } from '@prisma/client'; import { format, fromUnixTime, getUnixTime } from 'date-fns'; -import got, { Headers } from 'got'; @Injectable() export class CoinGeckoService implements DataProviderInterface { private readonly apiUrl: string; - private readonly headers: Headers = {}; + private readonly headers: HeadersInit = {}; public constructor( private readonly configurationService: ConfigurationService @@ -69,19 +68,18 @@ export class CoinGeckoService implements DataProviderInterface { }; try { - const { name } = await got(`${this.apiUrl}/coins/${symbol}`, { + const { name } = await fetch(`${this.apiUrl}/coins/${symbol}`, { headers: this.headers, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) - }).json(); + }).then((res) => res.json()); response.name = name; } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -114,7 +112,7 @@ export class CoinGeckoService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const { prices } = await got( + const { prices } = await fetch( `${ this.apiUrl }/coins/${symbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime( @@ -122,10 +120,9 @@ export class CoinGeckoService implements DataProviderInterface { )}&to=${getUnixTime(to)}`, { headers: this.headers, - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); const result: { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; @@ -169,16 +166,15 @@ export class CoinGeckoService implements DataProviderInterface { } try { - const quotes = await got( + const quotes = await fetch( `${this.apiUrl}/simple/price?ids=${symbols.join( ',' )}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}`, { headers: this.headers, - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); for (const symbol in quotes) { response[symbol] = { @@ -192,7 +188,7 @@ export class CoinGeckoService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -212,13 +208,12 @@ export class CoinGeckoService implements DataProviderInterface { let items: LookupItem[] = []; try { - const { coins } = await got(`${this.apiUrl}/search?query=${query}`, { + const { coins } = await fetch(`${this.apiUrl}/search?query=${query}`, { headers: this.headers, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) - }).json(); + }).then((res) => res.json()); items = coins.map(({ id: symbol, name }) => { return { @@ -234,7 +229,7 @@ export class CoinGeckoService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; diff --git a/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts index 3efe7e40d..bb9d0606c 100644 --- a/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts @@ -4,7 +4,6 @@ import { parseSymbol } from '@ghostfolio/common/helper'; import { Injectable } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; -import got, { Headers } from 'got'; @Injectable() export class OpenFigiDataEnhancerService implements DataEnhancerInterface { @@ -32,7 +31,7 @@ export class OpenFigiDataEnhancerService implements DataEnhancerInterface { return response; } - const headers: Headers = {}; + const headers: HeadersInit = {}; const { exchange, ticker } = parseSymbol({ symbol, dataSource: response.dataSource @@ -43,14 +42,20 @@ export class OpenFigiDataEnhancerService implements DataEnhancerInterface { this.configurationService.get('API_KEY_OPEN_FIGI'); } - const mappings = await got - .post(`${OpenFigiDataEnhancerService.baseUrl}/v3/mapping`, { - headers, - json: [{ exchCode: exchange, idType: 'TICKER', idValue: ticker }], - // @ts-ignore + const mappings = (await fetch( + `${OpenFigiDataEnhancerService.baseUrl}/v3/mapping`, + { + body: JSON.stringify([ + { exchCode: exchange, idType: 'TICKER', idValue: ticker } + ]), + headers: { + 'Content-Type': 'application/json', + ...headers + }, + method: 'POST', signal: AbortSignal.timeout(requestTimeout) - }) - .json(); + } + ).then((res) => res.json())) as any[]; if (mappings?.length === 1 && mappings[0].data?.length === 1) { const { compositeFIGI, figi, shareClassFIGI } = mappings[0].data[0]; diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index a4e695284..56b90082d 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -7,7 +7,6 @@ import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { Injectable } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; import { countries } from 'countries-list'; -import got from 'got'; @Injectable() export class TrackinsightDataEnhancerService implements DataEnhancerInterface { @@ -45,27 +44,25 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return response; } - const profile = await got( + const profile = await fetch( `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } ) - .json() + .then((res) => res.json()) .catch(() => { - return got( + return fetch( `${TrackinsightDataEnhancerService.baseUrl}/funds/${ symbol.split('.')?.[0] }.json`, { - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } ) - .json() + .then((res) => res.json()) .catch(() => { return {}; }); @@ -77,29 +74,27 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { response.isin = isin; } - const holdings = await got( + const holdings = await fetch( `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`, { - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } ) - .json() + .then((res) => res.json()) .catch(() => { - return got( + return fetch( `${TrackinsightDataEnhancerService.baseUrl}/holdings/${ symbol.split('.')?.[0] }.json`, { - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } ) - .json() + .then((res) => res.json()) .catch(() => { return {}; }); 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 3ba8eb04f..e427a830a 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 @@ -31,7 +31,6 @@ import { SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay, isToday } from 'date-fns'; -import got from 'got'; import { isNumber } from 'lodash'; @Injectable() @@ -95,7 +94,7 @@ export class EodHistoricalDataService implements DataProviderInterface { [date: string]: IDataProviderHistoricalResponse; } = {}; - const historicalResult = await got( + const historicalResult = await fetch( `${this.URL}/div/${symbol}?api_token=${ this.apiKey }&fmt=json&from=${format(from, DATE_FORMAT)}&to=${format( @@ -103,10 +102,9 @@ export class EodHistoricalDataService implements DataProviderInterface { DATE_FORMAT )}`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); for (const { date, value } of historicalResult) { response[date] = { @@ -140,7 +138,7 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol = this.convertToEodSymbol(symbol); try { - const response = await got( + const response = await fetch( `${this.URL}/eod/${symbol}?api_token=${ this.apiKey }&fmt=json&from=${format(from, DATE_FORMAT)}&to=${format( @@ -148,10 +146,9 @@ export class EodHistoricalDataService implements DataProviderInterface { DATE_FORMAT )}&period=${granularity}`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); return response.reduce( (result, { adjusted_close, date }) => { @@ -205,15 +202,14 @@ export class EodHistoricalDataService implements DataProviderInterface { }); try { - const realTimeResponse = await got( + const realTimeResponse = await fetch( `${this.URL}/real-time/${eodHistoricalDataSymbols[0]}?api_token=${ this.apiKey }&fmt=json&s=${eodHistoricalDataSymbols.join(',')}`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); const quotes: { close: number; @@ -286,7 +282,7 @@ export class EodHistoricalDataService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -400,15 +396,14 @@ export class EodHistoricalDataService implements DataProviderInterface { })[] = []; try { - const response = await got( + const response = await fetch( `${this.URL}/search/${aQuery}?api_token=${this.apiKey}`, { - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json()); searchResult = response.map( ({ Code, Currency, Exchange, ISIN: isin, Name: name, Type }) => { @@ -431,7 +426,7 @@ export class EodHistoricalDataService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to search for ${aQuery} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; 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 933af4802..ed55b3c2f 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 @@ -21,7 +21,6 @@ import { import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { format, isAfter, isBefore, isSameDay } from 'date-fns'; -import got from 'got'; @Injectable() export class FinancialModelingPrepService implements DataProviderInterface { @@ -72,13 +71,12 @@ export class FinancialModelingPrepService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const { historical } = await got( + const { historical } = await fetch( `${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); const result: { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; @@ -124,13 +122,12 @@ export class FinancialModelingPrepService implements DataProviderInterface { } try { - const quotes = await got( + const quotes = await fetch( `${this.URL}/quote/${symbols.join(',')}?apikey=${this.apiKey}`, { - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json()); for (const { price, symbol } of quotes) { response[symbol] = { @@ -144,7 +141,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -164,15 +161,14 @@ export class FinancialModelingPrepService implements DataProviderInterface { let items: LookupItem[] = []; try { - const result = await got( + const result = await fetch( `${this.URL}/search?query=${query}&apikey=${this.apiKey}`, { - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json()); items = result.map(({ currency, name, symbol }) => { return { @@ -187,7 +183,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; diff --git a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts index b6e86949b..a674d479a 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -28,7 +28,6 @@ import { import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; -import got from 'got'; import { StatusCodes } from 'http-status-codes'; @Injectable() @@ -86,17 +85,16 @@ export class GhostfolioService implements DataProviderInterface { } = {}; try { - const { dividends } = await got( + const { dividends } = (await fetch( `${this.URL}/v2/data-providers/ghostfolio/dividends/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( to, DATE_FORMAT )}`, { headers: await this.getRequestHeaders(), - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json())) as DividendsResponse; response = dividends; } catch (error) { @@ -130,17 +128,16 @@ export class GhostfolioService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const { historicalData } = await got( + const { historicalData } = (await fetch( `${this.URL}/v2/data-providers/ghostfolio/historical/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( to, DATE_FORMAT )}`, { headers: await this.getRequestHeaders(), - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json())) as HistoricalResponse; return { [symbol]: historicalData @@ -192,20 +189,19 @@ export class GhostfolioService implements DataProviderInterface { } try { - const { quotes } = await got( + const { quotes } = (await fetch( `${this.URL}/v2/data-providers/ghostfolio/quotes?symbols=${symbols.join(',')}`, { headers: await this.getRequestHeaders(), - // @ts-ignore signal: AbortSignal.timeout(requestTimeout) } - ).json(); + ).then((res) => res.json())) as QuotesResponse; response = quotes; } catch (error) { let message = error; - if (error.code === 'ABORT_ERR') { + if (error.name === 'AbortError') { message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -235,20 +231,19 @@ export class GhostfolioService implements DataProviderInterface { let searchResult: LookupResponse = { items: [] }; try { - searchResult = await got( + searchResult = (await fetch( `${this.URL}/v2/data-providers/ghostfolio/lookup?query=${query}`, { headers: await this.getRequestHeaders(), - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json())) as LookupResponse; } catch (error) { let message = error; - if (error.code === 'ABORT_ERR') { + if (error.name === 'AbortError') { message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; 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 3e98a9d77..9ba84d6d3 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -27,7 +27,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import * as cheerio from 'cheerio'; import { addDays, format, isBefore } from 'date-fns'; -import got, { Headers } from 'got'; import * as jsonpath from 'jsonpath'; @Injectable() @@ -276,23 +275,22 @@ export class ManualService implements DataProviderInterface { ): Promise { try { let locale = scraperConfiguration.locale; - const { body, headers } = await got(scraperConfiguration.url, { - headers: scraperConfiguration.headers as Headers, - // @ts-ignore + const response = await fetch(scraperConfiguration.url, { + headers: scraperConfiguration.headers as HeadersInit, signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) }); - if (headers['content-type'].includes('application/json')) { - const data = JSON.parse(body); + if (response.headers['content-type'].includes('application/json')) { + const data = await response.json(); const value = String( jsonpath.query(data, scraperConfiguration.selector)[0] ); return extractNumberFromString({ locale, value }); } else { - const $ = cheerio.load(body); + const $ = cheerio.load(await response.text()); if (!locale) { try { 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 08cc2ef05..4c9bb2717 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 @@ -20,7 +20,6 @@ import { import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; -import got from 'got'; @Injectable() export class RapidApiService implements DataProviderInterface { @@ -135,7 +134,7 @@ export class RapidApiService implements DataProviderInterface { oneYearAgo: { value: number; valueText: string }; }> { try { - const { fgi } = await got( + const { fgi } = await fetch( `https://fear-and-greed-index.p.rapidapi.com/v1/fgi`, { headers: { @@ -143,18 +142,17 @@ export class RapidApiService implements DataProviderInterface { 'x-rapidapi-host': 'fear-and-greed-index.p.rapidapi.com', 'x-rapidapi-key': this.configurationService.get('API_KEY_RAPID_API') }, - // @ts-ignore signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) } - ).json(); + ).then((res) => res.json()); return fgi; } catch (error) { let message = error; - if (error?.code === 'ABORT_ERR') { + if (error?.name === 'AbortError') { message = `RequestError: The operation was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; diff --git a/apps/client/src/app/components/access-table/access-table.component.html b/apps/client/src/app/components/access-table/access-table.component.html index 2a20e4631..44aee1644 100644 --- a/apps/client/src/app/components/access-table/access-table.component.html +++ b/apps/client/src/app/components/access-table/access-table.component.html @@ -66,7 +66,7 @@ @if (element.type === 'PUBLIC') { -
diff --git a/apps/client/src/app/components/access-table/access-table.component.ts b/apps/client/src/app/components/access-table/access-table.component.ts index da8ceb094..32ae7bfef 100644 --- a/apps/client/src/app/components/access-table/access-table.component.ts +++ b/apps/client/src/app/components/access-table/access-table.component.ts @@ -12,6 +12,7 @@ import { OnChanges, Output } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableDataSource } from '@angular/material/table'; @Component({ @@ -34,7 +35,8 @@ export class AccessTableComponent implements OnChanges { public constructor( private clipboard: Clipboard, - private notificationService: NotificationService + private notificationService: NotificationService, + private snackBar: MatSnackBar ) {} public ngOnChanges() { @@ -55,8 +57,16 @@ export class AccessTableComponent implements OnChanges { return `${this.baseUrl}/${languageCode}/p/${aId}`; } - public onCopyToClipboard(aId: string): void { + public onCopyUrlToClipboard(aId: string): void { this.clipboard.copy(this.getPublicUrl(aId)); + + this.snackBar.open( + '✅ ' + $localize`Link has been copied to the clipboard`, + undefined, + { + duration: 3000 + } + ); } public onDeleteAccess(aId: string) { diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 506a46a31..271a5cd53 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -1,7 +1,5 @@ import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; -import { DataService } from '@ghostfolio/client/services/data.service'; -import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { translate } from '@ghostfolio/ui/i18n'; @@ -24,6 +22,8 @@ import { isAfter, isToday } from 'date-fns'; import { EMPTY, Observable, Subject, lastValueFrom, of } from 'rxjs'; import { catchError, delay, map, startWith, takeUntil } from 'rxjs/operators'; +import { DataService } from '../../../../services/data.service'; +import { validateObjectForForm } from '../../../../util/form.util'; import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces'; @Component({ @@ -124,7 +124,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { name: [this.data.activity?.SymbolProfile?.name, Validators.required], quantity: [this.data.activity?.quantity, Validators.required], searchSymbol: [ - !!this.data.activity?.SymbolProfile + this.data.activity?.SymbolProfile ? { dataSource: this.data.activity?.SymbolProfile?.dataSource, symbol: this.data.activity?.SymbolProfile?.symbol @@ -476,7 +476,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { fee: this.activityForm.get('fee').value, quantity: this.activityForm.get('quantity').value, symbol: - this.activityForm.get('searchSymbol')?.value?.symbol ?? + (['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes( + this.activityForm.get('type').value + ) + ? undefined + : this.activityForm.get('searchSymbol')?.value?.symbol) ?? this.activityForm.get('name')?.value, tags: this.activityForm.get('tags').value, type: this.activityForm.get('type').value, @@ -485,8 +489,9 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { try { if (this.mode === 'create') { - (activity as CreateOrderDto).updateAccountBalance = - this.activityForm.get('updateAccountBalance').value; + activity.updateAccountBalance = this.activityForm.get( + 'updateAccountBalance' + ).value; await validateObjectForForm({ classDto: CreateOrderDto, @@ -495,7 +500,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { object: activity }); - this.dialogRef.close(activity as CreateOrderDto); + this.dialogRef.close(activity); } else { (activity as UpdateOrderDto).id = this.data.activity.id; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 0bd4e85e3..7e27a05f9 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -11,10 +11,13 @@ import { ToggleOption, User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GroupBy } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; +import { Clipboard } from '@angular/cdk/clipboard'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { SymbolProfile } from '@prisma/client'; import { isNumber, sortBy } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; @@ -38,6 +41,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public dividendTimelineDataLabel = $localize`Dividend`; public firstOrderDate: Date; public hasImpersonationId: boolean; + public hasPermissionToReadAiPrompt: boolean; public investments: InvestmentItem[]; public investmentTimelineDataLabel = $localize`Investment`; public investmentsByGroup: InvestmentItem[]; @@ -64,9 +68,11 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public constructor( private changeDetectorRef: ChangeDetectorRef, + private clipboard: Clipboard, private dataService: DataService, private deviceService: DeviceDetectorService, private impersonationStorageService: ImpersonationStorageService, + private snackBar: MatSnackBar, private userService: UserService ) { const { benchmarks } = this.dataService.fetchInfo(); @@ -104,6 +110,11 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { return id === this.user.settings?.benchmark; }); + this.hasPermissionToReadAiPrompt = hasPermission( + this.user.permissions, + permissions.readAiPrompt + ); + this.update(); } }); @@ -130,6 +141,20 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { this.fetchDividendsAndInvestments(); } + public onCopyPromptToClipboard() { + this.dataService.fetchPrompt().subscribe(({ prompt }) => { + this.clipboard.copy(prompt); + + this.snackBar.open( + '✅ ' + $localize`AI prompt has been copied to the clipboard`, + undefined, + { + duration: 3000 + } + ); + }); + } + public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 087b3bd54..07ffa705d 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -1,5 +1,37 @@

Analysis

+ @if (user?.settings?.isExperimentalFeatures) { +
+
+
+ + + + +
+
+
+ }
('/api/v1/portfolio/report'); } + public fetchPrompt() { + return this.http.get('/api/v1/ai/prompt'); + } + public fetchPublicPortfolio(aAccessId: string) { return this.http .get(`/api/v1/public/${aAccessId}/portfolio`) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 9981f2a6b..1b7d0cbaf 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -5067,7 +5067,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -5079,11 +5079,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5095,7 +5095,7 @@ Monthly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -5103,7 +5103,7 @@ Yearly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -5119,7 +5119,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -5127,7 +5127,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -5135,7 +5135,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -5143,7 +5143,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -5151,7 +5151,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -5159,7 +5159,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -5167,7 +5167,7 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -5175,7 +5175,7 @@ Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -5183,7 +5183,7 @@ Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -5191,7 +5191,7 @@ Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -5199,7 +5199,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -5207,7 +5207,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -5215,7 +5215,7 @@ Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index fe4be3713..d8f41758d 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -2146,7 +2146,7 @@ Zeitstrahl der Investitionen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -2154,7 +2154,7 @@ Gewinner apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2162,7 +2162,7 @@ Verlierer apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2806,7 +2806,7 @@ Monatlich apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2998,7 +2998,7 @@ Portfolio Wertentwicklung apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -3314,7 +3314,7 @@ Zeitstrahl der Dividenden apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3322,7 +3322,7 @@ Dividenden apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -3430,7 +3430,7 @@ Jährlich apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4174,7 +4174,7 @@ Aktueller Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4182,7 +4182,7 @@ Längster Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6311,11 +6311,11 @@ Einlage apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Absolute Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Absolute Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Absolute Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + KI-Anweisung wurde in die Zwischenablage kopiert + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Kopiere KI-Anweisung die Zwischenablage + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 56bf700b0..581d28818 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -2147,7 +2147,7 @@ Cronología de la inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -2155,7 +2155,7 @@ Lo mejor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2163,7 +2163,7 @@ Lo peor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2835,7 +2835,7 @@ Mensual apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2999,7 +2999,7 @@ Evolución cartera apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -3315,7 +3315,7 @@ Dividendo apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -3327,7 +3327,7 @@ Calendario de dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3431,7 +3431,7 @@ Anual apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4175,7 +4175,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4183,7 +4183,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6312,11 +6312,11 @@ Inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6328,7 +6328,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6336,7 +6336,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6344,7 +6344,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6352,7 +6352,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6360,7 +6360,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6368,7 +6368,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7637,6 +7637,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 4159e4645..8ae14e5d9 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -2746,7 +2746,7 @@ Dividende apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -2766,7 +2766,7 @@ Mensuel apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2782,7 +2782,7 @@ Haut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2790,7 +2790,7 @@ Bas apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2798,7 +2798,7 @@ Évolution du Portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -2806,7 +2806,7 @@ Historique des Investissements apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -2814,7 +2814,7 @@ Historique des Dividendes apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3430,7 +3430,7 @@ Annuel apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4174,7 +4174,7 @@ Série en cours apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4182,7 +4182,7 @@ Série la plus longue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6311,11 +6311,11 @@ Investissement apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Performance des Actifs en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Performance des Actifs apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Performance des devises en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Performance des devises apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Performance nette absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Performance nette apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 8423c6f67..b2dc49ce6 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -2147,7 +2147,7 @@ Cronologia degli investimenti apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -2155,7 +2155,7 @@ In alto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2163,7 +2163,7 @@ In basso apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2835,7 +2835,7 @@ Mensile apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2999,7 +2999,7 @@ Evoluzione del portafoglio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -3315,7 +3315,7 @@ Dividendi apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -3327,7 +3327,7 @@ Cronologia dei dividendi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3431,7 +3431,7 @@ Annuale apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4175,7 +4175,7 @@ Serie attuale apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4183,7 +4183,7 @@ Serie più lunga apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6312,11 +6312,11 @@ Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6328,7 +6328,7 @@ Rendimento assoluto dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6336,7 +6336,7 @@ Rendimento dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6344,7 +6344,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6352,7 +6352,7 @@ Rendimento della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6360,7 +6360,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6368,7 +6368,7 @@ Rendimento Netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7637,6 +7637,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 71e3d1f5f..aef90c10c 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -2146,7 +2146,7 @@ Tijdlijn investeringen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -2154,7 +2154,7 @@ Winnaars apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2162,7 +2162,7 @@ Verliezers apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2834,7 +2834,7 @@ Maandelijks apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2998,7 +2998,7 @@ Waardeontwikkeling van portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -3314,7 +3314,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -3326,7 +3326,7 @@ Tijdlijn dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3430,7 +3430,7 @@ Jaarlijks apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4174,7 +4174,7 @@ Huidige reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4182,7 +4182,7 @@ Langste reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6311,11 +6311,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index d2a4090e7..f17bd92c3 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -4659,7 +4659,7 @@ Dywidenda apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -4679,7 +4679,7 @@ Miesięcznie apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -4687,7 +4687,7 @@ Rocznie apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4703,7 +4703,7 @@ Największe wzrosty apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -4711,7 +4711,7 @@ Największy spadek apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -4719,7 +4719,7 @@ Rozwój portfela apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -4727,7 +4727,7 @@ Oś czasu inwestycji apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -4735,7 +4735,7 @@ Obecna passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4743,7 +4743,7 @@ Najdłuższa passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -4751,7 +4751,7 @@ Oś czasu dywidend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -6311,11 +6311,11 @@ Inwestycje apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Łączny wynik aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Wyniki aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Łączny wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Łączna wartość netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Wynik netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index f8034354b..3455cef82 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -2654,7 +2654,7 @@ Mensalmente apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -2670,7 +2670,7 @@ Topo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -2678,7 +2678,7 @@ Fundo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -2686,7 +2686,7 @@ Evolução do Portefólio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -2694,7 +2694,7 @@ Cronograma de Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -3382,7 +3382,7 @@ Dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -3394,7 +3394,7 @@ Cronograma de Dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -3430,7 +3430,7 @@ Anualmente apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4174,7 +4174,7 @@ Série Atual apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4182,7 +4182,7 @@ Série mais Longa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -6311,11 +6311,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 62e9608f5..b45449ba4 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -4147,7 +4147,7 @@ Temettü apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -4167,7 +4167,7 @@ Aylık apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -4175,7 +4175,7 @@ Yıllık apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4191,7 +4191,7 @@ Üst apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -4199,7 +4199,7 @@ Alt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -4207,7 +4207,7 @@ Portföyün Gelişimi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -4215,7 +4215,7 @@ Yatırım Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -4223,7 +4223,7 @@ Güncel Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4231,7 +4231,7 @@ En Uzun Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -4239,7 +4239,7 @@ Temettü Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -6311,11 +6311,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6327,7 +6327,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6335,7 +6335,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6343,7 +6343,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6351,7 +6351,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6359,7 +6359,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6367,7 +6367,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -7636,6 +7636,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index 03def3439..47ca44e51 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -4286,7 +4286,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -4304,14 +4304,14 @@ Monthly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 Yearly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4325,49 +4325,49 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -5743,32 +5743,32 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5779,21 +5779,21 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -6908,6 +6908,20 @@ 59 + + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + + + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 44ba35788..aacb0049c 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -4676,7 +4676,7 @@ 股息 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 38 + 41 libs/ui/src/lib/i18n.ts @@ -4696,7 +4696,7 @@ 每月 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 50 + 54 @@ -4704,7 +4704,7 @@ 每年 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 51 + 55 @@ -4720,7 +4720,7 @@ 顶部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 165 + 197 @@ -4728,7 +4728,7 @@ 底部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 214 + 246 @@ -4736,7 +4736,7 @@ 投资组合演变 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 267 + 299 @@ -4744,7 +4744,7 @@ 投资时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 294 + 326 @@ -4752,7 +4752,7 @@ 当前连胜 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 315 + 347 @@ -4760,7 +4760,7 @@ 最长连续纪录 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 324 + 356 @@ -4768,7 +4768,7 @@ 股息时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 351 + 383 @@ -6312,7 +6312,7 @@ 绝对货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 71 + 103 @@ -6320,7 +6320,7 @@ 绝对净性能 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 118 + 150 @@ -6328,7 +6328,7 @@ 绝对资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 60 @@ -6336,11 +6336,11 @@ 投资 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 46 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 60 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6352,7 +6352,7 @@ 资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 49 + 81 @@ -6360,7 +6360,7 @@ 净绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 137 + 169 @@ -6368,7 +6368,7 @@ 货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 95 + 127 @@ -7637,6 +7637,22 @@ 59 + + AI prompt has been copied to the clipboard + AI prompt has been copied to the clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts + 149 + + + + Copy AI prompt to clipboard + Copy AI prompt to clipboard + + apps/client/src/app/pages/portfolio/analysis/analysis-page.html + 27 + + diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index fa5eb25a5..7ad4948dc 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -38,6 +38,7 @@ import type { PortfolioSummary } from './portfolio-summary.interface'; import type { Position } from './position.interface'; import type { Product } from './product'; import type { AccountBalancesResponse } from './responses/account-balances-response.interface'; +import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; import type { BenchmarkResponse } from './responses/benchmark-response.interface'; import type { DataProviderGhostfolioStatusResponse } from './responses/data-provider-ghostfolio-status-response.interface'; @@ -74,6 +75,7 @@ export { AdminMarketDataDetails, AdminMarketDataItem, AdminUsers, + AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, Benchmark, diff --git a/libs/common/src/lib/interfaces/responses/ai-prompt-response.interface.ts b/libs/common/src/lib/interfaces/responses/ai-prompt-response.interface.ts new file mode 100644 index 000000000..4b95bc871 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/ai-prompt-response.interface.ts @@ -0,0 +1,3 @@ +export interface AiPromptResponse { + prompt: string; +} diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index d6676ec4e..d19b8daf0 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -35,6 +35,7 @@ export const permissions = { enableSubscriptionInterstitial: 'enableSubscriptionInterstitial', enableSystemMessage: 'enableSystemMessage', impersonateAllUsers: 'impersonateAllUsers', + readAiPrompt: 'readAiPrompt', readMarketData: 'readMarketData', readMarketDataOfOwnAssetProfile: 'readMarketDataOfOwnAssetProfile', readPlatforms: 'readPlatforms', @@ -76,6 +77,7 @@ export function getPermissions(aRole: Role): string[] { permissions.deletePlatform, permissions.deleteTag, permissions.deleteUser, + permissions.readAiPrompt, permissions.readMarketData, permissions.readMarketDataOfOwnAssetProfile, permissions.readPlatforms, @@ -95,7 +97,8 @@ export function getPermissions(aRole: Role): string[] { return [ permissions.accessAssistant, permissions.accessHoldingsChart, - permissions.createUserAccount + permissions.createUserAccount, + permissions.readAiPrompt ]; case 'USER': @@ -113,6 +116,7 @@ export function getPermissions(aRole: Role): string[] { permissions.deleteAuthDevice, permissions.deleteOrder, permissions.deleteOwnUser, + permissions.readAiPrompt, permissions.readMarketDataOfOwnAssetProfile, permissions.updateAccount, permissions.updateAuthDevice, diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts index 637ee3bd9..0ad868ff9 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts @@ -4,47 +4,48 @@ import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import '@angular/localize/init'; import { MatButtonModule } from '@angular/material/button'; +import { provideNativeDateAdapter } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { GfValueModule } from '../value'; -import { FireCalculatorComponent } from './fire-calculator.component'; +import { GfValueComponent } from '../value'; +import { GfFireCalculatorComponent } from './fire-calculator.component'; import { FireCalculatorService } from './fire-calculator.service'; export default { title: 'FIRE Calculator', - component: FireCalculatorComponent, + component: GfFireCalculatorComponent, decorators: [ moduleMetadata({ - declarations: [FireCalculatorComponent], imports: [ CommonModule, FormsModule, - GfValueModule, + GfFireCalculatorComponent, + GfValueComponent, MatButtonModule, + MatDatepickerModule, MatFormFieldModule, MatInputModule, NgxSkeletonLoaderModule, NoopAnimationsModule, ReactiveFormsModule ], - providers: [FireCalculatorService] + providers: [FireCalculatorService, provideNativeDateAdapter()] }) ] -} as Meta; +} as Meta; -const Template: Story = ( - args: FireCalculatorComponent -) => ({ - props: args -}); +type Story = StoryObj; -export const Simple = Template.bind({}); -Simple.args = { - currency: 'USD', - fireWealth: 0, - locale: locale +export const Simple: Story = { + args: { + currency: 'USD', + fireWealth: 0, + locale: locale + } }; diff --git a/libs/ui/src/lib/line-chart/line-chart.component.stories.ts b/libs/ui/src/lib/line-chart/line-chart.component.stories.ts index 59580a136..9770074d2 100644 --- a/libs/ui/src/lib/line-chart/line-chart.component.stories.ts +++ b/libs/ui/src/lib/line-chart/line-chart.component.stories.ts @@ -1,235 +1,234 @@ import { CommonModule } from '@angular/common'; -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { LineChartComponent } from './line-chart.component'; +import { GfLineChartComponent } from './line-chart.component'; export default { title: 'Line Chart', - component: LineChartComponent, + component: GfLineChartComponent, decorators: [ moduleMetadata({ - declarations: [LineChartComponent], - imports: [CommonModule, NgxSkeletonLoaderModule] + imports: [CommonModule, GfLineChartComponent, NgxSkeletonLoaderModule] }) ] -} as Meta; +} as Meta; -const Template: Story = (args: LineChartComponent) => ({ - props: args -}); +type Story = StoryObj; -export const Simple = Template.bind({}); -Simple.args = { - historicalDataItems: [ - { - date: '2017-01-01', - value: 2561.60376 - }, - { - date: '2017-02-01', - value: 2261.60376 - }, - { - date: '2017-03-01', - value: 2268.68157074 - }, - { - date: '2017-04-01', - value: 2525.2942 - }, - { - date: '2017-05-01', - value: 2929.3595107399997 - }, - { - date: '2017-06-01', - value: 3088.5172438900004 - }, - { - date: '2017-07-01', - value: 3281.2490946300004 - }, - { - date: '2017-08-01', - value: 4714.57822537 - }, - { - date: '2017-09-01', - value: 5717.262455259565 - }, - { - date: '2017-10-01', - value: 5338.742482334544 - }, - { - date: '2017-11-01', - value: 6361.263771554509 - }, - { - date: '2017-12-01', - value: 8373.260491904595 - }, - { - date: '2018-01-01', - value: 9783.208968191562 - }, - { - date: '2018-02-01', - value: 7841.2667100173 - }, - { - date: '2018-03-01', - value: 8582.133039380678 - }, - { - date: '2018-04-01', - value: 5901.8362986766715 - }, - { - date: '2018-05-01', - value: 7367.392976151925 - }, - { - date: '2018-06-01', - value: 6490.164314049853 - }, - { - date: '2018-07-01', - value: 6365.351621654618 - }, - { - date: '2018-08-01', - value: 6614.532706931272 - }, - { - date: '2018-09-01', - value: 6402.052882414409 - }, - { - date: '2018-10-01', - value: 15270.327917651943 - }, - { - date: '2018-11-01', - value: 13929.833891940816 - }, - { - date: '2018-12-01', - value: 12995.832254431414 - }, - { - date: '2019-01-01', - value: 11792.447013828998 - }, - { - date: '2019-02-01', - value: 11988.224284346446 - }, - { - date: '2019-03-01', - value: 13536.39667099519 - }, - { - date: '2019-04-01', - value: 14301.83740090022 - }, - { - date: '2019-05-01', - value: 14902.994910520581 - }, - { - date: '2019-06-01', - value: 15373.418326284132 - }, - { - date: '2019-07-01', - value: 17545.70705465703 - }, - { - date: '2019-08-01', - value: 17206.28500223782 - }, - { - date: '2019-09-01', - value: 17782.445200108898 - }, - { - date: '2019-10-01', - value: 17050.25875252655 - }, - { - date: '2019-11-01', - value: 18517.053521416237 - }, - { - date: '2019-12-01', - value: 17850.649021651363 - }, - { - date: '2020-01-01', - value: 18817.269786559067 - }, - { - date: '2020-02-01', - value: 22769.842312027653 - }, - { - date: '2020-03-01', - value: 23065.56002316582 - }, - { - date: '2020-04-01', - value: 19738.122641884733 - }, - { - date: '2020-05-01', - value: 25112.281463810643 - }, - { - date: '2020-06-01', - value: 28753.054132147452 - }, - { - date: '2020-07-01', - value: 32207.62827031543 - }, - { - date: '2020-08-01', - value: 37837.88816828611 - }, - { - date: '2020-09-01', - value: 50018.740185519295 - }, - { - date: '2020-10-01', - value: 46593.322295801525 - }, - { - date: '2020-11-01', - value: 44440.18743231742 - }, - { - date: '2020-12-01', - value: 57582.23077536893 - }, - { - date: '2021-01-01', - value: 68823.02446077733 - }, - { - date: '2021-02-01', - value: 64516.42092139593 - }, - { - date: '2021-03-01', - value: 82465.97581106682 - }, - { - date: '2021-03-18', - value: 86666.03082624623 - } - ], - isAnimated: true +export const Simple: Story = { + args: { + historicalDataItems: [ + { + date: '2017-01-01', + value: 2561.60376 + }, + { + date: '2017-02-01', + value: 2261.60376 + }, + { + date: '2017-03-01', + value: 2268.68157074 + }, + { + date: '2017-04-01', + value: 2525.2942 + }, + { + date: '2017-05-01', + value: 2929.3595107399997 + }, + { + date: '2017-06-01', + value: 3088.5172438900004 + }, + { + date: '2017-07-01', + value: 3281.2490946300004 + }, + { + date: '2017-08-01', + value: 4714.57822537 + }, + { + date: '2017-09-01', + value: 5717.262455259565 + }, + { + date: '2017-10-01', + value: 5338.742482334544 + }, + { + date: '2017-11-01', + value: 6361.263771554509 + }, + { + date: '2017-12-01', + value: 8373.260491904595 + }, + { + date: '2018-01-01', + value: 9783.208968191562 + }, + { + date: '2018-02-01', + value: 7841.2667100173 + }, + { + date: '2018-03-01', + value: 8582.133039380678 + }, + { + date: '2018-04-01', + value: 5901.8362986766715 + }, + { + date: '2018-05-01', + value: 7367.392976151925 + }, + { + date: '2018-06-01', + value: 6490.164314049853 + }, + { + date: '2018-07-01', + value: 6365.351621654618 + }, + { + date: '2018-08-01', + value: 6614.532706931272 + }, + { + date: '2018-09-01', + value: 6402.052882414409 + }, + { + date: '2018-10-01', + value: 15270.327917651943 + }, + { + date: '2018-11-01', + value: 13929.833891940816 + }, + { + date: '2018-12-01', + value: 12995.832254431414 + }, + { + date: '2019-01-01', + value: 11792.447013828998 + }, + { + date: '2019-02-01', + value: 11988.224284346446 + }, + { + date: '2019-03-01', + value: 13536.39667099519 + }, + { + date: '2019-04-01', + value: 14301.83740090022 + }, + { + date: '2019-05-01', + value: 14902.994910520581 + }, + { + date: '2019-06-01', + value: 15373.418326284132 + }, + { + date: '2019-07-01', + value: 17545.70705465703 + }, + { + date: '2019-08-01', + value: 17206.28500223782 + }, + { + date: '2019-09-01', + value: 17782.445200108898 + }, + { + date: '2019-10-01', + value: 17050.25875252655 + }, + { + date: '2019-11-01', + value: 18517.053521416237 + }, + { + date: '2019-12-01', + value: 17850.649021651363 + }, + { + date: '2020-01-01', + value: 18817.269786559067 + }, + { + date: '2020-02-01', + value: 22769.842312027653 + }, + { + date: '2020-03-01', + value: 23065.56002316582 + }, + { + date: '2020-04-01', + value: 19738.122641884733 + }, + { + date: '2020-05-01', + value: 25112.281463810643 + }, + { + date: '2020-06-01', + value: 28753.054132147452 + }, + { + date: '2020-07-01', + value: 32207.62827031543 + }, + { + date: '2020-08-01', + value: 37837.88816828611 + }, + { + date: '2020-09-01', + value: 50018.740185519295 + }, + { + date: '2020-10-01', + value: 46593.322295801525 + }, + { + date: '2020-11-01', + value: 44440.18743231742 + }, + { + date: '2020-12-01', + value: 57582.23077536893 + }, + { + date: '2021-01-01', + value: 68823.02446077733 + }, + { + date: '2021-02-01', + value: 64516.42092139593 + }, + { + date: '2021-03-01', + value: 82465.97581106682 + }, + { + date: '2021-03-18', + value: 86666.03082624623 + } + ], + isAnimated: true + } }; diff --git a/libs/ui/src/lib/logo/logo.component.stories.ts b/libs/ui/src/lib/logo/logo.component.stories.ts index 196043eb3..c720ebd92 100644 --- a/libs/ui/src/lib/logo/logo.component.stories.ts +++ b/libs/ui/src/lib/logo/logo.component.stories.ts @@ -1,30 +1,32 @@ -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; -import { LogoComponent } from './logo.component'; +import { GfLogoComponent } from './logo.component'; export default { title: 'Logo', - component: LogoComponent, + component: GfLogoComponent, decorators: [ moduleMetadata({ imports: [] }) ] -} as Meta; +} as Meta; -const Template: Story = (args: LogoComponent) => ({ - props: args -}); +type Story = StoryObj; -export const Default = Template.bind({}); -Default.args = {}; +export const Default: Story = { + args: {} +}; -export const Large = Template.bind({}); -Large.args = { - size: 'large' +export const Large: Story = { + args: { + size: 'large' + } }; -export const NoLabel = Template.bind({}); -NoLabel.args = { - showLabel: false +export const NoLabel: Story = { + args: { + showLabel: false + } }; diff --git a/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.stories.ts b/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.stories.ts index 13a3404ee..faf536244 100644 --- a/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.stories.ts +++ b/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.stories.ts @@ -1,25 +1,23 @@ -import { GfLogoModule } from '@ghostfolio/ui/logo'; +import { GfLogoComponent } from '@ghostfolio/ui/logo'; import { RouterTestingModule } from '@angular/router/testing'; -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; -import { NoTransactionsInfoComponent } from './no-transactions-info.component'; +import { GfNoTransactionsInfoComponent } from './no-transactions-info.component'; export default { title: 'No Transactions Info', - component: NoTransactionsInfoComponent, + component: GfNoTransactionsInfoComponent, decorators: [ moduleMetadata({ - imports: [GfLogoModule, RouterTestingModule] + imports: [GfLogoComponent, RouterTestingModule] }) ] -} as Meta; +} as Meta; -const Template: Story = ( - args: NoTransactionsInfoComponent -) => ({ - props: args -}); +type Story = StoryObj; -export const Default = Template.bind({}); -Default.args = {}; +export const Default: Story = { + args: {} +}; diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts index 7d6db1b52..90aa0cee8 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.stories.ts @@ -1,38 +1,39 @@ import { CommonModule } from '@angular/common'; import '@angular/localize/init'; -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { PortfolioProportionChartComponent } from './portfolio-proportion-chart.component'; +import { GfPortfolioProportionChartComponent } from './portfolio-proportion-chart.component'; export default { title: 'Portfolio Proportion Chart', - component: PortfolioProportionChartComponent, + component: GfPortfolioProportionChartComponent, decorators: [ moduleMetadata({ - declarations: [PortfolioProportionChartComponent], - imports: [CommonModule, NgxSkeletonLoaderModule] + imports: [ + CommonModule, + GfPortfolioProportionChartComponent, + NgxSkeletonLoaderModule + ] }) ] -} as Meta; +} as Meta; -const Template: Story = ( - args: PortfolioProportionChartComponent -) => ({ - props: args -}); +type Story = StoryObj; -export const Simple = Template.bind({}); -Simple.args = { - baseCurrency: 'USD', - keys: ['name'], - locale: 'en-US', - positions: { - Africa: { name: 'Africa', value: 983.22461479889288 }, - Asia: { name: 'Asia', value: 12074.754633964973 }, - Europe: { name: 'Europe', value: 34432.837085290535 }, - 'North America': { name: 'North America', value: 26539.89987780503 }, - Oceania: { name: 'Oceania', value: 1402.220605072031 }, - 'South America': { name: 'South America', value: 4938.25202180719859 } +export const Simple: Story = { + args: { + baseCurrency: 'USD', + keys: ['name'], + locale: 'en-US', + positions: { + Africa: { name: 'Africa', value: 983.22461479889288 }, + Asia: { name: 'Asia', value: 12074.754633964973 }, + Europe: { name: 'Europe', value: 34432.837085290535 }, + 'North America': { name: 'North America', value: 26539.89987780503 }, + Oceania: { name: 'Oceania', value: 1402.220605072031 }, + 'South America': { name: 'South America', value: 4938.25202180719859 } + } } }; diff --git a/libs/ui/src/lib/premium-indicator/premium-indicator.component.stories.ts b/libs/ui/src/lib/premium-indicator/premium-indicator.component.stories.ts index bdb9c6543..4cf61084c 100644 --- a/libs/ui/src/lib/premium-indicator/premium-indicator.component.stories.ts +++ b/libs/ui/src/lib/premium-indicator/premium-indicator.component.stories.ts @@ -1,29 +1,28 @@ import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; -import { PremiumIndicatorComponent } from './premium-indicator.component'; +import { GfPremiumIndicatorComponent } from './premium-indicator.component'; export default { title: 'Premium Indicator', - component: PremiumIndicatorComponent, + component: GfPremiumIndicatorComponent, decorators: [ moduleMetadata({ imports: [CommonModule, RouterTestingModule] }) ] -} as Meta; +} as Meta; -const Template: Story = ( - args: PremiumIndicatorComponent -) => ({ - props: args -}); +type Story = StoryObj; -export const Default = Template.bind({}); -Default.args = {}; +export const Default: Story = { + args: {} +}; -export const WithoutLink = Template.bind({}); -WithoutLink.args = { - enableLink: false +export const WithoutLink = { + args: { + enableLink: false + } }; diff --git a/libs/ui/src/lib/trend-indicator/trend-indicator.component.stories.ts b/libs/ui/src/lib/trend-indicator/trend-indicator.component.stories.ts index 0d8438706..1db9e2995 100644 --- a/libs/ui/src/lib/trend-indicator/trend-indicator.component.stories.ts +++ b/libs/ui/src/lib/trend-indicator/trend-indicator.component.stories.ts @@ -1,50 +1,53 @@ -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { TrendIndicatorComponent } from './trend-indicator.component'; +import { GfTrendIndicatorComponent } from './trend-indicator.component'; export default { title: 'Trend Indicator', - component: TrendIndicatorComponent, + component: GfTrendIndicatorComponent, decorators: [ moduleMetadata({ imports: [NgxSkeletonLoaderModule] }) ] -} as Meta; +} as Meta; -const Template: Story = ( - args: TrendIndicatorComponent -) => ({ - props: args -}); +type Story = StoryObj; -export const Loading = Template.bind({}); -Loading.args = { - isLoading: true +export const Loading: Story = { + args: { + isLoading: true + } }; -export const Default = Template.bind({}); -Default.args = {}; +export const Default: Story = { + args: {} +}; -export const Delayed = Template.bind({}); -Delayed.args = { - marketState: 'delayed', - range: '1d' +export const Delayed: Story = { + args: { + dateRange: '1d', + marketState: 'delayed' + } }; -export const Down = Template.bind({}); -Down.args = { - value: -1 +export const Down: Story = { + args: { + value: -1 + } }; -export const Up = Template.bind({}); -Up.args = { - value: 1 +export const Up: Story = { + args: { + value: 1 + } }; -export const MarketClosed = Template.bind({}); -MarketClosed.args = { - marketState: 'closed', - range: '1d' +export const MarketClosed: Story = { + args: { + dateRange: '1d', + marketState: 'closed' + } }; diff --git a/libs/ui/src/lib/value/value.component.stories.ts b/libs/ui/src/lib/value/value.component.stories.ts index fc3e202ea..ff9f5a259 100644 --- a/libs/ui/src/lib/value/value.component.stories.ts +++ b/libs/ui/src/lib/value/value.component.stories.ts @@ -1,71 +1,77 @@ -import { Meta, Story, moduleMetadata } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { ValueComponent } from './value.component'; +import { GfValueComponent } from './value.component'; export default { title: 'Value', - component: ValueComponent, + component: GfValueComponent, decorators: [ moduleMetadata({ imports: [NgxSkeletonLoaderModule] }) ] -} as Meta; +} as Meta; -const Template: Story = (args: ValueComponent) => ({ - props: args -}); +type Story = StoryObj; -export const Loading = Template.bind({}); -Loading.args = { - value: undefined +export const Loading: Story = { + args: { + value: undefined + } }; -export const Currency = Template.bind({}); -Currency.args = { - isCurrency: true, - locale: 'en-US', - unit: 'USD', - value: 7 +export const Currency: Story = { + args: { + isCurrency: true, + locale: 'en-US', + unit: 'USD', + value: 7 + } }; -export const Label = Template.bind({}); -Label.args = { - locale: 'en-US', - value: 7.25 +export const Label: Story = { + args: { + locale: 'en-US', + value: 7.25 + } }; -export const PerformancePositive = Template.bind({}); -PerformancePositive.args = { - locale: 'en-US', - colorizeSign: true, - isPercent: true, - value: 0.0136810853673890378 +export const PerformancePositive: Story = { + args: { + colorizeSign: true, + isPercent: true, + locale: 'en-US', + value: 0.0136810853673890378 + }, + name: 'Performance (positive)' }; -PerformancePositive.storyName = 'Performance (positive)'; -export const PerformanceNegative = Template.bind({}); -PerformanceNegative.args = { - locale: 'en-US', - colorizeSign: true, - isPercent: true, - value: -0.0136810853673890378 +export const PerformanceNegative: Story = { + args: { + colorizeSign: true, + isPercent: true, + locale: 'en-US', + value: -0.0136810853673890378 + }, + name: 'Performance (negative)' }; -PerformanceNegative.storyName = 'Performance (negative)'; -export const PerformanceCloseToZero = Template.bind({}); -PerformanceCloseToZero.args = { - locale: 'en-US', - colorizeSign: true, - isPercent: true, - value: -2.388915360475e-8 +export const PerformanceCloseToZero: Story = { + args: { + colorizeSign: true, + isPercent: true, + locale: 'en-US', + value: -2.388915360475e-8 + }, + name: 'Performance (negative zero)' }; -PerformanceCloseToZero.storyName = 'Performance (negative zero)'; -export const Precision = Template.bind({}); -Precision.args = { - locale: 'en-US', - precision: 3, - value: 7.2534802394809285309 +export const Precision: Story = { + args: { + locale: 'en-US', + precision: 3, + value: 7.2534802394809285309 + } }; diff --git a/package-lock.json b/package-lock.json index 305bcb27c..d5ac0bab4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.132.0", + "version": "2.133.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.132.0", + "version": "2.133.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -63,9 +63,8 @@ "countries-list": "3.1.1", "countup.js": "2.8.0", "date-fns": "3.6.0", - "envalid": "7.3.1", + "envalid": "8.0.0", "google-spreadsheet": "3.2.0", - "got": "11.8.6", "helmet": "7.0.0", "http-status-codes": "2.3.0", "ionicons": "7.4.0", @@ -127,7 +126,7 @@ "@trivago/prettier-plugin-sort-imports": "4.3.0", "@types/big.js": "6.2.2", "@types/cache-manager": "4.0.6", - "@types/color": "3.0.6", + "@types/color": "4.2.0", "@types/google-spreadsheet": "3.1.5", "@types/jest": "29.5.13", "@types/lodash": "4.17.7", @@ -153,7 +152,7 @@ "prisma": "6.1.0", "react": "18.2.0", "react-dom": "18.2.0", - "replace-in-file": "7.0.1", + "replace-in-file": "8.3.0", "shx": "0.3.4", "storybook": "8.4.7", "ts-jest": "29.1.0", @@ -9561,18 +9560,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -10643,18 +10630,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -11148,37 +11123,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/cacheable-request/node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/cacheable-request/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, "node_modules/@types/color": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.6.tgz", - "integrity": "sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/color/-/color-4.2.0.tgz", + "integrity": "sha512-6+xrIRImMtGAL2X3qYkd02Mgs+gFGs+WsK0b7VVMaO4mYRISwyTjcqNrO0mNSmYEoq++rSLDB2F5HDNmqfOe+A==", "dev": true, "license": "MIT", "dependencies": { @@ -11418,12 +11366,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" - }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -11558,30 +11500,6 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/keyv/node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/keyv/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, "node_modules/@types/lodash": { "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", @@ -11826,30 +11744,6 @@ "@types/react": "^18.0.0" } }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/responselike/node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/responselike/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, "node_modules/@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", @@ -14422,33 +14316,6 @@ "node": ">= 18" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cachedir": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", @@ -15058,27 +14925,6 @@ "node": ">=0.10.0" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone-response/node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -16881,21 +16727,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -16984,15 +16815,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -17531,6 +17353,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -17584,21 +17407,21 @@ } }, "node_modules/envalid": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/envalid/-/envalid-7.3.1.tgz", - "integrity": "sha512-KL1YRwn8WcoF/Ty7t+yLLtZol01xr9ZJMTjzoGRM8NaSU+nQQjSWOQKKJhJP2P57bpdakJ9jbxqQX4fGTOicZg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/envalid/-/envalid-8.0.0.tgz", + "integrity": "sha512-PGeYJnJB5naN0ME6SH8nFcDj9HVbLpYIfg1p5lAyM9T4cH2lwtu2fLbozC/bq+HUUOIFxhX/LP0/GmlqPHT4tQ==", "license": "MIT", "dependencies": { - "tslib": "2.3.1" + "tslib": "2.6.2" }, "engines": { "node": ">=8.12" } }, "node_modules/envalid/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "license": "0BSD" }, "node_modules/environment": { @@ -19943,6 +19766,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0" @@ -20266,31 +20090,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -20786,6 +20585,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-deceiver": { @@ -20937,19 +20737,6 @@ "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", "license": "MIT" }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -23281,6 +23068,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -23521,6 +23309,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -24746,15 +24535,6 @@ "devOptional": true, "license": "0BSD" }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -25600,18 +25380,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -26361,18 +26129,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-bundled": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", @@ -26888,6 +26644,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "devOptional": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -27036,15 +26793,6 @@ "dev": true, "license": "MIT" }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -28662,6 +28410,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -28759,18 +28508,6 @@ ], "license": "MIT" }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/rambda": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.4.1.tgz", @@ -29254,54 +28991,71 @@ } }, "node_modules/replace-in-file": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-7.0.1.tgz", - "integrity": "sha512-KbhgPq04eA+TxXuUxpgWIH9k/TjF+28ofon2PXP7vq6izAILhxOtksCVcLuuQLtyjouBaPdlH6RJYYcSPVxCOA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.3.0.tgz", + "integrity": "sha512-4VhddQiMCPIuypiwHDTM+XHjZoVu9h7ngBbSCnwGRcwdHwxltjt/m//Ep3GDwqaOx1fDSrKFQ+n7uo4uVcEz9Q==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "glob": "^8.1.0", + "chalk": "^5.3.0", + "glob": "^10.4.2", "yargs": "^17.7.2" }, "bin": { "replace-in-file": "bin/cli.js" }, "engines": { - "node": ">=10" + "node": ">=18" + } + }, + "node_modules/replace-in-file/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/replace-in-file/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/replace-in-file/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/request-progress": { @@ -29368,12 +29122,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -29443,18 +29191,6 @@ "node": ">=10" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -34053,6 +33789,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "devOptional": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/package.json b/package.json index 650918efd..447d309a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.132.0", + "version": "2.133.1", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -39,7 +39,7 @@ "postinstall": "prisma generate", "prepare": "husky", "prisma": "prisma", - "replace-placeholders-in-build": "node ./replace.build.js", + "replace-placeholders-in-build": "node ./replace.build.mjs", "start": "node dist/apps/api/main", "start:client": "nx run client:copy-assets && nx run client:serve --configuration=development-en --hmr -o", "start:production": "npm run database:migrate && npm run database:seed && node main", @@ -109,9 +109,8 @@ "countries-list": "3.1.1", "countup.js": "2.8.0", "date-fns": "3.6.0", - "envalid": "7.3.1", + "envalid": "8.0.0", "google-spreadsheet": "3.2.0", - "got": "11.8.6", "helmet": "7.0.0", "http-status-codes": "2.3.0", "ionicons": "7.4.0", @@ -173,7 +172,7 @@ "@trivago/prettier-plugin-sort-imports": "4.3.0", "@types/big.js": "6.2.2", "@types/cache-manager": "4.0.6", - "@types/color": "3.0.6", + "@types/color": "4.2.0", "@types/google-spreadsheet": "3.1.5", "@types/jest": "29.5.13", "@types/lodash": "4.17.7", @@ -199,7 +198,7 @@ "prisma": "6.1.0", "react": "18.2.0", "react-dom": "18.2.0", - "replace-in-file": "7.0.1", + "replace-in-file": "8.3.0", "shx": "0.3.4", "storybook": "8.4.7", "ts-jest": "29.1.0", diff --git a/replace.build.js b/replace.build.mjs similarity index 66% rename from replace.build.js rename to replace.build.mjs index 0811fa969..1d714d7a4 100644 --- a/replace.build.js +++ b/replace.build.mjs @@ -1,9 +1,13 @@ -const dotenv = require('dotenv'); -const path = require('path'); -const replace = require('replace-in-file'); +import dotenv from 'dotenv'; +import { dirname, resolve } from 'path'; +import { replaceInFileSync } from 'replace-in-file'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); dotenv.config({ - path: path.resolve(__dirname, '.env') + path: resolve(__dirname, '.env') }); const now = new Date(); @@ -16,7 +20,7 @@ const buildTimestamp = `${formatWithTwoDigits( )}:${formatWithTwoDigits(now.getMinutes())}`; try { - const changedFiles = replace.sync({ + const changedFiles = replaceInFileSync({ files: './dist/apps/client/main.*.js', from: /{BUILD_TIMESTAMP}/g, to: buildTimestamp,