diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc6b88a5..754157668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,18 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Improved the language localization for Catalan (`ca`) + +## 2.188.0 - 2025-08-02 + +### Changed + +- Enhanced the performance of the dynamically composed sitemap +- Improved the language localization for Polish (`pl`) +- Improved the language localization for Spanish (`es`) + +## 2.187.0 - 2025-08-02 + ### Added +- Added support to exclude an activity from analysis based on tags +- Added a _Storybook_ story for the accounts table component - Added a _Storybook_ story for the membership card component ### Changed - Moved the support for changing the asset profile identifier (`dataSource` and `symbol`) in the asset profile details dialog of the admin control panel from experimental to general availability - Improved the balance of headings on the landing page +- Improved the language localization for German (`de`) - Improved the language localization for Spanish (`es`) - Upgraded `angular` from version `20.0.7` to `20.1.3` - Upgraded `Nx` from version `21.2.4` to `21.3.9` +### Fixed + +- Fixed the missing localization for "Exclude from Analysis" in the create or update account dialog + ## 2.186.0 - 2025-07-30 ### Added diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts index d18fe884a..3774d2274 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts @@ -21,18 +21,42 @@ export class SitemapService { const rootUrl = this.configurationService.get('ROOT_URL'); return SUPPORTED_LANGUAGE_CODES.flatMap((languageCode) => { + const resourcesPath = this.i18nService.getTranslation({ + languageCode, + id: publicRoutes.resources.path.match( + SitemapService.TRANSLATION_TAGGED_MESSAGE_REGEX + ).groups.id + }); + + const personalFinanceToolsPath = this.i18nService.getTranslation({ + languageCode, + id: publicRoutes.resources.subRoutes.personalFinanceTools.path.match( + SitemapService.TRANSLATION_TAGGED_MESSAGE_REGEX + ).groups.id + }); + + const productPath = this.i18nService.getTranslation({ + languageCode, + id: publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product.path.match( + SitemapService.TRANSLATION_TAGGED_MESSAGE_REGEX + ).groups.id + }); + return personalFinanceTools.map(({ alias, key }) => { - const route = - publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes - .product; - const params = { - currentDate, - languageCode, + const location = [ rootUrl, - urlPostfix: alias ?? key - }; - - return this.createRouteSitemapUrl({ ...params, route }); + languageCode, + resourcesPath, + personalFinanceToolsPath, + `${productPath}-${alias ?? key}` + ].join('/'); + + return [ + ' ', + ` ${location}`, + ` ${currentDate}T00:00:00+00:00`, + ' ' + ].join('\n'); }); }).join('\n'); } @@ -58,14 +82,12 @@ export class SitemapService { currentDate, languageCode, rootUrl, - route, - urlPostfix + route }: { currentDate: string; languageCode: string; rootUrl: string; route?: PublicRoute; - urlPostfix?: string; }): string { const segments = route?.routerLink.map((link) => { @@ -83,9 +105,7 @@ export class SitemapService { return segment.replace(/^\/+|\/+$/, ''); }) ?? []; - const location = - [rootUrl, languageCode, ...segments].join('/') + - (urlPostfix ? `-${urlPostfix}` : ''); + const location = [rootUrl, languageCode, ...segments].join('/'); return [ ' ', diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 54fd0763d..7d78bdf22 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -41,7 +41,7 @@ export class ExportService { includeDrafts: true, sortColumn: 'date', sortDirection: 'asc', - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); if (activityIds?.length > 0) { @@ -141,15 +141,16 @@ export class ExportService { ); const tags = (await this.tagService.getTagsForUser(userId)) - .filter( - ({ id, isUsed }) => + .filter(({ id, isUsed }) => { + return ( isUsed && activities.some((activity) => { return activity.tags.some(({ id: tagId }) => { return tagId === id; }); }) - ) + ); + }) .map(({ id, name }) => { return { id, diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index d23427616..ef9d25f53 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -533,7 +533,7 @@ export class ImportService { userCurrency, userId, includeDrafts: true, - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); return activitiesDto.map( diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index cd8012cec..9bd45050e 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -144,7 +144,7 @@ export class OrderController { skip: isNaN(skip) ? undefined : skip, take: isNaN(take) ? undefined : take, userId: impersonationUserId || this.request.user.id, - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); return { activities, count }; @@ -165,7 +165,7 @@ export class OrderController { const { activities } = await this.orderService.getOrders({ userCurrency, userId: impersonationUserId || this.request.user.id, - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); const activity = activities.find((activity) => { diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 21fa0d076..e9d72233e 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -9,7 +9,8 @@ import { DATA_GATHERING_QUEUE_PRIORITY_HIGH, GATHER_ASSET_PROFILE_PROCESS_JOB_NAME, GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS, - ghostfolioPrefix + ghostfolioPrefix, + TAG_ID_EXCLUDE_FROM_ANALYSIS } from '@ghostfolio/common/config'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { @@ -275,7 +276,7 @@ export class OrderService { userId, includeDrafts: true, userCurrency: undefined, - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); const { count } = await this.prismaService.order.deleteMany({ @@ -332,7 +333,7 @@ export class OrderService { types, userCurrency, userId, - withExcludedAccounts = false + withExcludedAccountsAndActivities = false }: { endDate?: Date; filters?: Filter[]; @@ -345,7 +346,7 @@ export class OrderService { types?: ActivityType[]; userCurrency: string; userId: string; - withExcludedAccounts?: boolean; + withExcludedAccountsAndActivities?: boolean; }): Promise { let orderBy: Prisma.Enumerable = [ { date: 'asc' }, @@ -491,11 +492,18 @@ export class OrderService { where.type = { in: types }; } - if (withExcludedAccounts === false) { + if (withExcludedAccountsAndActivities === false) { where.OR = [ { account: null }, { account: { NOT: { isExcluded: true } } } ]; + + where.tags = { + ...where.tags, + none: { + id: TAG_ID_EXCLUDE_FROM_ANALYSIS + } + }; } const [orders, count] = await Promise.all([ @@ -609,7 +617,7 @@ export class OrderService { filters, userCurrency, userId, - withExcludedAccounts: false // TODO + withExcludedAccountsAndActivities: false // TODO }); } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 02804a847..d1b9af892 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -33,6 +33,7 @@ import { import { DEFAULT_CURRENCY, TAG_ID_EMERGENCY_FUND, + TAG_ID_EXCLUDE_FROM_ANALYSIS, UNKNOWN_KEY } from '@ghostfolio/common/config'; import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper'; @@ -1799,14 +1800,19 @@ export class PortfolioService { const { activities } = await this.orderService.getOrders({ userCurrency, userId, - withExcludedAccounts: true + withExcludedAccountsAndActivities: true }); const excludedActivities: Activity[] = []; const nonExcludedActivities: Activity[] = []; for (const activity of activities) { - if (activity.account?.isExcluded) { + if ( + activity.account?.isExcluded || + activity.tags?.some(({ id }) => { + return id === TAG_ID_EXCLUDE_FROM_ANALYSIS; + }) + ) { excludedActivities.push(activity); } else { nonExcludedActivities.push(activity); diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index a043761fa..405c4c0b0 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -28,6 +28,7 @@ import { DEFAULT_LANGUAGE_CODE, PROPERTY_IS_READ_ONLY_MODE, PROPERTY_SYSTEM_MESSAGE, + TAG_ID_EXCLUDE_FROM_ANALYSIS, locale } from '@ghostfolio/common/config'; import { @@ -121,7 +122,9 @@ export class UserService { const access = userData[0]; const activitiesCount = userData[1]; const firstActivity = userData[2]; - let tags = userData[3]; + let tags = userData[3].filter((tag) => { + return tag.id !== TAG_ID_EXCLUDE_FROM_ANALYSIS; + }); let systemMessage: SystemMessage; diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index 5cf353e9a..75ec37480 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -154,13 +154,9 @@ export class HtmlTemplateMiddleware implements NestMiddleware { if (filename === '/assets/LICENSE') { return true; } else if ( - filename.includes('auth/ey') || - filename.includes( - 'personal-finance-tools/open-source-alternative-to-de.fi' - ) || - filename.includes( - 'personal-finance-tools/open-source-alternative-to-markets.sh' - ) + filename.endsWith('-de.fi') || + filename.endsWith('-markets.sh') || + filename.includes('auth/ey') ) { return false; } diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index ec62e41fe..dc9a3af0f 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -12,7 +12,7 @@ -