From 65abae14e6655d77462b08e5955a84c7a6781e71 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 1 Nov 2023 15:07:35 +0100 Subject: [PATCH 1/4] First Implementation of tags on holdings --- apps/api/src/app/admin/admin.service.ts | 2 ++ apps/api/src/app/admin/update-asset-profile.dto.ts | 12 +++++++++++- .../symbol-profile/symbol-profile.service.ts | 2 ++ .../asset-profile-dialog.component.ts | 5 +++++ .../asset-profile-dialog/asset-profile-dialog.html | 11 +++++++++++ .../interfaces/enhanced-symbol-profile.interface.ts | 1 + 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index a42723ba3..4332bd8bb 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -308,6 +308,7 @@ export class AdminService { comment, dataSource, name, + tags, scraperConfiguration, symbol, symbolMapping @@ -318,6 +319,7 @@ export class AdminService { comment, dataSource, name, + tags, scraperConfiguration, symbol, symbolMapping diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/apps/api/src/app/admin/update-asset-profile.dto.ts index a39f8db81..d4efd8c8b 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/apps/api/src/app/admin/update-asset-profile.dto.ts @@ -1,5 +1,11 @@ import { AssetClass, AssetSubClass, Prisma } from '@prisma/client'; -import { IsEnum, IsObject, IsOptional, IsString } from 'class-validator'; +import { + IsArray, + IsEnum, + IsObject, + IsOptional, + IsString +} from 'class-validator'; export class UpdateAssetProfileDto { @IsEnum(AssetClass, { each: true }) @@ -18,6 +24,10 @@ export class UpdateAssetProfileDto { @IsOptional() name?: string; + @IsArray() + @IsOptional() + tags?: string[]; + @IsObject() @IsOptional() scraperConfiguration?: Prisma.InputJsonObject; diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 46a6991cb..5b3ea1b7d 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -91,6 +91,7 @@ export class SymbolProfileService { comment, dataSource, name, + tags, scraperConfiguration, symbol, symbolMapping @@ -101,6 +102,7 @@ export class SymbolProfileService { assetSubClass, comment, name, + tags, scraperConfiguration, symbolMapping }, diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 5e331ca91..818564e24 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -51,6 +51,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { assetSubClass: new FormControl(undefined), comment: '', name: ['', Validators.required], + tags: new FormControl(undefined), scraperConfiguration: '', symbolMapping: '' }); @@ -66,6 +67,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { [name: string]: { name: string; value: number }; }; + public HoldingTags: { id: string; label: string }[]; + private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format( new Date(), DATE_FORMAT @@ -132,6 +135,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { assetClass: this.assetProfile.assetClass, assetSubClass: this.assetProfile.assetSubClass, comment: this.assetProfile?.comment ?? '', + tags: this.assetProfile?.tags, scraperConfiguration: JSON.stringify( this.assetProfile?.scraperConfiguration ?? {} ), @@ -225,6 +229,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { assetSubClass: this.assetProfileForm.controls['assetSubClass'].value, comment: this.assetProfileForm.controls['comment'].value ?? null, name: this.assetProfileForm.controls['name'].value, + tag: this.assetProfileForm.controls['tags'].value, scraperConfiguration, symbolMapping }; diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 3a095ace2..4b521c929 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -210,6 +210,17 @@ +
+ + Tags + + + {{ tag.label }} + + +
Date: Mon, 6 Nov 2023 17:38:54 +0100 Subject: [PATCH 2/4] Further Work on Holdings on tags --- apps/api/src/app/admin/admin.controller.ts | 15 +++++- apps/api/src/app/admin/admin.service.ts | 20 ++++++-- .../src/app/admin/update-asset-profile.dto.ts | 4 +- .../symbol-profile/symbol-profile.service.ts | 15 ++++-- .../asset-profile-dialog.component.ts | 47 +++++++++++++++++-- .../asset-profile-dialog.html | 30 ++++++++++-- .../asset-profile-dialog.module.ts | 4 ++ apps/client/src/app/services/admin.service.ts | 6 ++- .../interfaces/admin-market-data.interface.ts | 3 +- .../enhanced-symbol-profile.interface.ts | 4 +- .../migration.sql | 20 ++++++++ prisma/schema.prisma | 2 + 12 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 prisma/migrations/20231106132716_added_tag_to_symbol_profile/migration.sql diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index e277e77e4..14f24360f 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -448,10 +448,23 @@ export class AdminController { ); } + await this.adminService.patchAssetProfileData({ + dataSource, + symbol, + tags: { + set: [] + } + }); + return this.adminService.patchAssetProfileData({ ...assetProfileData, dataSource, - symbol + symbol, + tags: { + connect: assetProfileData.tags?.map(({ id }) => { + return { id }; + }) + } }); } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 4332bd8bb..a3733df01 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -23,7 +23,13 @@ import { } from '@ghostfolio/common/interfaces'; import { MarketDataPreset } from '@ghostfolio/common/types'; import { BadRequestException, Injectable } from '@nestjs/common'; -import { AssetSubClass, Prisma, Property, SymbolProfile } from '@prisma/client'; +import { + AssetSubClass, + Prisma, + Property, + SymbolProfile, + Tag +} from '@prisma/client'; import { differenceInDays } from 'date-fns'; import { groupBy } from 'lodash'; @@ -204,7 +210,8 @@ export class AdminService { }, scraperConfiguration: true, sectors: true, - symbol: true + symbol: true, + tags: true } }), this.prismaService.symbolProfile.count({ where }) @@ -222,7 +229,8 @@ export class AdminService { name, Order, sectors, - symbol + symbol, + tags }) => { const countriesCount = countries ? Object.keys(countries).length : 0; const marketDataItemCount = @@ -246,7 +254,8 @@ export class AdminService { marketDataItemCount, sectorsCount, activitiesCount: _count.Order, - date: Order?.[0]?.date + date: Order?.[0]?.date, + tags }; } ); @@ -378,7 +387,8 @@ export class AdminService { countriesCount: 0, currency: symbol.replace(DEFAULT_CURRENCY, ''), name: symbol, - sectorsCount: 0 + sectorsCount: 0, + tags: [] }; }); diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/apps/api/src/app/admin/update-asset-profile.dto.ts index d4efd8c8b..ee801cab5 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/apps/api/src/app/admin/update-asset-profile.dto.ts @@ -1,4 +1,4 @@ -import { AssetClass, AssetSubClass, Prisma } from '@prisma/client'; +import { AssetClass, AssetSubClass, Prisma, Tag } from '@prisma/client'; import { IsArray, IsEnum, @@ -26,7 +26,7 @@ export class UpdateAssetProfileDto { @IsArray() @IsOptional() - tags?: string[]; + tags?: Tag[]; @IsObject() @IsOptional() diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 5b3ea1b7d..3747b07f3 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -8,7 +8,12 @@ import { import { Country } from '@ghostfolio/common/interfaces/country.interface'; import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { Injectable } from '@nestjs/common'; -import { Prisma, SymbolProfile, SymbolProfileOverrides } from '@prisma/client'; +import { + Prisma, + SymbolProfile, + SymbolProfileOverrides, + Tag +} from '@prisma/client'; import { continents, countries } from 'countries-list'; @Injectable() @@ -49,6 +54,7 @@ export class SymbolProfileService { select: { date: true }, take: 1 }, + tags: true, SymbolProfileOverrides: true }, where: { @@ -72,7 +78,8 @@ export class SymbolProfileService { _count: { select: { Order: true } }, - SymbolProfileOverrides: true + SymbolProfileOverrides: true, + tags: true }, where: { id: { @@ -116,6 +123,7 @@ export class SymbolProfileService { Order?: { date: Date; }[]; + tags?: Tag[]; SymbolProfileOverrides: SymbolProfileOverrides; })[] ): EnhancedSymbolProfile[] { @@ -129,7 +137,8 @@ export class SymbolProfileService { dateOfFirstActivity: undefined, scraperConfiguration: this.getScraperConfiguration(symbolProfile), sectors: this.getSectors(symbolProfile), - symbolMapping: this.getSymbolMapping(symbolProfile) + symbolMapping: this.getSymbolMapping(symbolProfile), + tags: symbolProfile?.tags }; item.activitiesCount = symbolProfile._count.Order; diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 818564e24..4de9a1357 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -2,9 +2,11 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, Inject, OnDestroy, - OnInit + OnInit, + ViewChild } from '@angular/core'; import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -21,7 +23,8 @@ import { AssetClass, AssetSubClass, MarketData, - SymbolProfile + SymbolProfile, + Tag } from '@prisma/client'; import { format } from 'date-fns'; import { parse as csvToJson } from 'papaparse'; @@ -29,6 +32,8 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { AssetProfileDialogParams } from './interfaces/interfaces'; +import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; +import { COMMA, ENTER } from '@angular/cdk/keycodes'; @Component({ host: { class: 'd-flex flex-column h-100' }, @@ -38,6 +43,8 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; styleUrls: ['./asset-profile-dialog.component.scss'] }) export class AssetProfileDialog implements OnDestroy, OnInit { + @ViewChild('tagInput') tagInput: ElementRef; + public separatorKeysCodes: number[] = [ENTER, COMMA]; public assetProfileClass: string; public assetClasses = Object.keys(AssetClass).map((assetClass) => { return { id: assetClass, label: translate(assetClass) }; @@ -51,7 +58,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { assetSubClass: new FormControl(undefined), comment: '', name: ['', Validators.required], - tags: new FormControl(undefined), + tags: new FormControl(undefined), scraperConfiguration: '', symbolMapping: '' }); @@ -67,7 +74,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { [name: string]: { name: string; value: number }; }; - public HoldingTags: { id: string; label: string }[]; + public HoldingTags: { id: string; name: string }[]; private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format( new Date(), @@ -94,6 +101,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.historicalDataAsCsvString = AssetProfileDialog.HISTORICAL_DATA_TEMPLATE; + this.adminService + .fetchTags() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((tags) => { + this.HoldingTags = tags.map(({ id, name }) => { + return { id, name }; + }); + this.dataService.updateInfo(); + + this.changeDetectorRef.markForCheck(); + }); + this.adminService .fetchAdminMarketDataBySymbol({ dataSource: this.data.dataSource, @@ -229,7 +248,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { assetSubClass: this.assetProfileForm.controls['assetSubClass'].value, comment: this.assetProfileForm.controls['comment'].value ?? null, name: this.assetProfileForm.controls['name'].value, - tag: this.assetProfileForm.controls['tags'].value, + tags: this.assetProfileForm.controls['tags'].value, scraperConfiguration, symbolMapping }; @@ -258,6 +277,24 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } + public onRemoveTag(aTag: Tag) { + this.assetProfileForm.controls['tags'].setValue( + this.assetProfileForm.controls['tags'].value.filter(({ id }) => { + return id !== aTag.id; + }) + ); + } + + public onAddTag(event: MatAutocompleteSelectedEvent) { + this.assetProfileForm.controls['tags'].setValue([ + ...(this.assetProfileForm.controls['tags'].value ?? []), + this.HoldingTags.find(({ id }) => { + return id === event.option.value; + }) + ]); + this.tagInput.nativeElement.value = ''; + } + public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 4b521c929..27571a360 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -213,12 +213,32 @@
Tags - - - {{ tag.label }} + - + {{ tag.name }} + + + + + + + {{ tag.name }} + +
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts index b836e4066..fcf918527 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts @@ -13,6 +13,8 @@ import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-propo import { GfValueModule } from '@ghostfolio/ui/value'; import { AssetProfileDialog } from './asset-profile-dialog.component'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatChipsModule } from '@angular/material/chips'; @NgModule({ declarations: [AssetProfileDialog], @@ -21,6 +23,8 @@ import { AssetProfileDialog } from './asset-profile-dialog.component'; FormsModule, GfAdminMarketDataDetailModule, GfPortfolioProportionChartModule, + MatAutocompleteModule, + MatChipsModule, GfValueModule, MatButtonModule, MatCheckboxModule, diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 042914ee4..b8ea0c70d 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -209,7 +209,8 @@ export class AdminService { name, scraperConfiguration, symbol, - symbolMapping + symbolMapping, + tags }: UniqueAsset & UpdateAssetProfileDto) { return this.http.patch( `/api/v1/admin/profile-data/${dataSource}/${symbol}`, @@ -219,7 +220,8 @@ export class AdminService { comment, name, scraperConfiguration, - symbolMapping + symbolMapping, + tags } ); } diff --git a/libs/common/src/lib/interfaces/admin-market-data.interface.ts b/libs/common/src/lib/interfaces/admin-market-data.interface.ts index 08838d4bc..791530b38 100644 --- a/libs/common/src/lib/interfaces/admin-market-data.interface.ts +++ b/libs/common/src/lib/interfaces/admin-market-data.interface.ts @@ -1,4 +1,4 @@ -import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; +import { AssetClass, AssetSubClass, DataSource, Tag } from '@prisma/client'; export interface AdminMarketData { count: number; @@ -16,4 +16,5 @@ export interface AdminMarketDataItem { name: string; sectorsCount: number; symbol: string; + tags: Tag[]; } diff --git a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts index ba28babcf..f53d41354 100644 --- a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts +++ b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts @@ -1,4 +1,4 @@ -import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; +import { AssetClass, AssetSubClass, DataSource, Tag } from '@prisma/client'; import { Country } from './country.interface'; import { ScraperConfiguration } from './scraper-configuration.interface'; @@ -23,5 +23,5 @@ export interface EnhancedSymbolProfile { symbolMapping?: { [key: string]: string }; updatedAt: Date; url?: string; - tags?: string[]; + tags?: Tag[]; } diff --git a/prisma/migrations/20231106132716_added_tag_to_symbol_profile/migration.sql b/prisma/migrations/20231106132716_added_tag_to_symbol_profile/migration.sql new file mode 100644 index 000000000..b9fd176db --- /dev/null +++ b/prisma/migrations/20231106132716_added_tag_to_symbol_profile/migration.sql @@ -0,0 +1,20 @@ +-- AlterEnum +ALTER TYPE "Type" ADD VALUE 'STAKE'; + +-- CreateTable +CREATE TABLE "_SymbolProfileToTag" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "_SymbolProfileToTag_AB_unique" ON "_SymbolProfileToTag"("A", "B"); + +-- CreateIndex +CREATE INDEX "_SymbolProfileToTag_B_index" ON "_SymbolProfileToTag"("B"); + +-- AddForeignKey +ALTER TABLE "_SymbolProfileToTag" ADD CONSTRAINT "_SymbolProfileToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "SymbolProfile"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_SymbolProfileToTag" ADD CONSTRAINT "_SymbolProfileToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ea7b31a01..c7579b9cc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -145,6 +145,7 @@ model SymbolProfile { symbolMapping Json? url String? Order Order[] + tags Tag[] SymbolProfileOverrides SymbolProfileOverrides? @@unique([dataSource, symbol]) @@ -176,6 +177,7 @@ model Tag { id String @id @default(uuid()) name String @unique orders Order[] + symbolProfile SymbolProfile[] } model User { From a6880e1aff5a0d94bff76454fc5217aaa79e54b9 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 6 Nov 2023 19:04:14 +0100 Subject: [PATCH 3/4] First try adding tag to portfolio performance analysis --- apps/api/src/app/order/order.service.ts | 35 +++++++++++++++---- .../src/app/portfolio/portfolio.service.ts | 1 + apps/api/src/services/tag/tag.service.ts | 17 ++++++--- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 10515018c..78d153115 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -298,13 +298,30 @@ export class OrderService { } if (filtersByTag?.length > 0) { - where.tags = { - some: { - OR: filtersByTag.map(({ id }) => { - return { id }; - }) + where.OR = [ + { + tags: { + some: { + OR: filtersByTag.map(({ id }) => { + return { + id: id + }; + }) + } + } + }, + { + SymbolProfile: { + tags: { + some: { + OR: filtersByTag.map(({ id }) => { + return { id }; + }) + } + } + } } - }; + ]; } if (types) { @@ -330,7 +347,11 @@ export class OrderService { } }, // eslint-disable-next-line @typescript-eslint/naming-convention - SymbolProfile: true, + SymbolProfile: { + include: { + tags: true + } + }, tags: true }, orderBy: { date: 'asc' } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index d768268ec..e656d2df3 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1190,6 +1190,7 @@ export class PortfolioService { dataProviderResponses[position.symbol]?.marketState ?? 'delayed', name: symbolProfileMap[position.symbol].name, netPerformance: position.netPerformance?.toNumber() ?? null, + tags: symbolProfileMap[position.symbol].tags, netPerformancePercentage: position.netPerformancePercentage?.toNumber() ?? null, quantity: new Big(position.quantity).toNumber() diff --git a/apps/api/src/services/tag/tag.service.ts b/apps/api/src/services/tag/tag.service.ts index c02345784..a47a528bc 100644 --- a/apps/api/src/services/tag/tag.service.ts +++ b/apps/api/src/services/tag/tag.service.ts @@ -19,11 +19,20 @@ export class TagService { name: 'asc' }, where: { - orders: { - some: { - userId + OR: [ + { + orders: { + some: { + userId + } + } + }, + { + symbolProfile: { + some: {} + } } - } + ] } }); } From 757c91a9904304b29d3161d9c5234d761ffb6975 Mon Sep 17 00:00:00 2001 From: Daniel Devaud Date: Wed, 8 Nov 2023 08:50:54 +0100 Subject: [PATCH 4/4] Latest Changes on holding tags --- apps/api/src/app/order/order.service.ts | 42 ++++++++++--------- apps/api/src/app/tag/tag.service.ts | 5 ++- .../admin-tag/admin-tag.component.html | 14 +++++++ .../admin-tag/admin-tag.component.ts | 2 +- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 78d153115..3c228bc79 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -298,28 +298,32 @@ export class OrderService { } if (filtersByTag?.length > 0) { - where.OR = [ + where.AND = [ { - tags: { - some: { - OR: filtersByTag.map(({ id }) => { - return { - id: id - }; - }) - } - } - }, - { - SymbolProfile: { - tags: { - some: { - OR: filtersByTag.map(({ id }) => { - return { id }; - }) + OR: [ + { + tags: { + some: { + OR: filtersByTag.map(({ id }) => { + return { + id: id + }; + }) + } + } + }, + { + SymbolProfile: { + tags: { + some: { + OR: filtersByTag.map(({ id }) => { + return { id }; + }) + } + } } } - } + ] } ]; } diff --git a/apps/api/src/app/tag/tag.service.ts b/apps/api/src/app/tag/tag.service.ts index 9da7cc475..674f6aa92 100644 --- a/apps/api/src/app/tag/tag.service.ts +++ b/apps/api/src/app/tag/tag.service.ts @@ -50,7 +50,7 @@ export class TagService { const tagsWithOrderCount = await this.prismaService.tag.findMany({ include: { _count: { - select: { orders: true } + select: { orders: true, symbolProfile: true } } } }); @@ -59,7 +59,8 @@ export class TagService { return { id, name, - activityCount: _count.orders + activityCount: _count.orders, + holdingCount: _count.symbolProfile }; }); } diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.html b/apps/client/src/app/components/admin-tag/admin-tag.component.html index b08ae96d4..5081a3d69 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.html +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.html @@ -48,6 +48,20 @@ + + + Holdings + + + {{ element.holdingCount }} + + + = new MatTableDataSource(); public deviceType: string; - public displayedColumns = ['name', 'activities', 'actions']; + public displayedColumns = ['name', 'activities', 'holdings', 'actions']; public tags: Tag[]; private unsubscribeSubject = new Subject();