From e1d343fb700d893cd3dc0a3aaf767213a99e713e Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Wed, 11 Feb 2026 02:15:45 +0700 Subject: [PATCH 1/4] Task/improve accounts table type safety (#6301) * fix(lib): replace null with undefined in activities table stories * feat(lib): implement inject functions in accounts table * feat(lib): change to input functions * feat(lib): change displayedColumns to a computed field * feat(lib): change accounts to use input function * feat(lib): change isLoading to a computed field * fix(lib): remove unused routeQueryParams variable * feat(lib): change sort to use viewChild function * feat(lib): change dataSource to a computed field * feat(lib): remove ngOnChanges --- .../accounts-table.component.html | 16 +-- .../accounts-table.component.ts | 114 +++++++++--------- .../activities-table.component.stories.ts | 26 ++-- 3 files changed, 76 insertions(+), 80 deletions(-) diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.html b/libs/ui/src/lib/accounts-table/accounts-table.component.html index 68ae78474..63a7a89b6 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.html +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.html @@ -1,9 +1,9 @@ -@if (showActions) { +@if (showActions()) {
-@if (isLoading) { +@if (isLoading()) { (); @Output() transferBalance = new EventEmitter(); - @ViewChild(MatSort) sort: MatSort; - - public dataSource = new MatTableDataSource(); - public displayedColumns = []; - public isLoading = true; - public routeQueryParams: Subscription; - - private unsubscribeSubject = new Subject(); - - public constructor( - private notificationService: NotificationService, - private router: Router - ) { - addIcons({ - arrowRedoOutline, - createOutline, - documentTextOutline, - ellipsisHorizontal, - eyeOffOutline, - trashOutline, - walletOutline - }); - } - - public ngOnChanges() { - this.displayedColumns = ['status', 'account', 'platform']; - - if (this.showActivitiesCount) { - this.displayedColumns.push('activitiesCount'); + public readonly accounts = input.required(); + public readonly showActions = input(); + public readonly showActivitiesCount = input(true); + public readonly showAllocationInPercentage = input(); + public readonly showBalance = input(true); + public readonly showValue = input(true); + public readonly showValueInBaseCurrency = input(false); + public readonly sort = viewChild.required(MatSort); + + protected readonly dataSource = computed(() => { + const dataSource = new MatTableDataSource(this.accounts()); + dataSource.sortingDataAccessor = getLowercase; + dataSource.sort = this.sort(); + return dataSource; + }); + + protected readonly displayedColumns = computed(() => { + const columns = ['status', 'account', 'platform']; + + if (this.showActivitiesCount()) { + columns.push('activitiesCount'); } - if (this.showBalance) { - this.displayedColumns.push('balance'); + if (this.showBalance()) { + columns.push('balance'); } - if (this.showValue) { - this.displayedColumns.push('value'); + if (this.showValue()) { + columns.push('value'); } - this.displayedColumns.push('currency'); + columns.push('currency'); - if (this.showValueInBaseCurrency) { - this.displayedColumns.push('valueInBaseCurrency'); + if (this.showValueInBaseCurrency()) { + columns.push('valueInBaseCurrency'); } - if (this.showAllocationInPercentage) { - this.displayedColumns.push('allocation'); + if (this.showAllocationInPercentage()) { + columns.push('allocation'); } - this.displayedColumns.push('comment'); + columns.push('comment'); - if (this.showActions) { - this.displayedColumns.push('actions'); + if (this.showActions()) { + columns.push('actions'); } - this.isLoading = true; + return columns; + }); - this.dataSource = new MatTableDataSource(this.accounts); - this.dataSource.sortingDataAccessor = getLowercase; + protected readonly isLoading = computed(() => !this.accounts()); - this.dataSource.sort = this.sort; + private readonly notificationService = inject(NotificationService); + private readonly router = inject(Router); + private readonly unsubscribeSubject = new Subject(); - if (this.accounts) { - this.isLoading = false; - } + public constructor() { + addIcons({ + arrowRedoOutline, + createOutline, + documentTextOutline, + ellipsisHorizontal, + eyeOffOutline, + trashOutline, + walletOutline + }); } public onDeleteAccount(aId: string) { 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') }, From 8a50b8c922ad7dedf137268224c24bb4b310b3fd Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Thu, 12 Feb 2026 02:38:13 +0700 Subject: [PATCH 2/4] Task/improve type safety in accounts table component (#6307) * fix(lib): prevent table data source to be created every time * fix(lib): remove unused unsubscribeSubject variable * fix(lib): change to protected methods * feat(lib): change locale to signal input * feat(lib): remove unused deviceType input * feat(lib): change hasPermissionToOpenDetails to signal input * feat(lib): change showFooter to signal input * feat(lib): change activitiesCount to signal input * feat(lib): change baseCurrency to signal input * feat(lib): change totalBalanceInBaseCurrency to signal input * feat(lib): change totalValueInBaseCurrency to signal input * feat(lib): change outputs to signal outputs --- .../holding-detail-dialog.html | 1 - .../src/app/pages/accounts/accounts-page.html | 1 - .../accounts-table.component.html | 32 ++++----- .../accounts-table.component.stories.ts | 3 - .../accounts-table.component.ts | 71 +++++++++---------- 5 files changed, 50 insertions(+), 58 deletions(-) diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index 27df91a17..86f4676f3 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -377,7 +377,6 @@ @@ -19,7 +19,7 @@ matSort matSortActive="name" matSortDirection="asc" - [dataSource]="dataSource()" + [dataSource]="dataSource" > - {{ baseCurrency }} + {{ baseCurrency() }} @@ -129,7 +129,7 @@ {{ element.activitiesCount }} - {{ activitiesCount }} + {{ activitiesCount() }} @@ -150,7 +150,7 @@ @@ -162,8 +162,8 @@ @@ -185,7 +185,7 @@ @@ -197,8 +197,8 @@ @@ -220,7 +220,7 @@ @@ -232,8 +232,8 @@ @@ -255,7 +255,7 @@ @@ -341,14 +341,14 @@ *matRowDef="let row; columns: displayedColumns()" mat-row [ngClass]="{ - 'cursor-pointer': hasPermissionToOpenDetails + 'cursor-pointer': hasPermissionToOpenDetails() }" (click)="onOpenAccountDetailDialog(row.id)" > diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts b/libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts index 594db4c6a..62a01164f 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts @@ -111,7 +111,6 @@ export const Loading: Story = { args: { accounts: undefined, baseCurrency: 'USD', - deviceType: 'desktop', hasPermissionToOpenDetails: false, locale: 'en-US', showActions: false, @@ -129,7 +128,6 @@ export const Default: Story = { accounts, activitiesCount: 12, baseCurrency: 'USD', - deviceType: 'desktop', hasPermissionToOpenDetails: false, locale: 'en-US', showActions: false, @@ -149,7 +147,6 @@ export const WithoutFooter: Story = { accounts, activitiesCount: 12, baseCurrency: 'USD', - deviceType: 'desktop', hasPermissionToOpenDetails: false, locale: 'en-US', showActions: false, 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 834e33fb3..d486b775f 100644 --- a/libs/ui/src/lib/accounts-table/accounts-table.component.ts +++ b/libs/ui/src/lib/accounts-table/accounts-table.component.ts @@ -8,13 +8,11 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, - EventEmitter, - Input, - OnDestroy, - Output, computed, + effect, inject, input, + output, viewChild } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; @@ -35,7 +33,6 @@ import { walletOutline } from 'ionicons/icons'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { Subject } from 'rxjs'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -55,35 +52,29 @@ import { Subject } from 'rxjs'; styleUrls: ['./accounts-table.component.scss'], templateUrl: './accounts-table.component.html' }) -export class GfAccountsTableComponent implements OnDestroy { - @Input() activitiesCount: number; - @Input() baseCurrency: string; - @Input() deviceType: string; - @Input() hasPermissionToOpenDetails = true; - @Input() locale = getLocale(); - @Input() showFooter = true; - @Input() totalBalanceInBaseCurrency: number; - @Input() totalValueInBaseCurrency: number; - - @Output() accountDeleted = new EventEmitter(); - @Output() accountToUpdate = new EventEmitter(); - @Output() transferBalance = new EventEmitter(); - +export class GfAccountsTableComponent { public readonly accounts = input.required(); + public readonly activitiesCount = input(); + public readonly baseCurrency = input(); + public readonly hasPermissionToOpenDetails = input(true); + public readonly locale = input(getLocale()); public readonly showActions = input(); public readonly showActivitiesCount = input(true); public readonly showAllocationInPercentage = input(); public readonly showBalance = input(true); + public readonly showFooter = input(true); public readonly showValue = input(true); public readonly showValueInBaseCurrency = input(false); + public readonly totalBalanceInBaseCurrency = input(); + public readonly totalValueInBaseCurrency = input(); + + public readonly accountDeleted = output(); + public readonly accountToUpdate = output(); + public readonly transferBalance = output(); + public readonly sort = viewChild.required(MatSort); - protected readonly dataSource = computed(() => { - const dataSource = new MatTableDataSource(this.accounts()); - dataSource.sortingDataAccessor = getLowercase; - dataSource.sort = this.sort(); - return dataSource; - }); + protected readonly dataSource = new MatTableDataSource([]); protected readonly displayedColumns = computed(() => { const columns = ['status', 'account', 'platform']; @@ -123,7 +114,6 @@ export class GfAccountsTableComponent implements OnDestroy { private readonly notificationService = inject(NotificationService); private readonly router = inject(Router); - private readonly unsubscribeSubject = new Subject(); public constructor() { addIcons({ @@ -135,9 +125,21 @@ export class GfAccountsTableComponent implements OnDestroy { trashOutline, walletOutline }); + + this.dataSource.sortingDataAccessor = getLowercase; + + // Reactive data update + effect(() => { + this.dataSource.data = this.accounts(); + }); + + // Reactive view connection + effect(() => { + this.dataSource.sort = this.sort(); + }); } - public onDeleteAccount(aId: string) { + protected onDeleteAccount(aId: string) { this.notificationService.confirm({ confirmFn: () => { this.accountDeleted.emit(aId); @@ -147,30 +149,25 @@ export class GfAccountsTableComponent implements OnDestroy { }); } - public onOpenAccountDetailDialog(accountId: string) { - if (this.hasPermissionToOpenDetails) { + protected onOpenAccountDetailDialog(accountId: string) { + if (this.hasPermissionToOpenDetails()) { this.router.navigate([], { queryParams: { accountId, accountDetailDialog: true } }); } } - public onOpenComment(aComment: string) { + protected onOpenComment(aComment: string) { this.notificationService.alert({ title: aComment }); } - public onTransferBalance() { + protected onTransferBalance() { this.transferBalance.emit(); } - public onUpdateAccount(aAccount: Account) { + protected onUpdateAccount(aAccount: Account) { this.accountToUpdate.emit(aAccount); } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } } From e66f468d65c3b06190a043b2a8b4d1149a73998d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:50:06 +0100 Subject: [PATCH 3/4] Task/improve TestMu AI logo and url (#6303) * Improve logo and url --- README.md | 4 ++-- .../src/app/pages/about/overview/about-overview-page.html | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 03566b2dd..3be15e49f 100644 --- a/README.md +++ b/README.md @@ -318,8 +318,8 @@ If you like to support this project, become a [**Sponsor**](https://github.com/s ## Sponsors diff --git a/apps/client/src/app/pages/about/overview/about-overview-page.html b/apps/client/src/app/pages/about/overview/about-overview-page.html index 2fb3ef812..cab1caa23 100644 --- a/apps/client/src/app/pages/about/overview/about-overview-page.html +++ b/apps/client/src/app/pages/about/overview/about-overview-page.html @@ -209,7 +209,7 @@
Browser testing via
@@ -217,9 +217,9 @@ alt="TestMu AI Logo" height="32" [src]=" - user?.settings?.colorScheme === 'LIGHT' - ? '../assets/images/sponsors/logo-testmu-dark.svg' - : '../assets/images/sponsors/logo-testmu-light.svg' + user?.settings?.colorScheme === 'DARK' + ? '../assets/images/sponsors/logo-testmu-light.svg' + : '../assets/images/sponsors/logo-testmu-dark.svg' " /> From ed610ddd4ea20d8bb71843ec63921e3a47095319 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:50:33 +0100 Subject: [PATCH 4/4] Task/upgrade twitter-api-v2 to version 1.29.0 (#6300) * Upgrade twitter-api-v2 to version 1.29.0 * Update changelog --- CHANGELOG.md | 6 ++++++ package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ddad0b5..edbfa5460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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 + +### Changed + +- Upgraded `twitter-api-v2` from version `1.27.0` to `1.29.0` + ## 2.237.0 - 2026-02-08 ### Changed diff --git a/package-lock.json b/package-lock.json index da8bf26f4..d415758b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,7 @@ "stripe": "20.3.0", "svgmap": "2.14.0", "tablemark": "4.1.0", - "twitter-api-v2": "1.27.0", + "twitter-api-v2": "1.29.0", "yahoo-finance2": "3.13.0", "zone.js": "0.16.0" }, @@ -33383,9 +33383,9 @@ } }, "node_modules/twitter-api-v2": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.27.0.tgz", - "integrity": "sha512-hbIFwzg0NeOcFOdmJqtKMCXjLjc0INff/7NwhnZ2zpnw65oku8i+0eMxo5M0iTc1hs+inD/IpDw3S0Xh2c45QQ==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.29.0.tgz", + "integrity": "sha512-v473q5bwme4N+DWSg6qY+JCvfg1nSJRWwui3HUALafxfqCvVkKiYmS/5x/pVeJwTmyeBxexMbzHwnzrH4h6oYQ==", "license": "Apache-2.0" }, "node_modules/type-check": { diff --git a/package.json b/package.json index 70922d840..0f43fff08 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "stripe": "20.3.0", "svgmap": "2.14.0", "tablemark": "4.1.0", - "twitter-api-v2": "1.27.0", + "twitter-api-v2": "1.29.0", "yahoo-finance2": "3.13.0", "zone.js": "0.16.0" },