diff --git a/CHANGELOG.md b/CHANGELOG.md index c012d540c..35c87f60f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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 +## 3.26.0 - 2026-07-14 ### Added diff --git a/apps/client/src/app/components/home-holdings/home-holdings.component.ts b/apps/client/src/app/components/home-holdings/home-holdings.component.ts index bf70bf8e0..bae154607 100644 --- a/apps/client/src/app/components/home-holdings/home-holdings.component.ts +++ b/apps/client/src/app/components/home-holdings/home-holdings.component.ts @@ -58,7 +58,7 @@ export class GfHomeHoldingsComponent implements OnInit { protected hasImpersonationId: boolean; protected hasPermissionToAccessHoldingsChart: boolean; protected hasPermissionToCreateActivity: boolean; - protected holdings: PortfolioPosition[]; + protected holdings: PortfolioPosition[] | undefined; protected holdingType: HoldingType = 'ACTIVE'; protected readonly holdingTypeOptions: ToggleOption[] = [ { label: $localize`Active`, value: 'ACTIVE' }, @@ -192,7 +192,7 @@ export class GfHomeHoldingsComponent implements OnInit { ); } - this.holdings = []; + this.holdings = undefined; this.fetchHoldings() .pipe(takeUntilDestroyed(this.destroyRef)) 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 fd4e63f29..eb0265af3 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -15,18 +15,16 @@ import { ChangeDetectionStrategy, Component, ElementRef, - EventEmitter, - Input, + input, OnChanges, OnDestroy, - Output, - ViewChild + output, + viewChild } from '@angular/core'; import { DataSource } from '@prisma/client'; import { Big } from 'big.js'; import type { ChartData, TooltipOptions } from 'chart.js'; -import { LinearScale } from 'chart.js'; -import { Chart, Tooltip } from 'chart.js'; +import { Chart, LinearScale, Tooltip } from 'chart.js'; import { TreemapController, TreemapElement } from 'chartjs-chart-treemap'; import { isUUID } from 'class-validator'; import { differenceInDays, max } from 'date-fns'; @@ -52,33 +50,31 @@ const { gray, green, red } = OpenColor; export class GfTreemapChartComponent implements AfterViewInit, OnChanges, OnDestroy { - @Input() baseCurrency: string; - @Input() colorScheme: ColorScheme; - @Input() cursor: string; - @Input() dateRange: DateRange; - @Input() holdings: PortfolioPosition[]; - @Input() locale = getLocale(); + public readonly baseCurrency = input.required(); + public readonly colorScheme = input.required(); + public readonly cursor = input.required(); + public readonly dateRange = input.required(); + public readonly holdings = input(); + public readonly locale = input(getLocale()); - @Output() treemapChartClicked = new EventEmitter(); + public readonly treemapChartClicked = output(); - @ViewChild('chartCanvas') chartCanvas: ElementRef; + protected isLoading = true; - public chart: Chart<'treemap'>; - public isLoading = true; + private chart: Chart<'treemap'>; + private readonly chartCanvas = + viewChild.required>('chartCanvas'); public constructor() { Chart.register(LinearScale, Tooltip, TreemapController, TreemapElement); } + public ngAfterViewInit() { - if (this.holdings) { - this.initialize(); - } + this.initialize(); } public ngOnChanges() { - if (this.holdings) { - this.initialize(); - } + this.initialize(); } public ngOnDestroy() { @@ -159,13 +155,19 @@ export class GfTreemapChartComponent } private initialize() { + const holdings = this.holdings(); + + if (!holdings) { + return; + } + this.isLoading = true; const { endDate, startDate } = getIntervalFromDateRange({ - dateRange: this.dateRange + dateRange: this.dateRange() }); - const netPerformancePercentsWithCurrencyEffect = this.holdings.map( + const netPerformancePercentsWithCurrencyEffect = holdings.map( ({ dateOfFirstActivity, netPerformancePercentWithCurrencyEffect }) => { return getAnnualizedPerformancePercent({ daysInMarket: differenceInDays( @@ -301,12 +303,12 @@ export class GfTreemapChartComponent }, spacing: 1, // @ts-expect-error: should be PortfolioPosition[] - tree: this.holdings + tree: this.holdings() } ] }; - if (this.chartCanvas) { + if (this.chartCanvas()) { if (this.chart) { this.chart.data = data; this.chart.options.plugins ??= {}; @@ -315,7 +317,7 @@ export class GfTreemapChartComponent this.chart.update(); } else { - this.chart = new Chart<'treemap'>(this.chartCanvas.nativeElement, { + this.chart = new Chart<'treemap'>(this.chartCanvas().nativeElement, { data, options: { animation: false, @@ -339,9 +341,9 @@ export class GfTreemapChartComponent } catch {} }, onHover: (event, chartElement) => { - if (this.cursor) { + if (this.cursor()) { (event.native?.target as HTMLElement).style.cursor = - chartElement[0] ? this.cursor : 'default'; + chartElement[0] ? this.cursor() : 'default'; } }, plugins: { @@ -359,9 +361,9 @@ export class GfTreemapChartComponent private getTooltipPluginConfiguration(): Partial> { return { ...getTooltipOptions({ - colorScheme: this.colorScheme, - currency: this.baseCurrency, - locale: this.locale + colorScheme: this.colorScheme(), + currency: this.baseCurrency(), + locale: this.locale() }), // @ts-expect-error: no need to set all attributes in callbacks callbacks: { @@ -381,19 +383,19 @@ export class GfTreemapChartComponent return [ `${name ?? symbol} (${allocationInPercentage})`, - `${value?.toLocaleString(this.locale, { + `${value?.toLocaleString(this.locale(), { maximumFractionDigits: 2, minimumFractionDigits: 2 - })} ${this.baseCurrency}`, + })} ${this.baseCurrency()}`, '', $localize`Change` + ' (' + $localize`Performance` + ')', `${sign}${raw._data.netPerformanceWithCurrencyEffect.toLocaleString( - this.locale, + this.locale(), { maximumFractionDigits: 2, minimumFractionDigits: 2 } - )} ${this.baseCurrency} (${netPerformanceInPercentageWithSign})` + )} ${this.baseCurrency()} (${netPerformanceInPercentageWithSign})` ]; } else { return [ diff --git a/package-lock.json b/package-lock.json index 9cc8f9160..00128128e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.25.0", + "version": "3.26.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.25.0", + "version": "3.26.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index c484e3617..384ca8a8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.25.0", + "version": "3.26.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio",