From 4a382f8eb8b16c575cd9cb55adefa390e7c7edd3 Mon Sep 17 00:00:00 2001 From: "Fabio Azevedo (CTW)" Date: Mon, 1 May 2023 23:40:47 +0100 Subject: [PATCH] Implemented platform creation --- apps/api/src/app/app.module.ts | 2 + ...te-or-update-account-platform.component.ts | 40 +++++++++ .../create-or-update-platform-dialog.html | 29 +++++++ ...create-or-update-platform-dialog.module.ts | 26 ++++++ .../create-or-update-platform-dialog.scss | 7 ++ .../interfaces/interfaces.ts | 5 ++ .../platform/platform.component.html | 18 +++- .../platform/platform.component.scss | 10 +++ .../components/platform/platform.component.ts | 85 +++++++++++++++++-- .../components/platform/platform.module.ts | 4 +- apps/client/src/app/services/data.service.ts | 11 ++- .../interfaces/admin-platforms.interface.ts | 16 ---- 12 files changed, 224 insertions(+), 29 deletions(-) create mode 100644 apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-account-platform.component.ts create mode 100644 apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html create mode 100644 apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.module.ts create mode 100644 apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.scss create mode 100644 apps/client/src/app/components/platform/create-or-update-platform-dialog/interfaces/interfaces.ts delete mode 100644 libs/common/src/lib/interfaces/admin-platforms.interface.ts diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 83179a402..3cc2ee507 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -34,6 +34,7 @@ import { RedisCacheModule } from './redis-cache/redis-cache.module'; import { SubscriptionModule } from './subscription/subscription.module'; import { SymbolModule } from './symbol/symbol.module'; import { UserModule } from './user/user.module'; +import { PlatformModule } from './platform/platform.module'; @Module({ imports: [ @@ -66,6 +67,7 @@ import { UserModule } from './user/user.module'; PortfolioModule, PrismaModule, RedisCacheModule, + PlatformModule, ScheduleModule.forRoot(), ServeStaticModule.forRoot({ serveStaticOptions: { diff --git a/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-account-platform.component.ts b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-account-platform.component.ts new file mode 100644 index 000000000..dc82a7e83 --- /dev/null +++ b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-account-platform.component.ts @@ -0,0 +1,40 @@ +import { + ChangeDetectionStrategy, + Component, + Inject, + OnDestroy +} from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Subject } from 'rxjs'; + +import { CreateOrUpdatePlatformDialogParams } from './interfaces/interfaces'; + +@Component({ + host: { class: 'h-100' }, + selector: 'gf-create-or-update-platform-dialog', + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrls: ['./create-or-update-platform-dialog.scss'], + templateUrl: 'create-or-update-platform-dialog.html' +}) +export class CreateOrUpdatePlatformDialog implements OnDestroy { + public currencies: string[] = []; + public platforms: { id: string; name: string }[]; + + private unsubscribeSubject = new Subject(); + + public constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdatePlatformDialogParams + ) {} + + ngOnInit() {} + + public onCancel() { + this.dialogRef.close(); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } +} diff --git a/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html new file mode 100644 index 000000000..09656295c --- /dev/null +++ b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.html @@ -0,0 +1,29 @@ +
+

Update platform

+

Add platform

+
+
+ + Name + + +
+
+ + Url + + +
+
+
+ + +
+
diff --git a/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.module.ts b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.module.ts new file mode 100644 index 000000000..bace1a7aa --- /dev/null +++ b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.module.ts @@ -0,0 +1,26 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { CreateOrUpdatePlatformDialog } from './create-or-update-account-platform.component'; + +@NgModule({ + declarations: [CreateOrUpdatePlatformDialog], + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatCheckboxModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + ReactiveFormsModule + ] +}) +export class GfCreateOrUpdatePlatformDialogModule {} diff --git a/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.scss b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.scss new file mode 100644 index 000000000..b63df0134 --- /dev/null +++ b/apps/client/src/app/components/platform/create-or-update-platform-dialog/create-or-update-platform-dialog.scss @@ -0,0 +1,7 @@ +:host { + display: block; + + .mat-mdc-dialog-content { + max-height: unset; + } +} diff --git a/apps/client/src/app/components/platform/create-or-update-platform-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/platform/create-or-update-platform-dialog/interfaces/interfaces.ts new file mode 100644 index 000000000..be4af5407 --- /dev/null +++ b/apps/client/src/app/components/platform/create-or-update-platform-dialog/interfaces/interfaces.ts @@ -0,0 +1,5 @@ +import { Platform } from '@prisma/client'; + +export interface CreateOrUpdatePlatformDialogParams { + platform: Platform; +} diff --git a/apps/client/src/app/components/platform/platform.component.html b/apps/client/src/app/components/platform/platform.component.html index 4e8e0e8b9..5de7dc60c 100644 --- a/apps/client/src/app/components/platform/platform.component.html +++ b/apps/client/src/app/components/platform/platform.component.html @@ -14,7 +14,11 @@ Id - + {{ element.id }} @@ -23,7 +27,11 @@ Name - + {{ element.name }} @@ -32,7 +40,11 @@ Url - + {{ element.url }} diff --git a/apps/client/src/app/components/platform/platform.component.scss b/apps/client/src/app/components/platform/platform.component.scss index 0fd1e7bb1..bd9796d74 100644 --- a/apps/client/src/app/components/platform/platform.component.scss +++ b/apps/client/src/app/components/platform/platform.component.scss @@ -11,4 +11,14 @@ bottom: 4rem; z-index: 999; } + + .mat-mdc-table { + th { + ::ng-deep { + .mat-sort-header-container { + justify-content: inherit; + } + } + } + } } diff --git a/apps/client/src/app/components/platform/platform.component.ts b/apps/client/src/app/components/platform/platform.component.ts index 4dc2befe3..c621d08e5 100644 --- a/apps/client/src/app/components/platform/platform.component.ts +++ b/apps/client/src/app/components/platform/platform.component.ts @@ -1,41 +1,55 @@ -import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core'; import { MatSort } from '@angular/material/sort'; +import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; + import { MatTableDataSource } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { User } from '@ghostfolio/common/interfaces'; -import { AdminPlatformsItem } from '@ghostfolio/common/interfaces/admin-platforms.interface'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { Platform as PlatformModel } from '@prisma/client'; +import { Platform, Platform as PlatformModel } from '@prisma/client'; import { Subject, takeUntil } from 'rxjs'; +import { CreateOrUpdatePlatformDialog } from './create-or-update-platform-dialog/create-or-update-account-platform.component'; +import { MatDialog } from '@angular/material/dialog'; +import { DeviceDetectorService } from 'ngx-device-detector'; +import { DataService } from '@ghostfolio/client/services/data.service'; @Component({ selector: 'gf-platform-overview', styleUrls: ['./platform.component.scss'], templateUrl: './platform.component.html' }) -export class AdminPlatformComponent implements OnInit { +export class AdminPlatformComponent implements OnInit, OnDestroy { @ViewChild(MatSort) sort: MatSort; public displayedColumns = ['id', 'name', 'url']; public platforms: PlatformModel[]; + public deviceType: string; public hasPermissionToCreatePlatform: boolean; public hasPermissionToDeletePlatform: boolean; public hasImpersonationId: boolean; public user: User; - public dataSource: MatTableDataSource = - new MatTableDataSource(); + public dataSource: MatTableDataSource = new MatTableDataSource(); private unsubscribeSubject = new Subject(); public constructor( + private dataService: DataService, private changeDetectorRef: ChangeDetectorRef, private userService: UserService, + private deviceService: DeviceDetectorService, private impersonationStorageService: ImpersonationStorageService, + private dialog: MatDialog, private route: ActivatedRoute, private router: Router ) { @@ -48,7 +62,7 @@ export class AdminPlatformComponent implements OnInit { params['createDialog'] && this.hasPermissionToCreatePlatform ) { - //this.openCreateAccountDialog(); + this.openCreatePlatformDialog(); } else if (params['editDialog']) { if (this.platforms) { const platform = this.platforms.find(({ id }) => { @@ -64,6 +78,8 @@ export class AdminPlatformComponent implements OnInit { } public ngOnInit() { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; + this.impersonationStorageService .onChangeHasImpersonation() .pipe(takeUntil(this.unsubscribeSubject)) @@ -93,5 +109,58 @@ export class AdminPlatformComponent implements OnInit { this.fetchPlatforms(); } - private fetchPlatforms() {} + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + + private fetchPlatforms() { + this.dataService + .fetchPlatforms() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((platforms) => { + this.platforms = platforms; + this.dataSource = new MatTableDataSource(platforms); + this.changeDetectorRef.markForCheck(); + }); + } + + private openCreatePlatformDialog() { + const dialogRef = this.dialog.open(CreateOrUpdatePlatformDialog, { + data: { + platform: { + name: null, + url: null + } + }, + + height: this.deviceType === 'mobile' ? '97.5vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); + + dialogRef + .afterClosed() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((data) => { + const platform: CreatePlatformDto = data?.platform; + + if (platform) { + this.dataService + .postPlatform(platform) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe({ + next: () => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + + this.fetchPlatforms(); + } + }); + } + + this.router.navigate(['.'], { relativeTo: this.route }); + }); + } } diff --git a/apps/client/src/app/components/platform/platform.module.ts b/apps/client/src/app/components/platform/platform.module.ts index 0d33213d2..6f788c33c 100644 --- a/apps/client/src/app/components/platform/platform.module.ts +++ b/apps/client/src/app/components/platform/platform.module.ts @@ -5,6 +5,7 @@ import { RouterModule } from '@angular/router'; import { MatButtonModule } from '@angular/material/button'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { GfCreateOrUpdatePlatformDialogModule } from './create-or-update-platform-dialog/create-or-update-platform-dialog.module'; @NgModule({ declarations: [AdminPlatformComponent], @@ -13,7 +14,8 @@ import { MatTableModule } from '@angular/material/table'; RouterModule, MatButtonModule, MatSortModule, - MatTableModule + MatTableModule, + GfCreateOrUpdatePlatformDialogModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index bff672717..ce28dd96d 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -6,6 +6,7 @@ import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Activities } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; +import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface'; import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface'; import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; @@ -39,7 +40,7 @@ import { import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; import { AccountWithValue, DateRange, GroupBy } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; -import { DataSource, Order as OrderModel } from '@prisma/client'; +import { DataSource, Order as OrderModel, Platform } from '@prisma/client'; import { format, parseISO } from 'date-fns'; import { cloneDeep, groupBy, isNumber } from 'lodash'; import { Observable } from 'rxjs'; @@ -72,6 +73,10 @@ export class DataService { return this.http.get('/api/v1/account'); } + public fetchPlatforms() { + return this.http.get('/api/v1/platform'); + } + public fetchActivities({ filters }: { @@ -405,6 +410,10 @@ export class DataService { return this.http.post(`/api/v1/account`, aAccount); } + public postPlatform(aPlatform: CreatePlatformDto) { + return this.http.post(`/api/v1/platform`, aPlatform); + } + public postOrder(aOrder: CreateOrderDto) { return this.http.post(`/api/v1/order`, aOrder); } diff --git a/libs/common/src/lib/interfaces/admin-platforms.interface.ts b/libs/common/src/lib/interfaces/admin-platforms.interface.ts deleted file mode 100644 index df684158d..000000000 --- a/libs/common/src/lib/interfaces/admin-platforms.interface.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; - -export interface AdminPlatforms { - platforms: AdminPlatformsItem[]; -} - -export interface AdminPlatformsItem { - assetClass?: AssetClass; - assetSubClass?: AssetSubClass; - countriesCount: number; - dataSource: DataSource; - date?: Date; - marketDataItemCount: number; - sectorsCount: number; - symbol: string; -}