Browse Source
Feature/improve asset profile management (#1485)
* Improve asset profile management (Add note, fix filter)
* Update changelog
pull/1487/head
Thomas Kaul
2 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with
107 additions and
17 deletions
-
CHANGELOG.md
-
apps/api/src/app/admin/admin.service.ts
-
apps/api/src/app/admin/update-asset-profile.dto.ts
-
apps/api/src/services/symbol-profile.service.ts
-
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
-
apps/client/src/app/components/admin-market-data/admin-market-data.html
-
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
-
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
-
apps/client/src/app/services/admin.service.ts
-
apps/client/src/app/services/data.service.ts
-
libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts
-
libs/ui/src/lib/activities-table/activities-table.component.html
-
libs/ui/src/lib/activities-table/activities-table.component.ts
-
prisma/migrations/20221128064317_added_comment_to_symbol_profile/migration.sql
-
prisma/schema.prisma
|
|
@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. |
|
|
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
|
|
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
|
|
|
|
|
|
|
## Unreleased |
|
|
|
|
|
|
|
### Added |
|
|
|
|
|
|
|
- Supported a note for asset profiles |
|
|
|
|
|
|
|
### Fixed |
|
|
|
|
|
|
|
- Fixed the filter by asset sub class for the asset profiles in the admin control |
|
|
|
|
|
|
|
## 1.215.0 - 2022-11-27 |
|
|
|
|
|
|
|
### Changed |
|
|
@ -538,7 +548,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
|
|
|
|
### Added |
|
|
|
|
|
|
|
- Support a note for activities |
|
|
|
- Supported a note for activities |
|
|
|
|
|
|
|
### Todo |
|
|
|
|
|
|
|
|
|
@ -116,6 +116,7 @@ export class AdminService { |
|
|
|
}, |
|
|
|
assetClass: true, |
|
|
|
assetSubClass: true, |
|
|
|
comment: true, |
|
|
|
countries: true, |
|
|
|
dataSource: true, |
|
|
|
Order: { |
|
|
@ -150,6 +151,7 @@ export class AdminService { |
|
|
|
activitiesCount: symbolProfile._count.Order, |
|
|
|
assetClass: symbolProfile.assetClass, |
|
|
|
assetSubClass: symbolProfile.assetSubClass, |
|
|
|
comment: symbolProfile.comment, |
|
|
|
dataSource: symbolProfile.dataSource, |
|
|
|
date: symbolProfile.Order?.[0]?.date, |
|
|
|
symbol: symbolProfile.symbol |
|
|
@ -190,11 +192,13 @@ export class AdminService { |
|
|
|
} |
|
|
|
|
|
|
|
public async patchAssetProfileData({ |
|
|
|
comment, |
|
|
|
dataSource, |
|
|
|
symbol, |
|
|
|
symbolMapping |
|
|
|
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) { |
|
|
|
await this.symbolProfileService.updateSymbolProfile({ |
|
|
|
comment, |
|
|
|
dataSource, |
|
|
|
symbol, |
|
|
|
symbolMapping |
|
|
|
|
|
@ -1,6 +1,10 @@ |
|
|
|
import { IsObject, IsOptional } from 'class-validator'; |
|
|
|
import { IsObject, IsOptional, IsString } from 'class-validator'; |
|
|
|
|
|
|
|
export class UpdateAssetProfileDto { |
|
|
|
@IsString() |
|
|
|
@IsOptional() |
|
|
|
comment?: string; |
|
|
|
|
|
|
|
@IsObject() |
|
|
|
@IsOptional() |
|
|
|
symbolMapping?: { |
|
|
|
|
|
@ -104,12 +104,13 @@ export class SymbolProfileService { |
|
|
|
} |
|
|
|
|
|
|
|
public updateSymbolProfile({ |
|
|
|
comment, |
|
|
|
dataSource, |
|
|
|
symbol, |
|
|
|
symbolMapping |
|
|
|
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) { |
|
|
|
return this.prismaService.symbolProfile.update({ |
|
|
|
data: { symbolMapping }, |
|
|
|
data: { comment, symbolMapping }, |
|
|
|
where: { dataSource_symbol: { dataSource, symbol } } |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
@ -16,6 +16,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { DATE_FORMAT, 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 { format, parseISO } from 'date-fns'; |
|
|
|
import { DeviceDetectorService } from 'ngx-device-detector'; |
|
|
@ -44,10 +45,10 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit { |
|
|
|
AssetSubClass.PRECIOUS_METAL, |
|
|
|
AssetSubClass.PRIVATE_EQUITY, |
|
|
|
AssetSubClass.STOCK |
|
|
|
].map((id) => { |
|
|
|
].map((assetSubClass) => { |
|
|
|
return { |
|
|
|
id, |
|
|
|
label: id, |
|
|
|
id: assetSubClass, |
|
|
|
label: translate(assetSubClass), |
|
|
|
type: 'ASSET_SUB_CLASS' |
|
|
|
}; |
|
|
|
}); |
|
|
@ -67,6 +68,7 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit { |
|
|
|
'marketDataItemCount', |
|
|
|
'sectorsCount', |
|
|
|
'countriesCount', |
|
|
|
'comment', |
|
|
|
'actions' |
|
|
|
]; |
|
|
|
public filters$ = new Subject<Filter[]>(); |
|
|
|
|
|
@ -100,6 +100,22 @@ |
|
|
|
</td> |
|
|
|
</ng-container> |
|
|
|
|
|
|
|
<ng-container matColumnDef="comment"> |
|
|
|
<th |
|
|
|
*matHeaderCellDef |
|
|
|
class="px-1" |
|
|
|
mat-header-cell |
|
|
|
mat-sort-header |
|
|
|
></th> |
|
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
|
<ion-icon |
|
|
|
*ngIf="element.comment" |
|
|
|
class="d-block" |
|
|
|
name="document-text-outline" |
|
|
|
></ion-icon> |
|
|
|
</td> |
|
|
|
</ng-container> |
|
|
|
|
|
|
|
<ng-container matColumnDef="actions"> |
|
|
|
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell> |
|
|
|
<button |
|
|
|
|
|
@ -30,6 +30,7 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; |
|
|
|
export class AssetProfileDialog implements OnDestroy, OnInit { |
|
|
|
public assetProfile: EnhancedSymbolProfile; |
|
|
|
public assetProfileForm = this.formBuilder.group({ |
|
|
|
comment: '', |
|
|
|
symbolMapping: '' |
|
|
|
}); |
|
|
|
public countries: { |
|
|
@ -86,6 +87,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { |
|
|
|
} |
|
|
|
|
|
|
|
this.assetProfileForm.setValue({ |
|
|
|
comment: this.assetProfile?.comment, |
|
|
|
symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping) |
|
|
|
}); |
|
|
|
|
|
|
@ -129,7 +131,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { |
|
|
|
} catch {} |
|
|
|
|
|
|
|
const assetProfileData: UpdateAssetProfileDto = { |
|
|
|
symbolMapping |
|
|
|
symbolMapping, |
|
|
|
comment: this.assetProfileForm.controls['comment'].value ?? null |
|
|
|
}; |
|
|
|
|
|
|
|
this.adminService |
|
|
|
|
|
@ -148,6 +148,18 @@ |
|
|
|
></textarea> |
|
|
|
</mat-form-field> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<mat-form-field appearance="outline" class="w-100"> |
|
|
|
<mat-label i18n>Note</mat-label> |
|
|
|
<textarea |
|
|
|
cdkAutosizeMinRows="2" |
|
|
|
cdkTextareaAutosize |
|
|
|
formControlName="comment" |
|
|
|
matInput |
|
|
|
(keyup.enter)="$event.stopPropagation()" |
|
|
|
></textarea> |
|
|
|
</mat-form-field> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="d-flex justify-content-end" mat-dialog-actions> |
|
|
|
|
|
@ -127,13 +127,14 @@ export class AdminService { |
|
|
|
} |
|
|
|
|
|
|
|
public patchAssetProfile({ |
|
|
|
comment, |
|
|
|
dataSource, |
|
|
|
symbol, |
|
|
|
symbolMapping |
|
|
|
}: UniqueAsset & UpdateAssetProfileDto) { |
|
|
|
return this.http.patch<EnhancedSymbolProfile>( |
|
|
|
`/api/v1/admin/profile-data/${dataSource}/${symbol}`, |
|
|
|
{ symbolMapping } |
|
|
|
{ comment, symbolMapping } |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -360,6 +360,7 @@ export class DataService { |
|
|
|
const { |
|
|
|
ACCOUNT: filtersByAccount, |
|
|
|
ASSET_CLASS: filtersByAssetClass, |
|
|
|
ASSET_SUB_CLASS: filtersByAssetSubClass, |
|
|
|
TAG: filtersByTag |
|
|
|
} = groupBy(filters, (filter) => { |
|
|
|
return filter.type; |
|
|
@ -387,6 +388,17 @@ export class DataService { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (filtersByAssetSubClass) { |
|
|
|
params = params.append( |
|
|
|
'assetSubClasses', |
|
|
|
filtersByAssetSubClass |
|
|
|
.map(({ id }) => { |
|
|
|
return id; |
|
|
|
}) |
|
|
|
.join(',') |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (filtersByTag) { |
|
|
|
params = params.append( |
|
|
|
'tags', |
|
|
|
|
|
@ -8,6 +8,7 @@ export interface EnhancedSymbolProfile { |
|
|
|
activitiesCount: number; |
|
|
|
assetClass: AssetClass; |
|
|
|
assetSubClass: AssetSubClass; |
|
|
|
comment?: string; |
|
|
|
countries: Country[]; |
|
|
|
createdAt: Date; |
|
|
|
currency: string | null; |
|
|
|
|
|
@ -299,6 +299,35 @@ |
|
|
|
<td *matFooterCellDef class="px-1" mat-footer-cell></td> |
|
|
|
</ng-container> |
|
|
|
|
|
|
|
<ng-container matColumnDef="comment"> |
|
|
|
<th |
|
|
|
*matHeaderCellDef |
|
|
|
class="d-none d-lg-table-cell px-1" |
|
|
|
mat-header-cell |
|
|
|
mat-sort-header |
|
|
|
></th> |
|
|
|
<td |
|
|
|
*matCellDef="let element" |
|
|
|
class="d-none d-lg-table-cell px-1" |
|
|
|
mat-cell |
|
|
|
> |
|
|
|
<button |
|
|
|
*ngIf="element.comment" |
|
|
|
class="mx-1 no-min-width px-2" |
|
|
|
mat-button |
|
|
|
title="Note" |
|
|
|
(click)="onOpenComment(element.comment); $event.stopPropagation()" |
|
|
|
> |
|
|
|
<ion-icon name="document-text-outline"></ion-icon> |
|
|
|
</button> |
|
|
|
</td> |
|
|
|
<td |
|
|
|
*matFooterCellDef |
|
|
|
class="d-none d-lg-table-cell px-1" |
|
|
|
mat-footer-cell |
|
|
|
></td> |
|
|
|
</ng-container> |
|
|
|
|
|
|
|
<ng-container matColumnDef="actions"> |
|
|
|
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell> |
|
|
|
<button |
|
|
@ -345,15 +374,6 @@ |
|
|
|
</mat-menu> |
|
|
|
</th> |
|
|
|
<td *matCellDef="let element" class="px-1 text-center" mat-cell> |
|
|
|
<button |
|
|
|
*ngIf="element.comment && !this.showActions" |
|
|
|
class="mx-1 no-min-width px-2" |
|
|
|
mat-button |
|
|
|
title="Note" |
|
|
|
(click)="onOpenComment(element.comment)" |
|
|
|
> |
|
|
|
<ion-icon name="document-text-outline"></ion-icon> |
|
|
|
</button> |
|
|
|
<button |
|
|
|
*ngIf="this.showActions" |
|
|
|
class="mx-1 no-min-width px-2" |
|
|
|
|
|
@ -94,6 +94,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy { |
|
|
|
'currency', |
|
|
|
'valueInBaseCurrency', |
|
|
|
'account', |
|
|
|
'comment', |
|
|
|
'actions' |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
@ -0,0 +1,2 @@ |
|
|
|
-- AlterTable |
|
|
|
ALTER TABLE "SymbolProfile" ADD COLUMN "comment" TEXT; |
|
|
@ -112,6 +112,7 @@ model Settings { |
|
|
|
model SymbolProfile { |
|
|
|
assetClass AssetClass? |
|
|
|
assetSubClass AssetSubClass? |
|
|
|
comment String? |
|
|
|
countries Json? |
|
|
|
createdAt DateTime @default(now()) |
|
|
|
currency String |
|
|
|