From 2f16f9255f2c05609dc42c2d64188da13ebc97b8 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:49:01 +0200 Subject: [PATCH] Support sorting --- apps/api/src/app/admin/admin.controller.ts | 6 ++- apps/api/src/app/admin/admin.service.ts | 12 ++++- .../admin-market-data.component.ts | 47 ++++++++++++++++--- .../admin-market-data/admin-market-data.html | 6 +-- apps/client/src/app/services/admin.service.ts | 14 +++++- 5 files changed, 72 insertions(+), 13 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 626ecc410..75b2b2bb9 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -33,7 +33,7 @@ import { } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; -import { DataSource, MarketData, SymbolProfile } from '@prisma/client'; +import { DataSource, MarketData, Prisma, SymbolProfile } from '@prisma/client'; import { isDate } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; @@ -250,6 +250,8 @@ export class AdminController { public async getMarketData( @Query('assetSubClasses') filterByAssetSubClasses?: string, @Query('skip') skip?: number, + @Query('sortColumn') sortColumn?: string, + @Query('sortDirection') sortDirection?: Prisma.SortOrder, @Query('take') take?: number ): Promise { if ( @@ -277,6 +279,8 @@ export class AdminController { return this.adminService.getMarketData({ filters, + sortColumn, + sortDirection, skip: isNaN(skip) ? undefined : skip, take: isNaN(take) ? undefined : take }); diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index d68d4d1e4..e50776e4d 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -103,13 +103,19 @@ export class AdminService { public async getMarketData({ filters, + sortColumn, + sortDirection, skip, take = DEFAULT_PAGE_SIZE }: { filters?: Filter[]; skip?: number; + sortColumn?: string; + sortDirection?: Prisma.SortOrder; take?: number; }): Promise { + let orderBy: Prisma.Enumerable = + [{ symbol: 'asc' }]; const where: Prisma.SymbolProfileWhereInput = {}; const { ASSET_SUB_CLASS: filtersByAssetSubClass } = groupBy( @@ -128,12 +134,16 @@ export class AdminService { where.assetSubClass = AssetSubClass[filtersByAssetSubClass[0].id]; } + if (sortColumn) { + orderBy = [{ [sortColumn]: sortDirection }]; + } + const [assetProfiles, count] = await Promise.all([ this.prismaService.symbolProfile.findMany({ + orderBy, skip, take, where, - orderBy: [{ symbol: 'asc' }], select: { _count: { select: { Order: true } diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index 6cca44f59..c14a38882 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -1,4 +1,5 @@ import { + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, @@ -7,7 +8,7 @@ import { ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { MatSort } from '@angular/material/sort'; +import { MatSort, Sort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; import { AdminService } from '@ghostfolio/client/services/admin.service'; @@ -16,7 +17,7 @@ import { getDateFormatString } from '@ghostfolio/common/helper'; import { Filter, UniqueAsset, User } from '@ghostfolio/common/interfaces'; import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface'; import { translate } from '@ghostfolio/ui/i18n'; -import { AssetSubClass, DataSource } from '@prisma/client'; +import { AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators'; @@ -34,7 +35,9 @@ import { MatPaginator, PageEvent } from '@angular/material/paginator'; styleUrls: ['./admin-market-data.scss'], templateUrl: './admin-market-data.html' }) -export class AdminMarketDataComponent implements OnDestroy, OnInit { +export class AdminMarketDataComponent + implements AfterViewInit, OnDestroy, OnInit +{ @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -130,12 +133,30 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit { }); } + public ngAfterViewInit() { + this.sort.sortChange.subscribe( + ({ active: sortColumn, direction }: Sort) => { + this.paginator.pageIndex = 0; + + this.loadData({ + sortColumn, + sortDirection: direction, + pageIndex: this.paginator.pageIndex + }); + } + ); + } + public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; } public onChangePage(page: PageEvent) { - this.loadData(page.pageIndex); + this.loadData({ + pageIndex: page.pageIndex, + sortColumn: this.sort.active, + sortDirection: this.sort.direction + }); } public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) { @@ -203,10 +224,20 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } - private loadData(aPageIndex = 0) { + private loadData( + { + pageIndex, + sortColumn, + sortDirection + }: { + pageIndex: number; + sortColumn?: string; + sortDirection?: Prisma.SortOrder; + } = { pageIndex: 0 } + ) { this.isLoading = true; - if (aPageIndex === 0 && this.paginator) { + if (pageIndex === 0 && this.paginator) { this.paginator.pageIndex = 0; } @@ -215,8 +246,10 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit { this.adminService .fetchAdminMarketData({ + sortColumn, + sortDirection, filters: this.activeFilters, - skip: aPageIndex * this.pageSize, + skip: pageIndex * this.pageSize, take: this.pageSize }) .pipe(takeUntil(this.unsubscribeSubject)) diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.html b/apps/client/src/app/components/admin-market-data/admin-market-data.html index 39fd67698..3a6c0b861 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.html +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.html @@ -29,7 +29,7 @@ - + Data Source @@ -38,7 +38,7 @@ - + Asset Class @@ -47,7 +47,7 @@ - + Asset Sub Class diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 7755137d1..856fe9341 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -15,7 +15,7 @@ import { Filter, UniqueAsset } from '@ghostfolio/common/interfaces'; -import { DataSource, MarketData, Platform } from '@prisma/client'; +import { DataSource, MarketData, Platform, Prisma } from '@prisma/client'; import { JobStatus } from 'bull'; import { format, parseISO } from 'date-fns'; import { Observable, map } from 'rxjs'; @@ -70,10 +70,14 @@ export class AdminService { public fetchAdminMarketData({ filters, skip, + sortColumn, + sortDirection, take }: { filters?: Filter[]; skip?: number; + sortColumn?: string; + sortDirection?: Prisma.SortOrder; take: number; }) { let params = this.dataService.buildFiltersAsQueryParams({ filters }); @@ -82,6 +86,14 @@ export class AdminService { params = params.append('skip', skip); } + if (sortColumn) { + params = params.append('sortColumn', sortColumn); + } + + if (sortDirection) { + params = params.append('sortDirection', sortDirection); + } + params = params.append('take', take); return this.http.get('/api/v1/admin/market-data', {