diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e2cdec28..c24f75ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.250.0 - 2026-03-17 + +### Added + +- Added support for specific calendar year date ranges (`2025`, `2024`, `2023`, etc.) on the portfolio activities page ### Changed - Consolidated the sign-out logic within the user service to unify cookie, state and token clearance - Improved the language localization for Polish (`pl`) - Upgraded `@ionic/angular` from version `8.7.3` to `8.8.1` +- Upgraded `replace-in-file` from version `8.3.0` to `8.4.0` - Upgraded `svgmap` from version `2.14.0` to `2.19.2` - Pinned the _Node.js_ version in the _Build code_ _GitHub Action_ to ensure environment consistency for tests ### Fixed - Fixed an issue with the detection of the thousand separator for the `de-CH` locale +- Fixed an issue in the _Storybook_ stories of the symbol autocomplete component caused by a circular dependency ## 2.249.0 - 2026-03-10 diff --git a/apps/api/src/app/activities/activities.controller.ts b/apps/api/src/app/activities/activities.controller.ts index 49c8885a1..141fd4c82 100644 --- a/apps/api/src/app/activities/activities.controller.ts +++ b/apps/api/src/app/activities/activities.controller.ts @@ -126,7 +126,7 @@ export class ActivitiesController { let startDate: Date; if (dateRange) { - ({ endDate, startDate } = getIntervalFromDateRange(dateRange)); + ({ endDate, startDate } = getIntervalFromDateRange({ dateRange })); } const filters = this.apiService.buildFiltersFromQueryParams({ diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 8a202a926..69b619625 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -172,7 +172,7 @@ export class AdminController { let date: Date; if (dateRange) { - const { startDate } = getIntervalFromDateRange(dateRange); + const { startDate } = getIntervalFromDateRange({ dateRange }); date = startDate; } diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts index 629d90928..970925777 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts @@ -126,10 +126,10 @@ export class BenchmarksController { @Query('tags') filterByTags?: string, @Query('withExcludedAccounts') withExcludedAccountsParam = 'false' ): Promise { - const { endDate, startDate } = getIntervalFromDateRange( + const { endDate, startDate } = getIntervalFromDateRange({ dateRange, - new Date(startDateString) - ); + startDate: new Date(startDateString) + }); const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 553cb8c90..d57b85d8c 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -158,10 +158,10 @@ export abstract class PortfolioCalculator { this.redisCacheService = redisCacheService; this.userId = userId; - const { endDate, startDate } = getIntervalFromDateRange( - 'max', - subDays(dateOfFirstActivity, 1) - ); + const { endDate, startDate } = getIntervalFromDateRange({ + dateRange: 'max', + startDate: subDays(dateOfFirstActivity, 1) + }); this.endDate = endOfDay(endDate); this.startDate = startOfDay(startDate); @@ -885,7 +885,7 @@ export abstract class PortfolioCalculator { // Make sure some key dates are present for (const dateRange of ['1d', '1y', '5y', 'max', 'mtd', 'wtd', 'ytd']) { const { endDate: dateRangeEnd, startDate: dateRangeStart } = - getIntervalFromDateRange(dateRange); + getIntervalFromDateRange({ dateRange }); if ( !isBefore(dateRangeStart, startDate) && diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index be69048df..2841e9975 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -860,7 +860,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { return format(date, 'yyyy'); }) ] as DateRange[]) { - const dateInterval = getIntervalFromDateRange(dateRange); + const dateInterval = getIntervalFromDateRange({ dateRange }); const endDate = dateInterval.endDate; let startDate = dateInterval.startDate; diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 73d4320d6..9c41aecb9 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -320,7 +320,7 @@ export class PortfolioController { await this.impersonationService.validateImpersonationId(impersonationId); const userCurrency = this.request.user.settings.settings.baseCurrency; - const { endDate, startDate } = getIntervalFromDateRange(dateRange); + const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); const { activities } = await this.activitiesService.getActivities({ endDate, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 5dab27939..60b413cf9 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -403,7 +403,7 @@ export class PortfolioService { const user = await this.userService.user({ id: userId }); const userCurrency = this.getUserCurrency(user); - const { endDate, startDate } = getIntervalFromDateRange(dateRange); + const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); const { activities } = await this.activitiesService.getActivitiesForPortfolioCalculator({ @@ -1047,7 +1047,7 @@ export class PortfolioService { const { errors, hasErrors, historicalData } = await portfolioCalculator.getSnapshot(); - const { endDate, startDate } = getIntervalFromDateRange(dateRange); + const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); const { chart } = await portfolioCalculator.getPerformance({ end: endDate, diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index 380fb69cb..7a3040391 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -26,11 +26,12 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, Inject, - OnDestroy, OnInit } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'; @@ -49,8 +50,7 @@ import { } from 'ionicons/icons'; import { isNumber } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { forkJoin, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { forkJoin } from 'rxjs'; import { AccountDetailDialogParams } from './interfaces/interfaces'; @@ -77,7 +77,7 @@ import { AccountDetailDialogParams } from './interfaces/interfaces'; styleUrls: ['./account-detail-dialog.component.scss'], templateUrl: 'account-detail-dialog.html' }) -export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { +export class GfAccountDetailDialogComponent implements OnInit { public accountBalances: AccountBalancesResponse['balances']; public activities: OrderWithAccount[]; public activitiesCount: number; @@ -104,18 +104,17 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { public user: User; public valueInBaseCurrency: number; - private unsubscribeSubject = new Subject(); - public constructor( private changeDetectorRef: ChangeDetectorRef, @Inject(MAT_DIALOG_DATA) public data: AccountDetailDialogParams, private dataService: DataService, + private destroyRef: DestroyRef, public dialogRef: MatDialogRef, private router: Router, private userService: UserService ) { this.userService.stateChanged - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((state) => { if (state?.user) { this.user = state.user; @@ -154,7 +153,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { public onAddAccountBalance(accountBalance: CreateAccountBalanceDto) { this.dataService .postAccountBalance(accountBalance) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.initialize(); }); @@ -163,7 +162,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { public onDeleteAccountBalance(aId: string) { this.dataService .deleteAccountBalance(aId) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.initialize(); }); @@ -176,7 +175,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { this.dataService .fetchExport({ activityIds }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((data) => { downloadAsFile({ content: data, @@ -212,7 +211,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { private fetchAccount() { this.dataService .fetchAccount(this.data.accountId) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( ({ activitiesCount, @@ -287,7 +286,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { sortColumn: this.sortColumn, sortDirection: this.sortDirection }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(({ activities, count }) => { this.dataSource = new MatTableDataSource(activities); this.totalItems = count; @@ -304,7 +303,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { forkJoin({ accountBalances: this.dataService .fetchAccountBalances(this.data.accountId) - .pipe(takeUntil(this.unsubscribeSubject)), + .pipe(takeUntilDestroyed(this.destroyRef)), portfolioPerformance: this.dataService .fetchPortfolioPerformance({ filters: [ @@ -317,7 +316,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { withExcludedAccounts: true, withItems: true }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) }).subscribe({ error: () => { this.isLoadingChart = false; @@ -360,7 +359,7 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { } ] }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(({ holdings }) => { this.holdings = holdings; @@ -374,9 +373,4 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit { this.fetchChart(); this.fetchPortfolioHoldings(); } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } } diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index c0ccb0f64..5d4e5268e 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -22,7 +22,13 @@ import { AdminService, DataService } from '@ghostfolio/ui/services'; import { GfValueComponent } from '@ghostfolio/ui/value'; import { CommonModule } from '@angular/common'; -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + DestroyRef, + OnInit +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -50,8 +56,6 @@ import { trashOutline } from 'ionicons/icons'; import ms, { StringValue } from 'ms'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; @Component({ imports: [ @@ -72,7 +76,7 @@ import { takeUntil } from 'rxjs/operators'; styleUrls: ['./admin-overview.scss'], templateUrl: './admin-overview.html' }) -export class GfAdminOverviewComponent implements OnDestroy, OnInit { +export class GfAdminOverviewComponent implements OnInit { public activitiesCount: number; public couponDuration: StringValue = '14 days'; public coupons: Coupon[]; @@ -88,13 +92,12 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { public user: User; public version: string; - private unsubscribeSubject = new Subject(); - public constructor( private adminService: AdminService, private cacheService: CacheService, private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, + private destroyRef: DestroyRef, private notificationService: NotificationService, private snackBar: MatSnackBar, private userService: UserService @@ -102,7 +105,7 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { this.info = this.dataService.fetchInfo(); this.userService.stateChanged - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((state) => { if (state?.user) { this.user = state.user; @@ -219,7 +222,7 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { confirmFn: () => { this.cacheService .flush() - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { setTimeout(() => { window.location.reload(); @@ -268,7 +271,7 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { public onSyncDemoUserAccount() { this.adminService .syncDemoUserAccount() - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.snackBar.open( '✅ ' + $localize`Demo user account has been synced.`, @@ -280,15 +283,10 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { }); } - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } - private fetchAdminData() { this.adminService .fetchAdminData() - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(({ activitiesCount, settings, userCount, version }) => { this.activitiesCount = activitiesCount; this.coupons = (settings[PROPERTY_COUPONS] as Coupon[]) ?? []; @@ -320,7 +318,7 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit { .putAdminSetting(key, { value: value || value === false ? JSON.stringify(value) : undefined }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { setTimeout(() => { window.location.reload(); diff --git a/apps/client/src/app/components/rule/rule.component.ts b/apps/client/src/app/components/rule/rule.component.ts index e2ffc1cf6..23f870ff3 100644 --- a/apps/client/src/app/components/rule/rule.component.ts +++ b/apps/client/src/app/components/rule/rule.component.ts @@ -9,11 +9,13 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, + DestroyRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; @@ -29,7 +31,6 @@ import { } from 'ionicons/icons'; import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { Subject, takeUntil } from 'rxjs'; import { RuleSettingsDialogParams } from './rule-settings-dialog/interfaces/interfaces'; import { GfRuleSettingsDialogComponent } from './rule-settings-dialog/rule-settings-dialog.component'; @@ -58,9 +59,8 @@ export class GfRuleComponent implements OnInit { @Output() ruleUpdated = new EventEmitter(); private deviceType: string; - private unsubscribeSubject = new Subject(); - public constructor( + private destroyRef: DestroyRef, private deviceService: DeviceDetectorService, private dialog: MatDialog ) { @@ -94,7 +94,7 @@ export class GfRuleComponent implements OnInit { dialogRef .afterClosed() - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((settings: RuleSettings) => { if (settings) { this.ruleUpdated.emit({ @@ -115,9 +115,4 @@ export class GfRuleComponent implements OnInit { this.ruleUpdated.emit(settings); } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } } diff --git a/apps/client/src/app/pages/landing/landing-page.component.ts b/apps/client/src/app/pages/landing/landing-page.component.ts index 25fb2d6e7..9ee9cceb4 100644 --- a/apps/client/src/app/pages/landing/landing-page.component.ts +++ b/apps/client/src/app/pages/landing/landing-page.component.ts @@ -9,7 +9,7 @@ import { GfValueComponent } from '@ghostfolio/ui/value'; import { GfWorldMapChartComponent } from '@ghostfolio/ui/world-map-chart'; import { CommonModule } from '@angular/common'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { RouterModule } from '@angular/router'; @@ -21,7 +21,6 @@ import { starOutline } from 'ionicons/icons'; import { DeviceDetectorService } from 'ngx-device-detector'; -import { Subject } from 'rxjs'; @Component({ host: { class: 'page' }, @@ -40,7 +39,7 @@ import { Subject } from 'rxjs'; styleUrls: ['./landing-page.scss'], templateUrl: './landing-page.html' }) -export class GfLandingPageComponent implements OnDestroy, OnInit { +export class GfLandingPageComponent implements OnInit { public countriesOfSubscribersMap: { [code: string]: { value: number }; } = {}; @@ -107,8 +106,6 @@ export class GfLandingPageComponent implements OnDestroy, OnInit { } ]; - private unsubscribeSubject = new Subject(); - public constructor( private dataService: DataService, private deviceService: DeviceDetectorService @@ -155,9 +152,4 @@ export class GfLandingPageComponent implements OnDestroy, OnInit { public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } } diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index dd78a0221..4e5daf345 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -10,6 +10,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { DateRange } from '@ghostfolio/common/types'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { DataService } from '@ghostfolio/ui/services'; @@ -129,8 +130,13 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public fetchActivities() { + const dateRange = this.user?.settings?.dateRange; + + const range = this.isCalendarYear(dateRange) ? dateRange : undefined; + this.dataService .fetchActivities({ + range, filters: this.userService.getFilters(), skip: this.pageIndex * this.pageSize, sortColumn: this.sortColumn, @@ -352,6 +358,14 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } + private isCalendarYear(dateRange: DateRange) { + if (!dateRange) { + return false; + } + + return /^\d{4}$/.test(dateRange); + } + private openCreateActivityDialog(aActivity?: Activity) { this.userService .get() diff --git a/libs/common/src/lib/calculation-helper.ts b/libs/common/src/lib/calculation-helper.ts index 76b38f9b2..2097fa52a 100644 --- a/libs/common/src/lib/calculation-helper.ts +++ b/libs/common/src/lib/calculation-helper.ts @@ -36,14 +36,16 @@ export function getAnnualizedPerformancePercent({ return new Big(0); } -export function getIntervalFromDateRange( - aDateRange: DateRange, - portfolioStart = new Date(0) -) { - let endDate = endOfDay(new Date()); - let startDate = portfolioStart; +export function getIntervalFromDateRange(params: { + dateRange: DateRange; + endDate?: Date; + startDate?: Date; +}) { + const { dateRange } = params; + let endDate = params.endDate ?? endOfDay(new Date()); + let startDate = params.startDate ?? new Date(0); - switch (aDateRange) { + switch (dateRange) { case '1d': startDate = max([startDate, subDays(resetHours(new Date()), 1)]); break; @@ -75,8 +77,8 @@ export function getIntervalFromDateRange( break; default: // '2024', '2023', '2022', etc. - endDate = endOfYear(new Date(aDateRange)); - startDate = max([startDate, new Date(aDateRange)]); + endDate = endOfYear(new Date(dateRange)); + startDate = max([startDate, new Date(dateRange)]); } return { endDate, startDate }; diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index dec2a4dd5..094001c2f 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -22,7 +22,6 @@ import { } from '@ghostfolio/common/interfaces'; import { DateRange } from '@ghostfolio/common/types'; import { GF_ENVIRONMENT } from '@ghostfolio/ui/environment'; -import { DataService } from '@ghostfolio/ui/services'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; @@ -31,6 +30,8 @@ import { DataSource, MarketData, Platform } from '@prisma/client'; import { JobStatus } from 'bull'; import { isNumber } from 'lodash'; +import { DataService } from './data.service'; + @Injectable({ providedIn: 'root' }) diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts index a80876f6a..7069cabb0 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -162,7 +162,9 @@ export class GfTreemapChartComponent private initialize() { this.isLoading = true; - const { endDate, startDate } = getIntervalFromDateRange(this.dateRange); + const { endDate, startDate } = getIntervalFromDateRange({ + dateRange: this.dateRange + }); const netPerformancePercentsWithCurrencyEffect = this.holdings.map( ({ dateOfFirstActivity, netPerformancePercentWithCurrencyEffect }) => { diff --git a/package-lock.json b/package-lock.json index e77522a6f..80260794c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.249.0", + "version": "2.250.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.249.0", + "version": "2.250.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -153,7 +153,7 @@ "prisma": "6.19.0", "react": "18.2.0", "react-dom": "18.2.0", - "replace-in-file": "8.3.0", + "replace-in-file": "8.4.0", "shx": "0.4.0", "storybook": "10.1.10", "ts-jest": "29.4.0", @@ -26007,11 +26007,11 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -29871,15 +29871,15 @@ } }, "node_modules/replace-in-file": { - "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==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.4.0.tgz", + "integrity": "sha512-D28k8jy2LtUGbCzCnR3znajaTWIjJ/Uee3UdodzcHRxE7zn6NmYW/dcSqyivnsYU3W+MxdX6SbF28NvJ0GRoLA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.3.0", - "glob": "^10.4.2", - "yargs": "^17.7.2" + "chalk": "^5.6.2", + "glob": "^13.0.0", + "yargs": "^18.0.0" }, "bin": { "replace-in-file": "bin/cli.js" @@ -29888,10 +29888,33 @@ "node": ">=18" } }, + "node_modules/replace-in-file/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/replace-in-file/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "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==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", "engines": { @@ -29901,23 +29924,65 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/replace-in-file/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/replace-in-file/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": ">=12" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/replace-in-file/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/replace-in-file/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/replace-in-file/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/require-directory": { diff --git a/package.json b/package.json index e8b38049b..411b36620 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.249.0", + "version": "2.250.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -198,7 +198,7 @@ "prisma": "6.19.0", "react": "18.2.0", "react-dom": "18.2.0", - "replace-in-file": "8.3.0", + "replace-in-file": "8.4.0", "shx": "0.4.0", "storybook": "10.1.10", "ts-jest": "29.4.0",