From 00d908c7e295965fdd133772e8454367735a49a1 Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:22:15 +0700 Subject: [PATCH] Task/improve type safety in create or update activity dialog component (#7294) * fix(client): resolve type errors * feat(client): enforce encapsulation * feat(client): replace constructor based DI with inject functions * feat(client): enforce immutability * fix(client): resolve type error in import activities dialog --- ...ate-or-update-activity-dialog.component.ts | 88 ++++++++++--------- .../import-activities-dialog.component.ts | 2 +- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index ba17dbef3..4dbf6ad9f 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -20,7 +20,7 @@ import { ChangeDetectorRef, Component, DestroyRef, - Inject + inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { @@ -75,42 +75,46 @@ import { ActivityType } from './types/activity-type.type'; templateUrl: 'create-or-update-activity-dialog.html' }) export class GfCreateOrUpdateActivityDialogComponent { - public activityForm: FormGroup; - - public assetClassOptions: AssetClassSelectorOption[] = Object.keys(AssetClass) - .map((id) => { - return { id, label: translate(id) } as AssetClassSelectorOption; - }) - .sort((a, b) => { - return a.label.localeCompare(b.label); - }); + protected activityForm: FormGroup; - public assetSubClassOptions: AssetClassSelectorOption[] = []; - public currencies: string[] = []; - public currencyOfAssetProfile: string | undefined; - public currentMarketPrice: number | null = null; - public defaultDateFormat: string; - public defaultLookupItems: LookupItem[] = []; - public hasPermissionToCreateOwnTag: boolean | undefined; - public isLoading = false; - public isToday = isToday; - public mode: 'create' | 'update'; - public tagsAvailable: Tag[] = []; - public total = 0; - public typesTranslationMap = new Map(); - public Validators = Validators; - - public constructor( - private changeDetectorRef: ChangeDetectorRef, - @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateActivityDialogParams, - private dataService: DataService, - private dateAdapter: DateAdapter, - private destroyRef: DestroyRef, - public dialogRef: MatDialogRef, - private formBuilder: FormBuilder, - @Inject(MAT_DATE_LOCALE) private locale: string, - private userService: UserService - ) { + protected readonly assetClassOptions: AssetClassSelectorOption[] = + Object.keys(AssetClass) + .map((id) => { + return { id, label: translate(id) } as AssetClassSelectorOption; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); + + protected assetSubClassOptions: AssetClassSelectorOption[] = []; + protected currencies: string[] = []; + protected currencyOfAssetProfile: string | undefined; + protected currentMarketPrice: number | null = null; + protected defaultDateFormat: string; + protected defaultLookupItems: LookupItem[] = []; + protected hasPermissionToCreateOwnTag: boolean | undefined; + protected isLoading = false; + protected readonly isToday = isToday; + protected mode: 'create' | 'update'; + protected tagsAvailable: Tag[] = []; + protected total = 0; + protected readonly typesTranslationMap = new Map(); + protected readonly Validators = Validators; + + protected readonly data = + inject(MAT_DIALOG_DATA); + + private readonly changeDetectorRef = inject(ChangeDetectorRef); + private readonly dataService = inject(DataService); + private readonly dateAdapter = inject>(DateAdapter); + private readonly destroyRef = inject(DestroyRef); + private readonly dialogRef = + inject>(MatDialogRef); + private readonly formBuilder = inject(FormBuilder); + private locale = inject(MAT_DATE_LOCALE); + private readonly userService = inject(UserService); + + public constructor() { addIcons({ calendarClearOutline, refreshOutline }); } @@ -138,7 +142,9 @@ export class GfCreateOrUpdateActivityDialogComponent { return !['CASH'].includes(assetProfile.assetSubClass); }) .sort((a, b) => { - return a.assetProfile.name?.localeCompare(b.assetProfile.name); + return (a.assetProfile.name ?? '').localeCompare( + b.assetProfile.name ?? '' + ); }) .map(({ assetProfile }) => { return { @@ -463,14 +469,14 @@ export class GfCreateOrUpdateActivityDialogComponent { } } - public applyCurrentMarketPrice() { + protected applyCurrentMarketPrice() { this.activityForm.patchValue({ currencyOfUnitPrice: this.activityForm.get('currency')?.value, unitPrice: this.currentMarketPrice }); } - public dateFilter(aDate: Date) { + protected dateFilter(aDate: Date) { if (!aDate) { return true; } @@ -478,11 +484,11 @@ export class GfCreateOrUpdateActivityDialogComponent { return isAfter(aDate, new Date(0)); } - public onCancel() { + protected onCancel() { this.dialogRef.close(); } - public async onSubmit() { + protected async onSubmit() { const activity: CreateOrderDto | UpdateOrderDto = { accountId: this.activityForm.get('accountId')?.value, assetClass: this.activityForm.get('assetClass')?.value, diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index c3dbe6cf2..4541009a0 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -151,7 +151,7 @@ export class GfImportActivitiesDialogComponent { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(({ holdings }) => { this.holdings = sortBy(holdings, ({ assetProfile }) => { - return assetProfile.name.toLowerCase(); + return assetProfile.name?.toLowerCase(); }); this.assetProfileForm.controls.assetProfileIdentifier.enable();