Browse Source

Merge remote-tracking branch 'origin/main' into task/enable-eslint-prefer-nullish-coalescing

pull/7317/head
KenTandrian 22 hours ago
parent
commit
ceb631496d
  1. 2
      CHANGELOG.md
  2. 4
      apps/client/src/app/components/home-holdings/home-holdings.component.ts
  3. 74
      libs/ui/src/lib/treemap-chart/treemap-chart.component.ts
  4. 4
      package-lock.json
  5. 2
      package.json

2
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

4
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))

74
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<string>();
public readonly colorScheme = input.required<ColorScheme>();
public readonly cursor = input.required<string>();
public readonly dateRange = input.required<DateRange>();
public readonly holdings = input<PortfolioPosition[]>();
public readonly locale = input<string>(getLocale());
@Output() treemapChartClicked = new EventEmitter<AssetProfileIdentifier>();
public readonly treemapChartClicked = output<AssetProfileIdentifier>();
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
protected isLoading = true;
public chart: Chart<'treemap'>;
public isLoading = true;
private chart: Chart<'treemap'>;
private readonly chartCanvas =
viewChild.required<ElementRef<HTMLCanvasElement>>('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<TooltipOptions<'treemap'>> {
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 [

4
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": {

2
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",

Loading…
Cancel
Save