mirror of https://github.com/ghostfolio/ghostfolio
				
				
			
							committed by
							
								 GitHub
								GitHub
							
						
					
				
				 17 changed files with 302 additions and 13 deletions
			
			
		| @ -0,0 +1,29 @@ | |||
| import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; | |||
| 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 type { AdminMarketDataDetails } from '@ghostfolio/common/interfaces'; | |||
| 
 | |||
| import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; | |||
| import { DataSource } from '@prisma/client'; | |||
| import { pick } from 'lodash'; | |||
| 
 | |||
| @Controller('asset') | |||
| export class AssetController { | |||
|   public constructor(private readonly adminService: AdminService) {} | |||
| 
 | |||
|   @Get(':dataSource/:symbol') | |||
|   @UseInterceptors(TransformDataSourceInRequestInterceptor) | |||
|   @UseInterceptors(TransformDataSourceInResponseInterceptor) | |||
|   public async getAsset( | |||
|     @Param('dataSource') dataSource: DataSource, | |||
|     @Param('symbol') symbol: string | |||
|   ): Promise<AdminMarketDataDetails> { | |||
|     const { assetProfile, marketData } = | |||
|       await this.adminService.getMarketDataBySymbol({ dataSource, symbol }); | |||
| 
 | |||
|     return { | |||
|       marketData, | |||
|       assetProfile: pick(assetProfile, ['dataSource', 'name', 'symbol']) | |||
|     }; | |||
|   } | |||
| } | |||
| @ -0,0 +1,17 @@ | |||
| import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; | |||
| import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; | |||
| import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; | |||
| 
 | |||
| import { Module } from '@nestjs/common'; | |||
| 
 | |||
| import { AssetController } from './asset.controller'; | |||
| 
 | |||
| @Module({ | |||
|   controllers: [AssetController], | |||
|   imports: [ | |||
|     AdminModule, | |||
|     TransformDataSourceInRequestModule, | |||
|     TransformDataSourceInResponseModule | |||
|   ] | |||
| }) | |||
| export class AssetModule {} | |||
| @ -0,0 +1,12 @@ | |||
| :host { | |||
|   display: block; | |||
| 
 | |||
|   .mat-mdc-dialog-content { | |||
|     max-height: unset; | |||
| 
 | |||
|     gf-line-chart { | |||
|       aspect-ratio: 16 / 9; | |||
|       margin: 0 -0.5rem; | |||
|     } | |||
|   } | |||
| } | |||
| @ -0,0 +1,87 @@ | |||
| import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; | |||
| import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; | |||
| import { DataService } from '@ghostfolio/client/services/data.service'; | |||
| import { DATE_FORMAT } from '@ghostfolio/common/helper'; | |||
| import { | |||
|   AdminMarketDataDetails, | |||
|   LineChartItem | |||
| } from '@ghostfolio/common/interfaces'; | |||
| import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; | |||
| 
 | |||
| import { CommonModule } from '@angular/common'; | |||
| import { | |||
|   CUSTOM_ELEMENTS_SCHEMA, | |||
|   ChangeDetectionStrategy, | |||
|   ChangeDetectorRef, | |||
|   Component, | |||
|   Inject, | |||
|   OnDestroy, | |||
|   OnInit | |||
| } from '@angular/core'; | |||
| import { | |||
|   MAT_DIALOG_DATA, | |||
|   MatDialogModule, | |||
|   MatDialogRef | |||
| } from '@angular/material/dialog'; | |||
| import { format } from 'date-fns'; | |||
| import { Subject } from 'rxjs'; | |||
| import { takeUntil } from 'rxjs/operators'; | |||
| 
 | |||
| import { BenchmarkDetailDialogParams } from './interfaces/interfaces'; | |||
| 
 | |||
| @Component({ | |||
|   changeDetection: ChangeDetectionStrategy.OnPush, | |||
|   host: { class: 'd-flex flex-column h-100' }, | |||
|   imports: [ | |||
|     CommonModule, | |||
|     GfDialogFooterModule, | |||
|     GfDialogHeaderModule, | |||
|     GfLineChartComponent, | |||
|     MatDialogModule | |||
|   ], | |||
|   schemas: [CUSTOM_ELEMENTS_SCHEMA], | |||
|   selector: 'gf-benchmark-detail-dialog', | |||
|   standalone: true, | |||
|   styleUrls: ['./benchmark-detail-dialog.component.scss'], | |||
|   templateUrl: 'benchmark-detail-dialog.html' | |||
| }) | |||
| export class GfBenchmarkDetailDialogComponent implements OnDestroy, OnInit { | |||
|   public assetProfile: AdminMarketDataDetails['assetProfile']; | |||
|   public historicalDataItems: LineChartItem[]; | |||
| 
 | |||
|   private unsubscribeSubject = new Subject<void>(); | |||
| 
 | |||
|   public constructor( | |||
|     private changeDetectorRef: ChangeDetectorRef, | |||
|     private dataService: DataService, | |||
|     public dialogRef: MatDialogRef<GfBenchmarkDetailDialogComponent>, | |||
|     @Inject(MAT_DIALOG_DATA) public data: BenchmarkDetailDialogParams | |||
|   ) {} | |||
| 
 | |||
|   public ngOnInit() { | |||
|     this.dataService | |||
|       .fetchAsset({ | |||
|         dataSource: this.data.dataSource, | |||
|         symbol: this.data.symbol | |||
|       }) | |||
|       .pipe(takeUntil(this.unsubscribeSubject)) | |||
|       .subscribe(({ assetProfile, marketData }) => { | |||
|         this.assetProfile = assetProfile; | |||
| 
 | |||
|         this.historicalDataItems = marketData.map(({ date, marketPrice }) => { | |||
|           return { date: format(date, DATE_FORMAT), value: marketPrice }; | |||
|         }); | |||
| 
 | |||
|         this.changeDetectorRef.markForCheck(); | |||
|       }); | |||
|   } | |||
| 
 | |||
|   public onClose() { | |||
|     this.dialogRef.close(); | |||
|   } | |||
| 
 | |||
|   public ngOnDestroy() { | |||
|     this.unsubscribeSubject.next(); | |||
|     this.unsubscribeSubject.complete(); | |||
|   } | |||
| } | |||
| @ -0,0 +1,30 @@ | |||
| <gf-dialog-header | |||
|   mat-dialog-title | |||
|   position="center" | |||
|   [deviceType]="data.deviceType" | |||
|   [title]="assetProfile?.name ?? assetProfile?.symbol" | |||
|   (closeButtonClicked)="onClose()" | |||
| /> | |||
| 
 | |||
| <div class="flex-grow-1" mat-dialog-content> | |||
|   <div class="container p-0"> | |||
|     <gf-line-chart | |||
|       benchmarkLabel="Average Unit Price" | |||
|       class="mb-4" | |||
|       [colorScheme]="data.colorScheme" | |||
|       [historicalDataItems]="historicalDataItems" | |||
|       [isAnimated]="true" | |||
|       [locale]="data.locale" | |||
|       [showGradient]="true" | |||
|       [showXAxis]="true" | |||
|       [showYAxis]="true" | |||
|       [symbol]="data.symbol" | |||
|     /> | |||
|   </div> | |||
| </div> | |||
| 
 | |||
| <gf-dialog-footer | |||
|   mat-dialog-actions | |||
|   [deviceType]="data.deviceType" | |||
|   (closeButtonClicked)="onClose()" | |||
| /> | |||
| @ -0,0 +1,11 @@ | |||
| import { ColorScheme } from '@ghostfolio/common/types'; | |||
| 
 | |||
| import { DataSource } from '@prisma/client'; | |||
| 
 | |||
| export interface BenchmarkDetailDialogParams { | |||
|   colorScheme: ColorScheme; | |||
|   dataSource: DataSource; | |||
|   deviceType: string; | |||
|   locale: string; | |||
|   symbol: string; | |||
| } | |||
					Loading…
					
					
				
		Reference in new issue