diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
index d81a3c1f3..618626b63 100644
--- a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
+++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
@@ -7,8 +7,8 @@
>
@if (
- data.rule.configuration.thresholdMin &&
- data.rule.configuration.thresholdMax
+ data.rule.configuration?.thresholdMin &&
+ data.rule.configuration?.thresholdMax
) {
diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
index 91f4b6210..397fdce57 100644
--- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
+++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
@@ -20,6 +20,7 @@ import {
ChangeDetectorRef,
Component,
DestroyRef,
+ inject,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -31,12 +32,14 @@ import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { format, parseISO } from 'date-fns';
import { DeviceDetectorService } from 'ngx-device-detector';
-import { Subscription } from 'rxjs';
+import { of } from 'rxjs';
+import { map, switchMap } from 'rxjs/operators';
import { GfCreateOrUpdateActivityDialogComponent } from './create-or-update-activity-dialog/create-or-update-activity-dialog.component';
import { CreateOrUpdateActivityDialogParams } from './create-or-update-activity-dialog/interfaces/interfaces';
import { GfImportActivitiesDialogComponent } from './import-activities-dialog/import-activities-dialog.component';
import { ImportActivitiesDialogParams } from './import-activities-dialog/interfaces/interfaces';
+import { ActivitiesPageParams } from './interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -51,54 +54,53 @@ import { ImportActivitiesDialogParams } from './import-activities-dialog/interfa
templateUrl: './activities-page.html'
})
export class GfActivitiesPageComponent implements OnInit {
- public activityTypesFilter: string[] = [];
- public dataSource: MatTableDataSource;
- public deviceType: string;
- public hasImpersonationId: boolean;
- public hasPermissionToCreateActivity: boolean;
- public hasPermissionToDeleteActivity: boolean;
- public pageIndex = 0;
- public pageSize = DEFAULT_PAGE_SIZE;
- public routeQueryParams: Subscription;
- public sortColumn = 'date';
- public sortDirection: SortDirection = 'desc';
- public totalItems: number | undefined;
- public user: User;
-
- public constructor(
- private changeDetectorRef: ChangeDetectorRef,
- private dataService: DataService,
- private destroyRef: DestroyRef,
- private deviceDetectorService: DeviceDetectorService,
- private dialog: MatDialog,
- private icsService: IcsService,
- private impersonationStorageService: ImpersonationStorageService,
- private route: ActivatedRoute,
- private router: Router,
- private userService: UserService
- ) {
- this.routeQueryParams = route.queryParams
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe((params) => {
- if (params['createDialog']) {
- if (params['activityId']) {
- this.dataService
- .fetchActivity(params['activityId'])
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe((activity) => {
- this.openCreateActivityDialog(activity);
- });
- } else {
- this.openCreateActivityDialog();
+ protected dataSource: MatTableDataSource | undefined;
+ protected deviceType: string;
+ protected hasImpersonationId: boolean;
+ protected hasPermissionToCreateActivity: boolean;
+ protected hasPermissionToDeleteActivity: boolean;
+ protected pageIndex = 0;
+ protected readonly pageSize = DEFAULT_PAGE_SIZE;
+ protected sortColumn = 'date';
+ protected sortDirection: SortDirection = 'desc';
+ protected totalItems: number | undefined;
+ protected user: User;
+
+ private activityTypesFilter: string[] = [];
+
+ private readonly changeDetectorRef = inject(ChangeDetectorRef);
+ private readonly dataService = inject(DataService);
+ private readonly destroyRef = inject(DestroyRef);
+ private readonly deviceDetectorService = inject(DeviceDetectorService);
+ private readonly dialog = inject(MatDialog);
+ private readonly icsService = inject(IcsService);
+ private readonly impersonationStorageService = inject(
+ ImpersonationStorageService
+ );
+ private readonly route = inject(ActivatedRoute);
+ private readonly router = inject(Router);
+ private readonly userService = inject(UserService);
+
+ public constructor() {
+ this.route.queryParams
+ .pipe(
+ takeUntilDestroyed(this.destroyRef),
+ switchMap((params: ActivitiesPageParams) => {
+ if (params.activityId && (params.createDialog || params.editDialog)) {
+ return this.dataService
+ .fetchActivity(params.activityId)
+ .pipe(map((activity) => ({ activity, params })));
}
- } else if (params['editDialog']) {
- if (params['activityId']) {
- this.dataService
- .fetchActivity(params['activityId'])
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe((activity) => {
- this.openUpdateActivityDialog(activity);
- });
+
+ return of({ params, activity: undefined });
+ })
+ )
+ .subscribe(({ activity, params }) => {
+ if (params.createDialog) {
+ this.openCreateActivityDialog(activity);
+ } else if (params.editDialog) {
+ if (activity) {
+ this.openUpdateActivityDialog(activity);
} else {
this.router.navigate(['.'], { relativeTo: this.route });
}
@@ -131,49 +133,13 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public fetchActivities() {
- // Reset dataSource and totalItems to show loading state
- this.dataSource = undefined;
- this.totalItems = undefined;
-
- const dateRange = this.user?.settings?.dateRange;
- const range = this.isCalendarYear(dateRange) ? dateRange : undefined;
-
- this.dataService
- .fetchActivities({
- range,
- activityTypes: this.activityTypesFilter.length
- ? this.activityTypesFilter
- : undefined,
- filters: this.userService.getFilters(),
- skip: this.pageIndex * this.pageSize,
- sortColumn: this.sortColumn,
- sortDirection: this.sortDirection,
- take: this.pageSize
- })
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe(({ activities, count }) => {
- this.dataSource = new MatTableDataSource(activities);
- this.totalItems = count;
-
- if (
- this.hasPermissionToCreateActivity &&
- this.user?.activitiesCount === 0
- ) {
- this.router.navigate([], { queryParams: { createDialog: true } });
- }
-
- this.changeDetectorRef.markForCheck();
- });
- }
-
- public onChangePage(page: PageEvent) {
+ protected onChangePage(page: PageEvent) {
this.pageIndex = page.pageIndex;
this.fetchActivities();
}
- public onClickActivity({ dataSource, symbol }: AssetProfileIdentifier) {
+ protected onClickActivity({ dataSource, symbol }: AssetProfileIdentifier) {
this.router.navigate([], {
queryParams: {
dataSource,
@@ -183,11 +149,11 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onCloneActivity(aActivity: Activity) {
+ protected onCloneActivity(aActivity: Activity) {
this.openCreateActivityDialog(aActivity);
}
- public onDeleteActivities() {
+ protected onDeleteActivities() {
this.dataService
.deleteActivities({
filters: this.userService.getFilters()
@@ -205,7 +171,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onDeleteActivity(aId: string) {
+ protected onDeleteActivity(aId: string) {
this.dataService
.deleteActivity(aId)
.pipe(takeUntilDestroyed(this.destroyRef))
@@ -221,7 +187,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onExport(activityIds?: string[]) {
+ protected onExport(activityIds?: string[]) {
let fetchExportParams: any = { activityIds };
if (!activityIds) {
@@ -238,7 +204,7 @@ export class GfActivitiesPageComponent implements OnInit {
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((data) => {
for (const activity of data.activities) {
- delete activity.id;
+ delete (activity as Omit & { id?: string }).id;
}
downloadAsFile({
@@ -252,7 +218,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onExportDrafts(activityIds?: string[]) {
+ protected onExportDrafts(activityIds?: string[]) {
this.dataService
.fetchExport({ activityIds })
.pipe(takeUntilDestroyed(this.destroyRef))
@@ -270,7 +236,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onImport() {
+ protected onImport() {
const dialogRef = this.dialog.open<
GfImportActivitiesDialogComponent,
ImportActivitiesDialogParams
@@ -298,7 +264,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onImportDividends() {
+ protected onImportDividends() {
const dialogRef = this.dialog.open<
GfImportActivitiesDialogComponent,
ImportActivitiesDialogParams
@@ -327,7 +293,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- public onSortChanged({ active, direction }: Sort) {
+ protected onSortChanged({ active, direction }: Sort) {
this.pageIndex = 0;
this.sortColumn = active;
this.sortDirection = direction;
@@ -335,20 +301,56 @@ export class GfActivitiesPageComponent implements OnInit {
this.fetchActivities();
}
- public onTypesFilterChanged(aTypes: string[]) {
+ protected onTypesFilterChanged(aTypes: string[]) {
this.activityTypesFilter = aTypes;
this.pageIndex = 0;
this.fetchActivities();
}
- public onUpdateActivity(aActivity: Activity) {
+ protected onUpdateActivity(aActivity: Activity) {
this.router.navigate([], {
queryParams: { activityId: aActivity.id, editDialog: true }
});
}
- public openUpdateActivityDialog(aActivity: Activity) {
+ private fetchActivities() {
+ // Reset dataSource and totalItems to show loading state
+ this.dataSource = undefined;
+ this.totalItems = undefined;
+
+ const dateRange = this.user?.settings?.dateRange;
+ const range = this.isCalendarYear(dateRange) ? dateRange : undefined;
+
+ this.dataService
+ .fetchActivities({
+ range,
+ activityTypes: this.activityTypesFilter.length
+ ? this.activityTypesFilter
+ : undefined,
+ filters: this.userService.getFilters(),
+ skip: this.pageIndex * this.pageSize,
+ sortColumn: this.sortColumn,
+ sortDirection: this.sortDirection,
+ take: this.pageSize
+ })
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe(({ activities, count }) => {
+ this.dataSource = new MatTableDataSource(activities);
+ this.totalItems = count;
+
+ if (
+ this.hasPermissionToCreateActivity &&
+ this.user?.activitiesCount === 0
+ ) {
+ this.router.navigate([], { queryParams: { createDialog: true } });
+ }
+
+ this.changeDetectorRef.markForCheck();
+ });
+ }
+
+ private openUpdateActivityDialog(aActivity: Activity) {
const dialogRef = this.dialog.open<
GfCreateOrUpdateActivityDialogComponent,
CreateOrUpdateActivityDialogParams
@@ -383,7 +385,7 @@ export class GfActivitiesPageComponent implements OnInit {
});
}
- private isCalendarYear(dateRange: DateRange) {
+ private isCalendarYear(dateRange?: DateRange) {
if (!dateRange) {
return false;
}
@@ -410,11 +412,12 @@ export class GfActivitiesPageComponent implements OnInit {
date: new Date(),
id: null,
fee: 0,
+ SymbolProfile: null,
type: aActivity?.type ?? 'BUY',
unitPrice: null
},
user: this.user
- },
+ } satisfies CreateOrUpdateActivityDialogParams,
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
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 4dbf6ad9f..022a8f2a2 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
@@ -536,7 +536,13 @@ export class GfCreateOrUpdateActivityDialogComponent {
this.dialogRef.close(activity);
} else {
- (activity as UpdateOrderDto).id = this.data.activity?.id;
+ const activityId = this.data.activity?.id;
+
+ if (!activityId) {
+ throw new Error('Activity ID is required for update');
+ }
+
+ (activity as UpdateOrderDto).id = activityId;
await validateObjectForForm({
classDto: UpdateOrderDto,
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts
index 322bcc076..37faff91f 100644
--- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts
+++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/interfaces/interfaces.ts
@@ -4,8 +4,10 @@ import { Account } from '@prisma/client';
export interface CreateOrUpdateActivityDialogParams {
accounts: Account[];
- activity: Omit & {
+ activity: Partial> & {
+ id: string | null;
SymbolProfile: Activity['SymbolProfile'] | null;
+ unitPrice: number | null;
};
user: User;
}
diff --git a/apps/client/src/app/pages/portfolio/activities/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/activities/interfaces/interfaces.ts
new file mode 100644
index 000000000..51f240cb5
--- /dev/null
+++ b/apps/client/src/app/pages/portfolio/activities/interfaces/interfaces.ts
@@ -0,0 +1,7 @@
+import { Params } from '@angular/router';
+
+export interface ActivitiesPageParams extends Params {
+ activityId?: string;
+ createDialog?: string;
+ editDialog?: string;
+}
diff --git a/apps/client/tsconfig.json b/apps/client/tsconfig.json
index d207f5966..ae0aaf61a 100644
--- a/apps/client/tsconfig.json
+++ b/apps/client/tsconfig.json
@@ -22,6 +22,7 @@
"compilerOptions": {
"lib": ["dom", "es2022"],
"module": "preserve",
+ "strictNullChecks": true,
"target": "es2020"
}
}
diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts
index d69e5a13a..16a66ff31 100644
--- a/libs/ui/src/lib/assistant/assistant.component.ts
+++ b/libs/ui/src/lib/assistant/assistant.component.ts
@@ -713,7 +713,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
return {
routerLink,
mode: SearchMode.QUICK_LINK as const,
- name: title
+ name: title ?? ''
};
});
}