From 854e774f948b56672cd8a1d645a6b465f10ed9e8 Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Sun, 27 Apr 2025 23:11:59 +0700 Subject: [PATCH] resolve comments --- .../watchlist/watchlist.controller.ts | 7 ++- .../endpoints/watchlist/watchlist.service.ts | 4 +- .../create-watchlist-item-dialog.component.ts | 25 ++++++++-- .../create-watchlist-item-dialog.module.ts | 25 ---------- .../home-watchlist.component.ts | 47 ++++++++----------- .../home-watchlist/home-watchlist.module.ts | 4 +- .../src/app/pages/home/home-page.component.ts | 3 +- apps/client/src/app/services/data.service.ts | 6 ++- 8 files changed, 53 insertions(+), 68 deletions(-) delete mode 100644 apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts index 0d25172c8..de6f43d4f 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts @@ -2,7 +2,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -55,8 +54,8 @@ export class WatchlistController { ) { const watchlistItem = await this.watchlistService .getWatchlistItems(this.request.user.id) - .then((items) => { - return items.find((item) => { + .then(({ watchlist }) => { + return watchlist.find((item) => { return item.dataSource === dataSource && item.symbol === symbol; }); }); @@ -79,7 +78,7 @@ export class WatchlistController { @HasPermission(permissions.readWatchlist) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInResponseInterceptor) - public async getWatchlistItems(): Promise { + public async getWatchlistItems() { return this.watchlistService.getWatchlistItems(this.request.user.id); } } diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts index fdb9dd97a..ab7ed88a4 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts @@ -64,7 +64,7 @@ export class WatchlistService { public async getWatchlistItems( userId: string - ): Promise { + ): Promise<{ watchlist: AssetProfileIdentifier[] }> { const user = await this.prismaService.user.findUnique({ select: { watchlist: { @@ -74,6 +74,6 @@ export class WatchlistService { where: { id: userId } }); - return user.watchlist ?? []; + return { watchlist: user.watchlist ?? [] }; } } diff --git a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts index b62ab4912..722f680c3 100644 --- a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts +++ b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts @@ -1,3 +1,6 @@ +import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete'; + +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -9,27 +12,39 @@ import { FormBuilder, FormControl, FormGroup, + FormsModule, + ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; -import { MatDialogRef } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { Subject } from 'rxjs'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'h-100' }, + imports: [ + CommonModule, + FormsModule, + GfSymbolAutocompleteComponent, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + ReactiveFormsModule + ], selector: 'gf-create-watchlist-item-dialog', styleUrls: ['./create-watchlist-item-dialog.component.scss'], - templateUrl: 'create-watchlist-item-dialog.html', - standalone: false + templateUrl: 'create-watchlist-item-dialog.html' }) -export class CreateWatchlistItemDialog implements OnInit, OnDestroy { +export class CreateWatchlistItemDialogComponent implements OnInit, OnDestroy { public createWatchlistItemForm: FormGroup; private unsubscribeSubject = new Subject(); public constructor( - public readonly dialogRef: MatDialogRef, + public readonly dialogRef: MatDialogRef, public readonly formBuilder: FormBuilder ) {} diff --git a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts deleted file mode 100644 index 167d320b8..000000000 --- a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete'; - -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatFormFieldModule } from '@angular/material/form-field'; - -import { CreateWatchlistItemDialog } from './create-watchlist-item-dialog.component'; - -@NgModule({ - declarations: [CreateWatchlistItemDialog], - imports: [ - CommonModule, - FormsModule, - GfSymbolAutocompleteComponent, - MatButtonModule, - MatDialogModule, - MatFormFieldModule, - ReactiveFormsModule - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class GfCreateWatchlistItemDialogModule {} diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts index 17990c809..df47f1235 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts @@ -12,10 +12,10 @@ import { import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { DeviceDetectorService } from 'ngx-device-detector'; -import { forkJoin, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; -import { CreateWatchlistItemDialog } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; +import { CreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/interfaces/interfaces'; @Component({ @@ -72,28 +72,21 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { } private loadWatchlistData() { - forkJoin({ - watchlistItems: this.dataService.fetchWatchlist(), - allBenchmarks: this.dataService - .fetchBenchmarks() - .pipe(map((response) => response.benchmarks)) - }) - .pipe( - takeUntil(this.unsubscribeSubject), - map(({ watchlistItems, allBenchmarks }) => { - const watchlistLookup = new Set( - watchlistItems.map((item) => `${item.dataSource}:${item.symbol}`) - ); - return allBenchmarks.filter((benchmark) => - watchlistLookup.has(`${benchmark.dataSource}:${benchmark.symbol}`) - ); - }) - ) - .subscribe({ - next: (filteredBenchmarks) => { - this.watchlist = filteredBenchmarks; - this.changeDetectorRef.markForCheck(); - } + this.dataService + .fetchWatchlist() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ watchlist }) => { + this.watchlist = watchlist.map((item) => ({ + dataSource: item.dataSource, + symbol: item.symbol, + name: item.symbol, + marketCondition: null, + performances: null, + trend200d: 'UNKNOWN', + trend50d: 'UNKNOWN' + })); + + this.changeDetectorRef.markForCheck(); }); } @@ -104,7 +97,7 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { .subscribe((user) => { this.user = user; - const dialogRef = this.dialog.open(CreateWatchlistItemDialog, { + const dialogRef = this.dialog.open(CreateWatchlistItemDialogComponent, { autoFocus: false, data: { deviceType: this.deviceType, @@ -119,7 +112,7 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { .subscribe(({ dataSource, symbol } = {}) => { if (dataSource && symbol) { this.dataService - .postWatchlist({ dataSource, symbol }) + .postWatchlistItem({ dataSource, symbol }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe({ next: () => this.loadWatchlistData() diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.module.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.module.ts index 176d98d90..05c86f82f 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.module.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.module.ts @@ -5,7 +5,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { RouterModule } from '@angular/router'; -import { GfCreateWatchlistItemDialogModule } from './create-watchlist-item-dialog/create-watchlist-item-dialog.module'; +import { CreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; import { HomeWatchlistComponent } from './home-watchlist.component'; @NgModule({ @@ -14,7 +14,7 @@ import { HomeWatchlistComponent } from './home-watchlist.component'; imports: [ CommonModule, GfBenchmarkComponent, - GfCreateWatchlistItemDialogModule, + CreateWatchlistItemDialogComponent, MatButtonModule, RouterModule ], diff --git a/apps/client/src/app/pages/home/home-page.component.ts b/apps/client/src/app/pages/home/home-page.component.ts index 8d46ddbe0..70e0c34fe 100644 --- a/apps/client/src/app/pages/home/home-page.component.ts +++ b/apps/client/src/app/pages/home/home-page.component.ts @@ -56,7 +56,8 @@ export class HomePageComponent implements OnDestroy, OnInit { { iconName: 'star-outline', label: $localize`Watchlist`, - path: ['/home', 'watchlist'] + path: ['/home', 'watchlist'], + showCondition: this.user?.settings?.isExperimentalFeatures } ]; this.user = state.user; diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 430482b4e..6a9a164a1 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -688,7 +688,9 @@ export class DataService { } public fetchWatchlist() { - return this.http.get('/api/v1/watchlist'); + return this.http.get<{ watchlist: AssetProfileIdentifier[] }>( + '/api/v1/watchlist' + ); } public generateAccessToken(aUserId: string) { @@ -753,7 +755,7 @@ export class DataService { return this.http.post('/api/v1/user', {}); } - public postWatchlist(watchlistItem: CreateWatchlistItemDto) { + public postWatchlistItem(watchlistItem: CreateWatchlistItemDto) { return this.http.post('/api/v1/watchlist', watchlistItem); }