Browse Source

Merge branch 'main' into translate-tr

pull/5030/head
Thomas Kaul 7 days ago
committed by GitHub
parent
commit
4933c5bbf2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      CHANGELOG.md
  2. 8
      apps/api/src/app/admin/admin.controller.ts
  3. 8
      apps/api/src/services/data-provider/manual/manual.service.ts
  4. 35
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  5. 18
      apps/client/src/locales/messages.ca.xlf

5
CHANGELOG.md

@ -9,8 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Improved the language localization for Catalan (`ca`)
- Improved the language localization for Turkish (`tr`) - Improved the language localization for Turkish (`tr`)
### Fixed
- Fixed an issue with the locale in the scraper configuration
## 2.174.0 - 2025-06-24 ## 2.174.0 - 2025-06-24
### Added ### Added

8
apps/api/src/app/admin/admin.controller.ts

@ -17,7 +17,8 @@ import {
AdminData, AdminData,
AdminMarketData, AdminMarketData,
AdminUsers, AdminUsers,
EnhancedSymbolProfile EnhancedSymbolProfile,
ScraperConfiguration
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions';
import type { import type {
@ -222,13 +223,12 @@ export class AdminController {
@Post('market-data/:dataSource/:symbol/test') @Post('market-data/:dataSource/:symbol/test')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async testMarketData( public async testMarketData(
@Body() data: { scraperConfiguration: string }, @Body() data: { scraperConfiguration: ScraperConfiguration },
@Param('dataSource') dataSource: DataSource, @Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string @Param('symbol') symbol: string
): Promise<{ price: number }> { ): Promise<{ price: number }> {
try { try {
const scraperConfiguration = JSON.parse(data.scraperConfiguration); const price = await this.manualService.test(data.scraperConfiguration);
const price = await this.manualService.test(scraperConfiguration);
if (price) { if (price) {
return { price }; return { price };

8
apps/api/src/services/data-provider/manual/manual.service.ts

@ -104,7 +104,7 @@ export class ManualService implements DataProviderInterface {
} }
return historical; return historical;
} else if (selector === undefined || url === undefined) { } else if (!selector || !url) {
return {}; return {};
} }
@ -162,7 +162,11 @@ export class ManualService implements DataProviderInterface {
const symbolProfilesWithScraperConfigurationAndInstantMode = const symbolProfilesWithScraperConfigurationAndInstantMode =
symbolProfiles.filter(({ scraperConfiguration }) => { symbolProfiles.filter(({ scraperConfiguration }) => {
return scraperConfiguration?.mode === 'instant'; return (
scraperConfiguration?.mode === 'instant' &&
scraperConfiguration?.selector &&
scraperConfiguration?.url
);
}); });
const scraperResultPromises = const scraperResultPromises =

35
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -11,6 +11,7 @@ import {
AdminMarketDataDetails, AdminMarketDataDetails,
AssetProfileIdentifier, AssetProfileIdentifier,
LineChartItem, LineChartItem,
ScraperConfiguration,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -41,6 +42,7 @@ import {
AssetClass, AssetClass,
AssetSubClass, AssetSubClass,
MarketData, MarketData,
Prisma,
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { format } from 'date-fns'; import { format } from 'date-fns';
@ -343,7 +345,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
public async onSubmitAssetProfileForm() { public async onSubmitAssetProfileForm() {
let countries = []; let countries = [];
let scraperConfiguration = {}; let scraperConfiguration: ScraperConfiguration = {
selector: '',
url: ''
};
let sectors = []; let sectors = [];
let symbolMapping = {}; let symbolMapping = {};
@ -354,9 +359,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
try { try {
scraperConfiguration = { scraperConfiguration = {
defaultMarketPrice: defaultMarketPrice:
this.assetProfileForm.controls['scraperConfiguration'].controls[ (this.assetProfileForm.controls['scraperConfiguration'].controls[
'defaultMarketPrice' 'defaultMarketPrice'
].value, ].value as number) || undefined,
headers: JSON.parse( headers: JSON.parse(
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'headers' 'headers'
@ -365,10 +370,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
locale: locale:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'locale' 'locale'
].value, ].value || undefined,
mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ mode: this.assetProfileForm.controls['scraperConfiguration'].controls[
'mode' 'mode'
].value, ].value as ScraperConfiguration['mode'],
selector: selector:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'selector' 'selector'
@ -377,6 +382,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
'url' 'url'
].value ].value
}; };
if (!scraperConfiguration.selector || !scraperConfiguration.url) {
scraperConfiguration = undefined;
}
} catch {} } catch {}
try { try {
@ -391,7 +400,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
const assetProfile: UpdateAssetProfileDto = { const assetProfile: UpdateAssetProfileDto = {
countries, countries,
scraperConfiguration,
sectors, sectors,
symbolMapping, symbolMapping,
assetClass: this.assetProfileForm.get('assetClass').value, assetClass: this.assetProfileForm.get('assetClass').value,
@ -400,6 +408,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
currency: this.assetProfileForm.get('currency').value, currency: this.assetProfileForm.get('currency').value,
isActive: this.assetProfileForm.get('isActive').value, isActive: this.assetProfileForm.get('isActive').value,
name: this.assetProfileForm.get('name').value, name: this.assetProfileForm.get('name').value,
scraperConfiguration:
scraperConfiguration as unknown as Prisma.InputJsonObject,
url: this.assetProfileForm.get('url').value || null url: this.assetProfileForm.get('url').value || null
}; };
@ -493,11 +503,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
this.adminService this.adminService
.testMarketData({ .testMarketData({
dataSource: this.data.dataSource, dataSource: this.data.dataSource,
scraperConfiguration: JSON.stringify({ scraperConfiguration: {
defaultMarketPrice: defaultMarketPrice: this.assetProfileForm.controls[
this.assetProfileForm.controls['scraperConfiguration'].controls[ 'scraperConfiguration'
'defaultMarketPrice' ].controls['defaultMarketPrice'].value as number,
].value,
headers: JSON.parse( headers: JSON.parse(
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'headers' 'headers'
@ -506,7 +515,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
locale: locale:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'locale' 'locale'
].value, ].value || undefined,
mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ mode: this.assetProfileForm.controls['scraperConfiguration'].controls[
'mode' 'mode'
].value, ].value,
@ -517,7 +526,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
url: this.assetProfileForm.controls['scraperConfiguration'].controls[ url: this.assetProfileForm.controls['scraperConfiguration'].controls[
'url' 'url'
].value ].value
}), },
symbol: this.data.symbol symbol: this.data.symbol
}) })
.pipe( .pipe(

18
apps/client/src/locales/messages.ca.xlf

@ -4845,7 +4845,7 @@
</trans-unit> </trans-unit>
<trans-unit id="50760c2140335b2324deaee120f40d9aa64b3238" datatype="html"> <trans-unit id="50760c2140335b2324deaee120f40d9aa64b3238" datatype="html">
<source>Currency Cluster Risks</source> <source>Currency Cluster Risks</source>
<target state="new">Currency Cluster Risks</target> <target state="translated">Riscos del clúster de divises</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html</context>
<context context-type="linenumber">93</context> <context context-type="linenumber">93</context>
@ -4853,7 +4853,7 @@
</trans-unit> </trans-unit>
<trans-unit id="c7b797e5df289241021db16010efb6ac3c6cfb86" datatype="html"> <trans-unit id="c7b797e5df289241021db16010efb6ac3c6cfb86" datatype="html">
<source>Account Cluster Risks</source> <source>Account Cluster Risks</source>
<target state="new">Account Cluster Risks</target> <target state="translated">Riscos del clúster de comptes</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html</context>
<context context-type="linenumber">141</context> <context context-type="linenumber">141</context>
@ -4861,7 +4861,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5080775557941296581" datatype="html"> <trans-unit id="5080775557941296581" datatype="html">
<source>Pricing</source> <source>Pricing</source>
<target state="new">Pricing</target> <target state="translated">Preus</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page-routing.module.ts</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page-routing.module.ts</context>
<context context-type="linenumber">13</context> <context context-type="linenumber">13</context>
@ -4873,7 +4873,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9ba87e803f0b976584dc2a4dd69d32090cdfc229" datatype="html"> <trans-unit id="9ba87e803f0b976584dc2a4dd69d32090cdfc229" datatype="html">
<source>Pricing Plans</source> <source>Pricing Plans</source>
<target state="new">Pricing Plans</target> <target state="translated">Plans de preus</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
@ -4881,7 +4881,7 @@
</trans-unit> </trans-unit>
<trans-unit id="de0d77a5255f97548d2b579f78c20c911a71820f" datatype="html"> <trans-unit id="de0d77a5255f97548d2b579f78c20c911a71820f" datatype="html">
<source> Our official Ghostfolio Premium cloud offering is the easiest way to get started. Due to the time it saves, this will be the best option for most people. Revenue is used to cover the costs of the hosting infrastructure and to fund ongoing development. </source> <source> Our official Ghostfolio Premium cloud offering is the easiest way to get started. Due to the time it saves, this will be the best option for most people. Revenue is used to cover the costs of the hosting infrastructure and to fund ongoing development. </source>
<target state="new"> Our official Ghostfolio Premium cloud offering is the easiest way to get started. Due to the time it saves, this will be the best option for most people. Revenue is used to cover the costs of the hosting infrastructure and to fund ongoing development. </target> <target state="translated"> La nostra oferta oficial al núvol Ghostfolio Premium és la manera més senzilla de començar. A causa del temps que estalvia, aquesta serà la millor opció per a la majoria de la gent. Els ingressos s’utilitzen per cobrir els costos de la infraestructura d’allotjament i per finançar el desenvolupament en curs. </target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">6</context> <context context-type="linenumber">6</context>
@ -4897,7 +4897,7 @@
</trans-unit> </trans-unit>
<trans-unit id="fd30b4e3189726798f09fa0ce875486cf50dda2d" datatype="html"> <trans-unit id="fd30b4e3189726798f09fa0ce875486cf50dda2d" datatype="html">
<source> For tech-savvy investors who prefer to run Ghostfolio on their own infrastructure. </source> <source> For tech-savvy investors who prefer to run Ghostfolio on their own infrastructure. </source>
<target state="new"> For tech-savvy investors who prefer to run Ghostfolio on their own infrastructure. </target> <target state="translated"> Per a inversors experts en tecnologia que prefereixen executar Ghostfolio a la seva pròpia infraestructura. </target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">24</context>
@ -4905,7 +4905,7 @@
</trans-unit> </trans-unit>
<trans-unit id="435445e728dcf7fdf68fbae0e55e83f61591ac87" datatype="html"> <trans-unit id="435445e728dcf7fdf68fbae0e55e83f61591ac87" datatype="html">
<source>Unlimited Transactions</source> <source>Unlimited Transactions</source>
<target state="new">Unlimited Transactions</target> <target state="translated">Transaccions il·limitades</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">31</context> <context context-type="linenumber">31</context>
@ -4921,7 +4921,7 @@
</trans-unit> </trans-unit>
<trans-unit id="897e322427107efb4a101afcc9641f0e81ce9643" datatype="html"> <trans-unit id="897e322427107efb4a101afcc9641f0e81ce9643" datatype="html">
<source>Unlimited Accounts</source> <source>Unlimited Accounts</source>
<target state="new">Unlimited Accounts</target> <target state="translated">Comptes il·limitats</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
@ -4937,7 +4937,7 @@
</trans-unit> </trans-unit>
<trans-unit id="60d7aee1c199a9995a318cfce24cbccbcfb52863" datatype="html"> <trans-unit id="60d7aee1c199a9995a318cfce24cbccbcfb52863" datatype="html">
<source>Portfolio Performance</source> <source>Portfolio Performance</source>
<target state="new">Portfolio Performance</target> <target state="translated">Rendiment de la cartera</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">39</context> <context context-type="linenumber">39</context>

Loading…
Cancel
Save