diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts index 7f03ea57f..b4565f6cd 100644 --- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts +++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -21,6 +21,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, + type ElementRef, EventEmitter, Input, OnChanges, @@ -42,7 +43,8 @@ import { PointElement, TimeScale, Tooltip, - TooltipPosition + type TooltipOptions, + type TooltipPosition } from 'chart.js'; import 'chartjs-adapter-date-fns'; import annotationPlugin from 'chartjs-plugin-annotation'; @@ -78,7 +80,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { @Output() benchmarkChanged = new EventEmitter(); - @ViewChild('chartCanvas') chartCanvas; + @ViewChild('chartCanvas') chartCanvas: ElementRef; public chart: Chart<'line'>; public hasPermissionToAccessAdminControl: boolean; @@ -158,7 +160,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { if (this.chart) { this.chart.data = data; this.chart.options.plugins.tooltip = - this.getTooltipPluginConfiguration() as unknown; + this.getTooltipPluginConfiguration(); this.chart.update(); } else { this.chart = new Chart(this.chartCanvas.nativeElement, { @@ -193,10 +195,11 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { display: false }, tooltip: this.getTooltipPluginConfiguration(), + // @ts-ignore verticalHoverLine: { color: `rgba(${getTextColor(this.colorScheme)}, 0.1)` } - } as unknown, + }, responsive: true, scales: { x: { @@ -253,7 +256,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { } } - private getTooltipPluginConfiguration() { + private getTooltipPluginConfiguration(): Partial> { return { ...getTooltipOptions({ colorScheme: this.colorScheme, @@ -261,7 +264,8 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { unit: '%' }), mode: 'index', - position: 'top' as unknown, + // @ts-ignore + position: 'top', xAlign: 'center', yAlign: 'bottom' }; diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.ts b/apps/client/src/app/components/investment-chart/investment-chart.component.ts index 5492ddd4c..c4d2805eb 100644 --- a/apps/client/src/app/components/investment-chart/investment-chart.component.ts +++ b/apps/client/src/app/components/investment-chart/investment-chart.component.ts @@ -20,6 +20,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, + type ElementRef, Input, OnChanges, OnDestroy, @@ -36,7 +37,8 @@ import { PointElement, TimeScale, Tooltip, - TooltipPosition + type TooltipOptions, + type TooltipPosition } from 'chart.js'; import 'chartjs-adapter-date-fns'; import annotationPlugin from 'chartjs-plugin-annotation'; @@ -62,7 +64,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { @Input() locale = getLocale(); @Input() savingsRate = 0; - @ViewChild('chartCanvas') chartCanvas; + @ViewChild('chartCanvas') chartCanvas: ElementRef; public chart: Chart<'bar' | 'line'>; private investments: InvestmentItem[]; @@ -121,12 +123,12 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { }), label: this.benchmarkDataLabel, segment: { - borderColor: (context: unknown) => + borderColor: (context) => this.isInFuture( context, `rgba(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b}, 0.67)` ), - borderDash: (context: unknown) => this.isInFuture(context, [2, 2]) + borderDash: (context) => this.isInFuture(context, [2, 2]) }, stepped: true }, @@ -143,12 +145,12 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { label: $localize`Total Amount`, pointRadius: 0, segment: { - borderColor: (context: unknown) => + borderColor: (context) => this.isInFuture( context, `rgba(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b}, 0.67)` ), - borderDash: (context: unknown) => this.isInFuture(context, [2, 2]) + borderDash: (context) => this.isInFuture(context, [2, 2]) } } ] @@ -158,7 +160,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { if (this.chart) { this.chart.data = chartData; this.chart.options.plugins.tooltip = - this.getTooltipPluginConfiguration() as unknown; + this.getTooltipPluginConfiguration(); if ( this.savingsRate && @@ -201,7 +203,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { color: 'white', content: $localize`Savings Rate`, display: true, - font: { size: '10px', weight: 'normal' }, + font: { size: 10, weight: 'normal' }, padding: { x: 4, y: 2 @@ -226,10 +228,11 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { display: false }, tooltip: this.getTooltipPluginConfiguration(), + // @ts-ignore verticalHoverLine: { color: `rgba(${getTextColor(this.colorScheme)}, 0.1)` } - } as unknown, + }, responsive: true, scales: { x: { @@ -286,7 +289,9 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { } } - private getTooltipPluginConfiguration() { + private getTooltipPluginConfiguration(): Partial< + TooltipOptions<'bar' | 'line'> + > { return { ...getTooltipOptions({ colorScheme: this.colorScheme, @@ -296,7 +301,8 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { unit: this.isInPercent ? '%' : undefined }), mode: 'index', - position: 'top' as unknown, + // @ts-ignore + position: 'top', xAlign: 'center', yAlign: 'bottom' }; diff --git a/libs/common/src/lib/chart-helper.ts b/libs/common/src/lib/chart-helper.ts index da6473645..2c34442c0 100644 --- a/libs/common/src/lib/chart-helper.ts +++ b/libs/common/src/lib/chart-helper.ts @@ -1,10 +1,5 @@ import type { ElementRef } from '@angular/core'; -import type { - Chart, - ChartTypeRegistry, - Plugin, - TooltipPosition -} from 'chart.js'; +import type { Chart, Plugin, TooltipOptions, TooltipPosition } from 'chart.js'; import { format } from 'date-fns'; import { @@ -21,7 +16,7 @@ export function formatGroupedDate({ date, groupBy }: { - date: Date; + date: number; groupBy: GroupBy; }) { if (groupBy === 'month') { @@ -45,27 +40,32 @@ export function getTooltipOptions({ groupBy?: GroupBy; locale?: string; unit?: string; -}) { +}): Partial { return { backgroundColor: getBackgroundColor(colorScheme), bodyColor: `rgb(${getTextColor(colorScheme)})`, borderWidth: 1, borderColor: `rgba(${getTextColor(colorScheme)}, 0.1)`, + // @ts-ignore callbacks: { label: (context) => { let label = context.dataset.label ?? ''; if (label) { label += ': '; } + // @ts-ignore if (context.parsed.y !== null) { if (currency) { + // @ts-ignore label += `${context.parsed.y.toLocaleString(locale, { maximumFractionDigits: 2, minimumFractionDigits: 2 })} ${currency}`; } else if (unit) { + // @ts-ignore label += `${context.parsed.y.toFixed(2)} ${unit}`; } else { + // @ts-ignore label += context.parsed.y.toFixed(2); } } @@ -73,6 +73,7 @@ export function getTooltipOptions({ }, title: (contexts) => { if (groupBy) { + // @ts-ignore return formatGroupedDate({ groupBy, date: contexts[0].parsed.x }); } @@ -91,8 +92,8 @@ export function getTooltipOptions({ }; } -export function getTooltipPositionerMapTop( - chart: Chart, +export function getTooltipPositionerMapTop( + chart: Chart, position: TooltipPosition ) { if (!position || !chart?.chartArea) { @@ -104,7 +105,7 @@ export function getTooltipPositionerMapTop( }; } -export function getVerticalHoverLinePlugin( +export function getVerticalHoverLinePlugin( chartCanvas: ElementRef, colorScheme: ColorScheme ): Plugin { diff --git a/libs/common/src/lib/interfaces/activities.interface.ts b/libs/common/src/lib/interfaces/activities.interface.ts index 8c88cc2cf..b9e64984b 100644 --- a/libs/common/src/lib/interfaces/activities.interface.ts +++ b/libs/common/src/lib/interfaces/activities.interface.ts @@ -8,7 +8,7 @@ export interface Activity extends Order { error?: ActivityError; feeInAssetProfileCurrency: number; feeInBaseCurrency: number; - SymbolProfile?: EnhancedSymbolProfile; + SymbolProfile: EnhancedSymbolProfile; tagIds?: string[]; tags?: Tag[]; unitPriceInAssetProfileCurrency: number; diff --git a/libs/common/src/lib/interfaces/lookup-item.interface.ts b/libs/common/src/lib/interfaces/lookup-item.interface.ts index fa91ed690..6cedeca09 100644 --- a/libs/common/src/lib/interfaces/lookup-item.interface.ts +++ b/libs/common/src/lib/interfaces/lookup-item.interface.ts @@ -7,7 +7,7 @@ export interface LookupItem { assetSubClass: AssetSubClass; currency: string; dataProviderInfo: DataProviderInfo; - dataSource: DataSource; + dataSource: DataSource | null; name: string; symbol: string; } diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.ts b/libs/ui/src/lib/accounts-table/accounts-table.component.ts index fe91e1eda..8566c122d 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.ts +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.ts @@ -77,7 +77,7 @@ export class GfAccountsTableComponent implements OnChanges, OnDestroy { @ViewChild(MatSort) sort: MatSort; public dataSource = new MatTableDataSource(); - public displayedColumns = []; + public displayedColumns: string[] = []; public isLoading = true; public routeQueryParams: Subscription; diff --git a/libs/ui/src/lib/activities-table/activities-table.component.stories.ts b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts index e7a2ba819..25463e576 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.stories.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts @@ -59,7 +59,7 @@ const activities: Activity[] = [ SymbolProfile: { assetClass: 'EQUITY', assetSubClass: 'ETF', - comment: null, + comment: undefined, countries: [], createdAt: new Date('2021-06-06T16:12:20.982Z'), currency: 'USD', @@ -74,12 +74,12 @@ const activities: Activity[] = [ isin: 'US9220427424', name: 'Vanguard Total World Stock Index Fund ETF Shares', updatedAt: new Date('2025-10-01T20:09:39.500Z'), - scraperConfiguration: null, + scraperConfiguration: undefined, sectors: [], symbol: 'VT', symbolMapping: {}, url: 'https://www.vanguard.com', - userId: null, + userId: undefined, activitiesCount: 267, dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') }, @@ -126,7 +126,7 @@ const activities: Activity[] = [ SymbolProfile: { assetClass: 'EQUITY', assetSubClass: 'ETF', - comment: null, + comment: undefined, countries: [], createdAt: new Date('2021-06-06T16:12:20.982Z'), currency: 'USD', @@ -141,12 +141,12 @@ const activities: Activity[] = [ isin: 'US9220427424', name: 'Vanguard Total World Stock Index Fund ETF Shares', updatedAt: new Date('2025-10-01T20:09:39.500Z'), - scraperConfiguration: null, + scraperConfiguration: undefined, sectors: [], symbol: 'VT', symbolMapping: {}, url: 'https://www.vanguard.com', - userId: null, + userId: undefined, activitiesCount: 267, dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') }, @@ -193,7 +193,7 @@ const activities: Activity[] = [ SymbolProfile: { assetClass: 'LIQUIDITY', assetSubClass: 'CRYPTOCURRENCY', - comment: null, + comment: undefined, countries: [], createdAt: new Date('2024-03-12T15:15:21.217Z'), currency: 'USD', @@ -208,12 +208,12 @@ const activities: Activity[] = [ isin: 'CA4639181029', name: 'iShares Bitcoin Trust', updatedAt: new Date('2025-09-29T03:14:07.742Z'), - scraperConfiguration: null, + scraperConfiguration: undefined, sectors: [], symbol: 'IBIT', symbolMapping: {}, url: 'https://www.ishares.com', - userId: null, + userId: undefined, activitiesCount: 6, dateOfFirstActivity: new Date('2024-01-01T08:00:00.000Z') }, @@ -280,7 +280,7 @@ const activities: Activity[] = [ symbol: 'BNDW', symbolMapping: {}, url: 'https://vanguard.com', - userId: null, + userId: undefined, activitiesCount: 38, dateOfFirstActivity: new Date('2022-04-13T20:05:48.742Z') }, @@ -327,7 +327,7 @@ const activities: Activity[] = [ SymbolProfile: { assetClass: 'EQUITY', assetSubClass: 'ETF', - comment: null, + comment: undefined, countries: [], createdAt: new Date('2021-06-06T16:12:20.982Z'), currency: 'USD', @@ -342,12 +342,12 @@ const activities: Activity[] = [ isin: 'US9220427424', name: 'Vanguard Total World Stock Index Fund ETF Shares', updatedAt: new Date('2025-10-01T20:09:39.500Z'), - scraperConfiguration: null, + scraperConfiguration: undefined, sectors: [], symbol: 'VT', symbolMapping: {}, url: 'https://www.vanguard.com', - userId: null, + userId: undefined, activitiesCount: 267, dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') }, diff --git a/libs/ui/src/lib/activities-table/activities-table.component.ts b/libs/ui/src/lib/activities-table/activities-table.component.ts index e53f37872..4b43d5333 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.ts @@ -132,7 +132,7 @@ export class GfActivitiesTableComponent @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - public displayedColumns = []; + public displayedColumns: string[] = []; public endOfToday = endOfToday(); public hasDrafts = false; public hasErrors = false; diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts index 6b0bc8dcb..a6057f124 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -38,8 +38,11 @@ import { BarElement, CategoryScale, Chart, + type ChartData, + type ChartDataset, LinearScale, - Tooltip + Tooltip, + type TooltipOptions } from 'chart.js'; import 'chartjs-adapter-date-fns'; import Color from 'color'; @@ -98,10 +101,15 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { @ViewChild('chartCanvas') chartCanvas: ElementRef; public calculatorForm = this.formBuilder.group({ + // @ts-ignore annualInterestRate: new FormControl(undefined), + // @ts-ignore paymentPerPeriod: new FormControl(undefined), + // @ts-ignore principalInvestmentAmount: new FormControl(undefined), + // @ts-ignore projectedTotalAmount: new FormControl(undefined), + // @ts-ignore retirementDate: new FormControl(undefined) }); public chart: Chart<'bar'>; @@ -148,25 +156,25 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { this.calculatorForm .get('annualInterestRate') - .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + ?.valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((annualInterestRate) => { this.annualInterestRateChanged.emit(annualInterestRate); }); this.calculatorForm .get('paymentPerPeriod') - .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + ?.valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((savingsRate) => { this.savingsRateChanged.emit(savingsRate); }); this.calculatorForm .get('projectedTotalAmount') - .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + ?.valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((projectedTotalAmount) => { this.projectedTotalAmountChanged.emit(projectedTotalAmount); }); this.calculatorForm .get('retirementDate') - .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + ?.valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((retirementDate) => { this.retirementDateChanged.emit(retirementDate); }); @@ -194,11 +202,11 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { this.calculatorForm.patchValue( { annualInterestRate: - this.calculatorForm.get('annualInterestRate').value, + this.calculatorForm.get('annualInterestRate')?.value, paymentPerPeriod: this.getPMT(), principalInvestmentAmount: this.calculatorForm.get( 'principalInvestmentAmount' - ).value, + )?.value, projectedTotalAmount: Math.round(this.getProjectedTotalAmount()) || 0, retirementDate: @@ -208,7 +216,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { emitEvent: false } ); - this.calculatorForm.get('principalInvestmentAmount').disable(); + this.calculatorForm.get('principalInvestmentAmount')?.disable(); this.changeDetectorRef.markForCheck(); }); @@ -217,34 +225,36 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { if (this.hasPermissionToUpdateUserSettings === true) { this.calculatorForm .get('annualInterestRate') - .enable({ emitEvent: false }); - this.calculatorForm.get('paymentPerPeriod').enable({ emitEvent: false }); + ?.enable({ emitEvent: false }); + this.calculatorForm.get('paymentPerPeriod')?.enable({ emitEvent: false }); this.calculatorForm .get('projectedTotalAmount') - .enable({ emitEvent: false }); + ?.enable({ emitEvent: false }); } else { this.calculatorForm .get('annualInterestRate') - .disable({ emitEvent: false }); - this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false }); + ?.disable({ emitEvent: false }); + this.calculatorForm + .get('paymentPerPeriod') + ?.disable({ emitEvent: false }); this.calculatorForm .get('projectedTotalAmount') - .disable({ emitEvent: false }); + ?.disable({ emitEvent: false }); } - this.calculatorForm.get('retirementDate').disable({ emitEvent: false }); + this.calculatorForm.get('retirementDate')?.disable({ emitEvent: false }); } public setMonthAndYear( normalizedMonthAndYear: Date, datepicker: MatDatepicker ) { - const retirementDate = this.calculatorForm.get('retirementDate').value; + const retirementDate = this.calculatorForm.get('retirementDate')!.value; const newRetirementDate = setMonth( setYear(retirementDate, normalizedMonthAndYear.getFullYear()), normalizedMonthAndYear.getMonth() ); - this.calculatorForm.get('retirementDate').setValue(newRetirementDate); + this.calculatorForm.get('retirementDate')?.setValue(newRetirementDate); datepicker.close(); } @@ -270,7 +280,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { this.chart.update(); } else { - this.chart = new Chart(this.chartCanvas.nativeElement, { + this.chart = new Chart<'bar'>(this.chartCanvas.nativeElement, { data: chartData, options: { plugins: { @@ -280,6 +290,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { callbacks: { footer: (items) => { const totalAmount = items.reduce( + // @ts-ignore (a, b) => a + b.parsed.y, 0 ); @@ -312,7 +323,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { return label; } } - } + } as TooltipOptions<'bar'> }, responsive: true, scales: { @@ -345,9 +356,9 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { this.isLoading = false; } - private getChartData() { + private getChartData(): ChartData<'bar'> { const currentYear = new Date().getFullYear(); - const labels = []; + const labels: number[] = []; // Principal investment amount const P: number = this.getP(); @@ -360,6 +371,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { // Calculate retirement date // if we want to retire at month x, we need the projectedTotalAmount at month x-1 + // @ts-ignore const lastPeriodDate = sub(this.getRetirementDate(), { months: 1 }); const yearsToRetire = lastPeriodDate.getFullYear() - currentYear; @@ -371,13 +383,13 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { labels.push(year); } - const datasetDeposit = { + const datasetDeposit: ChartDataset<'bar'> = { backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`, data: [], label: $localize`Deposit` }; - const datasetInterest = { + const datasetInterest: ChartDataset<'bar'> = { backgroundColor: Color( `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})` ) @@ -387,7 +399,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { label: $localize`Interest` }; - const datasetSavings = { + const datasetSavings: ChartDataset<'bar'> = { backgroundColor: Color( `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})` ) @@ -426,12 +438,12 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { } private getPeriodsToRetire(): number { - if (this.calculatorForm.get('projectedTotalAmount').value) { + if (this.calculatorForm.get('projectedTotalAmount')?.value) { let periods = this.fireCalculatorService.calculatePeriodsToRetire({ P: this.getP(), PMT: this.getPMT(), r: this.getR(), - totalAmount: this.calculatorForm.get('projectedTotalAmount').value + totalAmount: this.calculatorForm.get('projectedTotalAmount')!.value }); if (periods === Infinity) { @@ -452,13 +464,13 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { } } - private getPMT() { - return this.calculatorForm.get('paymentPerPeriod').value; + private getPMT(): number { + return this.calculatorForm.get('paymentPerPeriod')!.value; } private getProjectedTotalAmount() { - if (this.calculatorForm.get('projectedTotalAmount').value) { - return this.calculatorForm.get('projectedTotalAmount').value; + if (this.calculatorForm.get('projectedTotalAmount')?.value) { + return this.calculatorForm.get('projectedTotalAmount')!.value; } const { totalAmount } = @@ -473,10 +485,10 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { } private getR() { - return this.calculatorForm.get('annualInterestRate').value / 100; + return this.calculatorForm.get('annualInterestRate')!.value / 100; } - private getRetirementDate(): Date { + private getRetirementDate(): Date | undefined { if (this.periodsToRetire === Number.MAX_SAFE_INTEGER) { return undefined; } diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts index f408f7d9b..3abb9903e 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts @@ -62,7 +62,7 @@ export class GfHoldingsTableComponent implements OnChanges, OnDestroy { @ViewChild(MatSort) sort: MatSort; public dataSource = new MatTableDataSource(); - public displayedColumns = []; + public displayedColumns: string[] = []; public ignoreAssetSubClasses = [AssetSubClass.CASH]; public isLoading = true; public routeQueryParams: Subscription; diff --git a/libs/ui/src/lib/line-chart/line-chart.component.ts b/libs/ui/src/lib/line-chart/line-chart.component.ts index 0afef5959..0d1a709b5 100644 --- a/libs/ui/src/lib/line-chart/line-chart.component.ts +++ b/libs/ui/src/lib/line-chart/line-chart.component.ts @@ -19,12 +19,14 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + type ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core'; import { + type AnimationSpec, Chart, Filler, LinearScale, @@ -33,7 +35,8 @@ import { PointElement, TimeScale, Tooltip, - TooltipPosition + type TooltipOptions, + type TooltipPosition } from 'chart.js'; import 'chartjs-adapter-date-fns'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -67,7 +70,7 @@ export class GfLineChartComponent @Input() yMin: number; @Input() yMinLabel: string; - @ViewChild('chartCanvas') chartCanvas; + @ViewChild('chartCanvas') chartCanvas: ElementRef; public chart: Chart<'line'>; public isLoading = true; @@ -117,9 +120,9 @@ export class GfLineChartComponent private initialize() { this.isLoading = true; - const benchmarkPrices = []; + const benchmarkPrices: number[] = []; const labels: string[] = []; - const marketPrices = []; + const marketPrices: number[] = []; this.historicalDataItems?.forEach((historicalDataItem, index) => { benchmarkPrices.push(this.benchmarkDataItems?.[index]?.value); @@ -129,10 +132,11 @@ export class GfLineChartComponent const gradient = this.chartCanvas?.nativeElement ?.getContext('2d') - .createLinearGradient( + ?.createLinearGradient( 0, 0, 0, + // @ts-ignore (this.chartCanvas.nativeElement.parentNode.offsetHeight * 4) / 5 ); @@ -171,25 +175,28 @@ export class GfLineChartComponent if (this.chartCanvas) { if (this.chart) { this.chart.data = data; + + if (!this.chart.options.plugins) { + this.chart.options.plugins = {}; + } + this.chart.options.plugins.tooltip = - this.getTooltipPluginConfiguration() as unknown; - this.chart.options.animation = - this.isAnimated && - ({ - x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }), - y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' }) - } as unknown); + this.getTooltipPluginConfiguration(); + this.chart.options.animation = this.isAnimated && { + // @ts-ignore + x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }), + y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' }) + }; this.chart.update(); } else { this.chart = new Chart(this.chartCanvas.nativeElement, { data, options: { - animation: - this.isAnimated && - ({ - x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }), - y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' }) - } as unknown), + animation: this.isAnimated && { + // @ts-ignore + x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }), + y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' }) + }, aspectRatio: 16 / 9, elements: { point: { @@ -205,10 +212,11 @@ export class GfLineChartComponent position: 'bottom' }, tooltip: this.getTooltipPluginConfiguration(), + // @ts-ignore verticalHoverLine: { color: `rgba(${getTextColor(this.colorScheme)}, 0.1)` } - } as unknown, + }, scales: { x: { border: { @@ -298,7 +306,7 @@ export class GfLineChartComponent }: { axis: 'x' | 'y'; labels: string[]; - }) { + }): AnimationSpec<'line'> { const delayBetweenPoints = this.ANIMATION_DURATION / labels.length; return { @@ -308,16 +316,19 @@ export class GfLineChartComponent } context[`${axis}Started`] = true; + // @ts-ignore return context.index * delayBetweenPoints; }, duration: delayBetweenPoints, easing: 'linear', + // @ts-ignore from: NaN, + // @ts-ignore type: 'number' }; } - private getTooltipPluginConfiguration() { + private getTooltipPluginConfiguration(): Partial> { return { ...getTooltipOptions({ colorScheme: this.colorScheme, @@ -326,7 +337,8 @@ export class GfLineChartComponent unit: this.unit }), mode: 'index', - position: 'top' as unknown, + // @ts-ignore + position: 'top', xAlign: 'center', yAlign: 'bottom' }; diff --git a/libs/ui/src/lib/mocks/holdings.ts b/libs/ui/src/lib/mocks/holdings.ts index 0f30b3e6f..d162d7049 100644 --- a/libs/ui/src/lib/mocks/holdings.ts +++ b/libs/ui/src/lib/mocks/holdings.ts @@ -162,7 +162,7 @@ export const holdings: PortfolioPosition[] = [ symbol: 'bitcoin', tags: [], transactionCount: 1, - url: null, + url: undefined, valueInBaseCurrency: 54666.7898248 }, { diff --git a/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.ts b/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.ts index 8691dc998..9ca91d111 100644 --- a/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.ts +++ b/libs/ui/src/lib/no-transactions-info/no-transactions-info.component.ts @@ -24,5 +24,5 @@ export class GfNoTransactionsInfoComponent { @HostBinding('class.has-border') @Input() hasBorder = true; public routerLinkPortfolioActivities = - internalRoutes.portfolio.subRoutes.activities.routerLink; + internalRoutes.portfolio.subRoutes?.activities.routerLink; } diff --git a/libs/ui/src/lib/notifications/alert-dialog/alert-dialog.component.ts b/libs/ui/src/lib/notifications/alert-dialog/alert-dialog.component.ts index 33d26c493..acd63d995 100644 --- a/libs/ui/src/lib/notifications/alert-dialog/alert-dialog.component.ts +++ b/libs/ui/src/lib/notifications/alert-dialog/alert-dialog.component.ts @@ -11,8 +11,8 @@ import { AlertDialogParams } from './interfaces/interfaces'; templateUrl: './alert-dialog.html' }) export class GfAlertDialogComponent { - public discardLabel: string; - public message: string; + public discardLabel?: string; + public message?: string; public title: string; public constructor(public dialogRef: MatDialogRef) {} diff --git a/libs/ui/src/lib/notifications/confirmation-dialog/confirmation-dialog.component.ts b/libs/ui/src/lib/notifications/confirmation-dialog/confirmation-dialog.component.ts index a3bc053ee..ccbb3e737 100644 --- a/libs/ui/src/lib/notifications/confirmation-dialog/confirmation-dialog.component.ts +++ b/libs/ui/src/lib/notifications/confirmation-dialog/confirmation-dialog.component.ts @@ -13,10 +13,10 @@ import { ConfirmDialogParams } from './interfaces/interfaces'; templateUrl: './confirmation-dialog.html' }) export class GfConfirmationDialogComponent { - public confirmLabel: string; + public confirmLabel?: string; public confirmType: ConfirmationDialogType; - public discardLabel: string; - public message: string; + public discardLabel?: string; + public message?: string; public title: string; public constructor( diff --git a/libs/ui/src/lib/notifications/prompt-dialog/prompt-dialog.component.ts b/libs/ui/src/lib/notifications/prompt-dialog/prompt-dialog.component.ts index 6ea68d863..f62116c83 100644 --- a/libs/ui/src/lib/notifications/prompt-dialog/prompt-dialog.component.ts +++ b/libs/ui/src/lib/notifications/prompt-dialog/prompt-dialog.component.ts @@ -18,25 +18,25 @@ import { MatInputModule } from '@angular/material/input'; }) export class GfPromptDialogComponent { public confirmLabel: string; - public defaultValue: string; + public defaultValue?: string; public discardLabel: string; public formControl = new FormControl(''); public title: string; - public valueLabel: string; + public valueLabel?: string; public constructor(public dialogRef: MatDialogRef) {} public initialize(aParams: { - confirmLabel?: string; + confirmLabel: string; defaultValue?: string; - discardLabel?: string; + discardLabel: string; title: string; valueLabel?: string; }) { this.confirmLabel = aParams.confirmLabel; this.defaultValue = aParams.defaultValue; this.discardLabel = aParams.discardLabel; - this.formControl.setValue(aParams.defaultValue); + this.formControl.setValue(aParams.defaultValue ?? null); this.title = aParams.title; this.valueLabel = aParams.valueLabel; } diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts index afbe5af4e..b83a43aa8 100644 --- a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts @@ -70,10 +70,10 @@ export class GfPortfolioFilterFormComponent private formBuilder: FormBuilder ) { this.filterForm = this.formBuilder.group({ - account: new FormControl(null), - assetClass: new FormControl(null), - holding: new FormControl(null), - tag: new FormControl(null) + account: new FormControl(null), + assetClass: new FormControl(null), + holding: new FormControl(null), + tag: new FormControl(null) }); } diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts index fb11897eb..080847c2f 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts @@ -22,11 +22,18 @@ import { } from '@angular/core'; import { DataSource } from '@prisma/client'; import { Big } from 'big.js'; -import { ChartConfiguration, Tooltip } from 'chart.js'; -import { LinearScale } from 'chart.js'; -import { ArcElement } from 'chart.js'; -import { DoughnutController } from 'chart.js'; -import { Chart } from 'chart.js'; +import { + ArcElement, + Chart, + type ChartData, + type ChartDataset, + DoughnutController, + LinearScale, + type Plugin, + Tooltip, + type TooltipItem, + type TooltipOptions +} from 'chart.js'; import ChartDataLabels from 'chartjs-plugin-datalabels'; import { isUUID } from 'class-validator'; import Color from 'color'; @@ -135,16 +142,18 @@ export class GfPortfolioProportionChartComponent if ( chartData[this.data[symbol][this.keys[0]].toUpperCase()] - .subCategory[this.data[symbol][this.keys[1]]] + ?.subCategory?.[this.data[symbol][this.keys[1]]] ) { + // @ts-ignore chartData[ this.data[symbol][this.keys[0]].toUpperCase() ].subCategory[this.data[symbol][this.keys[1]]].value = chartData[ this.data[symbol][this.keys[0]].toUpperCase() - ].subCategory[this.data[symbol][this.keys[1]]].value.plus( + ].subCategory?.[this.data[symbol][this.keys[1]]].value.plus( this.data[symbol].value || 0 ); } else { + // @ts-ignore chartData[ this.data[symbol][this.keys[0]].toUpperCase() ].subCategory[this.data[symbol][this.keys[1]] ?? UNKNOWN_KEY] = { @@ -273,12 +282,14 @@ export class GfPortfolioProportionChartComponent Object.keys(item.subCategory ?? {}).forEach((subCategory) => { if (item.name === UNKNOWN_KEY) { + // @ts-ignore backgroundColorSubCategory.push(item.color); } else { backgroundColorSubCategory.push( Color(item.color).lighten(lightnessRatio).hex() ); } + // @ts-ignore dataSubCategory.push(item.subCategory[subCategory].value.toNumber()); labelSubCategory.push(subCategory); @@ -286,7 +297,7 @@ export class GfPortfolioProportionChartComponent }); }); - const datasets: ChartConfiguration<'doughnut'>['data']['datasets'] = [ + const datasets: ChartDataset<'doughnut'>[] = [ { backgroundColor: chartDataSorted.map(([, item]) => { return item.color; @@ -324,7 +335,7 @@ export class GfPortfolioProportionChartComponent datasets[1].data[1] = Number.MAX_SAFE_INTEGER; } - const data: ChartConfiguration<'doughnut'>['data'] = { + const data: ChartData<'doughnut'> = { datasets, labels }; @@ -332,9 +343,13 @@ export class GfPortfolioProportionChartComponent if (this.chartCanvas) { if (this.chart) { this.chart.data = data; - this.chart.options.plugins.tooltip = this.getTooltipPluginConfiguration( - data - ) as unknown; + + if (!this.chart.options.plugins) { + this.chart.options.plugins = {}; + } + + this.chart.options.plugins.tooltip = + this.getTooltipPluginConfiguration(data); this.chart.update(); } else { this.chart = new Chart<'doughnut'>(this.chartCanvas.nativeElement, { @@ -348,15 +363,17 @@ export class GfPortfolioProportionChartComponent onClick: (event, activeElements) => { try { const dataIndex = activeElements[0].index; + // @ts-ignore const symbol: string = event.chart.data.labels[dataIndex]; - const dataSource = this.data[symbol]?.dataSource; + const dataSource = this.data[symbol].dataSource!; this.proportionChartClicked.emit({ dataSource, symbol }); } catch {} }, onHover: (event, chartElement) => { if (this.cursor) { + // @ts-ignore event.native.target.style.cursor = chartElement[0] ? this.cursor : 'default'; @@ -392,8 +409,8 @@ export class GfPortfolioProportionChartComponent legend: { display: false }, tooltip: this.getTooltipPluginConfiguration(data) } - } as unknown, - plugins: [ChartDataLabels], + }, + plugins: [ChartDataLabels as Plugin<'doughnut'>], type: 'doughnut' }); } @@ -419,15 +436,18 @@ export class GfPortfolioProportionChartComponent ]; } - private getTooltipPluginConfiguration(data: ChartConfiguration['data']) { + private getTooltipPluginConfiguration( + data: ChartData<'doughnut'> + ): Partial> { return { ...getTooltipOptions({ colorScheme: this.colorScheme, currency: this.baseCurrency, locale: this.locale }), + // @ts-ignore callbacks: { - label: (context) => { + label: (context: TooltipItem<'doughnut'>) => { const labelIndex = (data.datasets[context.datasetIndex - 1]?.data?.length ?? 0) + context.dataIndex; diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index 145f134e3..a10798401 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -132,7 +132,7 @@ export class AdminService { public fetchJobs({ status }: { status?: JobStatus[] }) { let params = new HttpParams(); - if (status?.length > 0) { + if (status && status.length > 0) { params = params.append('status', status.join(',')); } @@ -158,8 +158,8 @@ export class AdminService { }) { let params = new HttpParams(); - params = params.append('skip', skip); - params = params.append('take', take); + if (skip) params = params.append('skip', skip); + if (take) params = params.append('take', take); return this.http.get('/api/v1/admin/user', { params }); } diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 37443cd20..d8c524f4e 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -89,7 +89,7 @@ export class DataService { public buildFiltersAsQueryParams({ filters }: { filters?: Filter[] }) { let params = new HttpParams(); - if (filters?.length > 0) { + if (filters && filters.length > 0) { const { ACCOUNT: filtersByAccount, ASSET_CLASS: filtersByAssetClass, diff --git a/libs/ui/src/lib/shared/abstract-mat-form-field.ts b/libs/ui/src/lib/shared/abstract-mat-form-field.ts index 628f0a659..ce8c447a5 100644 --- a/libs/ui/src/lib/shared/abstract-mat-form-field.ts +++ b/libs/ui/src/lib/shared/abstract-mat-form-field.ts @@ -137,7 +137,7 @@ export abstract class AbstractMatFormField public ngDoCheck() { if (this.ngControl) { - this.errorState = this.ngControl.invalid && this.ngControl.touched; + this.errorState = !!(this.ngControl.invalid && this.ngControl.touched); this.stateChanges.next(); } } diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index c74e8a077..e17044c3f 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -185,7 +185,7 @@ export class GfSymbolAutocompleteComponent public ngDoCheck() { if (this.ngControl) { this.validateRequired(); - this.errorState = this.ngControl.invalid && this.ngControl.touched; + this.errorState = !!(this.ngControl.invalid && this.ngControl.touched); this.stateChanges.next(); } } @@ -225,7 +225,7 @@ export class GfSymbolAutocompleteComponent ? !super.value?.dataSource || !super.value?.symbol : false; if (requiredCheck) { - this.ngControl.control.setErrors({ invalidData: true }); + this.ngControl.control?.setErrors({ invalidData: true }); } } } diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.ts index 7f1a8805e..6ed742019 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts @@ -77,7 +77,7 @@ export class GfTagsSelectorComponent this.tagInputControl.valueChanges .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((value) => { - this.filteredOptions.next(this.filterTags(value)); + this.filteredOptions.next(this.filterTags(value ?? '')); }); addIcons({ addCircleOutline, closeOutline }); @@ -100,21 +100,23 @@ export class GfTagsSelectorComponent if (!tag && this.hasPermissionToCreateTag) { tag = { - id: undefined, + id: '', name: event.option.value as string, userId: null }; } - this.tagsSelected.update((tags) => { - return [...(tags ?? []), tag]; - }); + if (tag) { + this.tagsSelected.update((tags) => { + return [...(tags ?? []), tag]; + }); + } const newTags = this.tagsSelected(); this.onChange(newTags); this.onTouched(); this.tagInput.nativeElement.value = ''; - this.tagInputControl.setValue(undefined); + this.tagInputControl.setValue(null); } public onRemoveTag(tag: Tag) { diff --git a/libs/ui/src/lib/toggle/toggle.component.ts b/libs/ui/src/lib/toggle/toggle.component.ts index be460f7fa..fb238556d 100644 --- a/libs/ui/src/lib/toggle/toggle.component.ts +++ b/libs/ui/src/lib/toggle/toggle.component.ts @@ -26,13 +26,15 @@ export class GfToggleComponent implements OnChanges { @Output() valueChange = new EventEmitter>(); - public optionFormControl = new FormControl(undefined); + public optionFormControl = new FormControl(null); public ngOnChanges() { this.optionFormControl.setValue(this.defaultValue); } public onValueChange() { - this.valueChange.emit({ value: this.optionFormControl.value }); + if (this.optionFormControl.value !== null) { + this.valueChange.emit({ value: this.optionFormControl.value }); + } } } diff --git a/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts b/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts index bb673ed64..0b567a8f5 100644 --- a/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts +++ b/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts @@ -1,5 +1,18 @@ +import { PortfolioPosition } from '@ghostfolio/common/interfaces'; + +import { + TreemapDataPoint, + TreemapScriptableContext +} from 'chartjs-chart-treemap'; + export interface GetColorParams { annualizedNetPerformancePercent: number; negativeNetPerformancePercentsRange: { max: number; min: number }; positiveNetPerformancePercentsRange: { max: number; min: number }; } + +export interface GfTreemapChartTooltipContext extends TreemapScriptableContext { + raw: TreemapDataPoint & { + _data: PortfolioPosition; + }; +} 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 6ae958b83..b4b7cd22a 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -25,7 +25,7 @@ import { } from '@angular/core'; import { DataSource } from '@prisma/client'; import { Big } from 'big.js'; -import { ChartConfiguration } from 'chart.js'; +import type { ChartData, TooltipOptions } from 'chart.js'; import { LinearScale } from 'chart.js'; import { Chart, Tooltip } from 'chart.js'; import { TreemapController, TreemapElement } from 'chartjs-chart-treemap'; @@ -35,7 +35,10 @@ import { orderBy } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import OpenColor from 'open-color'; -import { GetColorParams } from './interfaces/interfaces'; +import { + GetColorParams, + GfTreemapChartTooltipContext +} from './interfaces/interfaces'; const { gray, green, red } = OpenColor; @@ -198,10 +201,10 @@ export class GfTreemapChartComponent min: Math.min(...negativeNetPerformancePercents) }; - const data: ChartConfiguration<'treemap'>['data'] = { + const data: ChartData<'treemap'> = { datasets: [ { - backgroundColor: (context) => { + backgroundColor: (context: GfTreemapChartTooltipContext) => { let annualizedNetPerformancePercent = getAnnualizedPerformancePercent({ daysInMarket: differenceInDays( @@ -232,7 +235,7 @@ export class GfTreemapChartComponent key: 'allocationInPercentage', labels: { align: 'left', - color: (context) => { + color: (context: GfTreemapChartTooltipContext) => { let annualizedNetPerformancePercent = getAnnualizedPerformancePercent({ daysInMarket: differenceInDays( @@ -261,7 +264,7 @@ export class GfTreemapChartComponent }, display: true, font: [{ size: 16 }, { lineHeight: 1.5, size: 14 }], - formatter: ({ raw }) => { + formatter: ({ raw }: GfTreemapChartTooltipContext) => { // Round to 4 decimal places let netPerformancePercentWithCurrencyEffect = Math.round( @@ -286,19 +289,25 @@ export class GfTreemapChartComponent position: 'top' }, spacing: 1, + // @ts-ignore tree: this.holdings } ] - } as any; + }; if (this.chartCanvas) { if (this.chart) { this.chart.data = data; + + if (!this.chart.options.plugins) { + this.chart.options.plugins = {}; + } + this.chart.options.plugins.tooltip = - this.getTooltipPluginConfiguration() as unknown; + this.getTooltipPluginConfiguration(); this.chart.update(); } else { - this.chart = new Chart(this.chartCanvas.nativeElement, { + this.chart = new Chart<'treemap'>(this.chartCanvas.nativeElement, { data, options: { animation: false, @@ -308,6 +317,7 @@ export class GfTreemapChartComponent const datasetIndex = activeElements[0].datasetIndex; const dataset = orderBy( + // @ts-ignore event.chart.data.datasets[datasetIndex].tree, ['allocationInPercentage'], ['desc'] @@ -321,6 +331,7 @@ export class GfTreemapChartComponent }, onHover: (event, chartElement) => { if (this.cursor) { + // @ts-ignore event.native.target.style.cursor = chartElement[0] ? this.cursor : 'default'; @@ -329,7 +340,7 @@ export class GfTreemapChartComponent plugins: { tooltip: this.getTooltipPluginConfiguration() } - } as unknown, + }, type: 'treemap' }); } @@ -338,7 +349,7 @@ export class GfTreemapChartComponent this.isLoading = false; } - private getTooltipPluginConfiguration() { + private getTooltipPluginConfiguration(): Partial> { return { ...getTooltipOptions({ colorScheme: this.colorScheme, @@ -346,8 +357,9 @@ export class GfTreemapChartComponent locale: this.locale }), callbacks: { - label: ({ raw }) => { - const allocationInPercentage = `${((raw._data.allocationInPercentage as number) * 100).toFixed(2)}%`; + // @ts-ignore + label: ({ raw }: GfTreemapChartTooltipContext) => { + const allocationInPercentage = `${(raw._data.allocationInPercentage * 100).toFixed(2)}%`; const name = raw._data.name; const sign = raw._data.netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''; @@ -356,11 +368,11 @@ export class GfTreemapChartComponent const netPerformanceInPercentageWithSign = `${sign}${(raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`; if (raw._data.valueInBaseCurrency !== null) { - const value = raw._data.valueInBaseCurrency as number; + const value = raw._data.valueInBaseCurrency; return [ `${name ?? symbol} (${allocationInPercentage})`, - `${value.toLocaleString(this.locale, { + `${value?.toLocaleString(this.locale, { maximumFractionDigits: 2, minimumFractionDigits: 2 })} ${this.baseCurrency}`, diff --git a/libs/ui/src/lib/value/value.component.ts b/libs/ui/src/lib/value/value.component.ts index e24c00322..10ba3798e 100644 --- a/libs/ui/src/lib/value/value.component.ts +++ b/libs/ui/src/lib/value/value.component.ts @@ -139,7 +139,7 @@ export class GfValueComponent implements OnChanges { this.isNumber = false; this.isString = false; this.locale = this.locale || getLocale(); - this.precision = this.precision >= 0 ? this.precision : undefined; + this.precision = this.precision >= 0 ? this.precision : 0; this.useAbsoluteValue = false; } } diff --git a/libs/ui/tsconfig.json b/libs/ui/tsconfig.json index 04f4630bc..51348a52a 100644 --- a/libs/ui/tsconfig.json +++ b/libs/ui/tsconfig.json @@ -19,6 +19,7 @@ "target": "es2020", // TODO: Remove once solved in tsconfig.base.json "strict": false, + "strictNullChecks": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true },